ctdb-scripts: Move connection tracking to 10.interface
[samba4-gss.git] / source3 / client / smbspool_krb5_wrapper.c
blob539929054e29d39b6f0546ddf5eee3e2c09b616b
1 /*
2 * Unix SMB/CIFS implementation.
4 * CUPS printing backend helper to execute smbspool
6 * Copyright (C) 2010-2011 Andreas Schneider <asn@samba.org>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
23 #include "system/filesys.h"
24 #include "system/kerberos.h"
25 #include "system/passwd.h"
26 #include "lib/krb5_wrap/krb5_samba.h"
28 #include <cups/backend.h>
30 #include "dynconfig/dynconfig.h"
32 #undef calloc
34 enum cups_smb_dbglvl_e {
35 CUPS_SMB_LOG_DEBUG = 0,
36 CUPS_SMB_LOG_ERROR,
38 static void cups_smb_debug(enum cups_smb_dbglvl_e lvl, const char *format, ...)
39 PRINTF_ATTRIBUTE(2, 3);
41 #define CUPS_SMB_DEBUG(...) cups_smb_debug(CUPS_SMB_LOG_DEBUG, __VA_ARGS__)
42 #define CUPS_SMB_ERROR(...) cups_smb_debug(CUPS_SMB_LOG_DEBUG, __VA_ARGS__)
44 static void cups_smb_debug(enum cups_smb_dbglvl_e lvl, const char *format, ...)
46 const char *prefix = "DEBUG";
47 char buffer[1024];
48 va_list va;
50 va_start(va, format);
51 vsnprintf(buffer, sizeof(buffer), format, va);
52 va_end(va);
54 switch (lvl) {
55 case CUPS_SMB_LOG_DEBUG:
56 prefix = "DEBUG";
57 break;
58 case CUPS_SMB_LOG_ERROR:
59 prefix = "ERROR";
60 break;
63 fprintf(stderr,
64 "%s: SMBSPOOL_KRB5 - %s\n",
65 prefix,
66 buffer);
69 static bool kerberos_get_default_ccache(char *ccache_buf, size_t len)
71 krb5_context ctx;
72 const char *ccache_name = NULL;
73 char *full_ccache_name = NULL;
74 krb5_ccache ccache = NULL;
75 krb5_error_code code;
77 code = krb5_init_context(&ctx);
78 if (code != 0) {
79 return false;
82 ccache_name = smb_force_krb5_cc_default_name(ctx);
83 if (ccache_name == NULL) {
84 krb5_free_context(ctx);
85 return false;
88 code = krb5_cc_resolve(ctx, ccache_name, &ccache);
89 if (code != 0) {
90 krb5_free_context(ctx);
91 return false;
94 code = krb5_cc_get_full_name(ctx, ccache, &full_ccache_name);
95 krb5_cc_close(ctx, ccache);
96 if (code != 0) {
97 krb5_free_context(ctx);
98 return false;
101 snprintf(ccache_buf, len, "%s", full_ccache_name);
103 #ifdef SAMBA4_USES_HEIMDAL
104 free(full_ccache_name);
105 #else
106 krb5_free_string(ctx, full_ccache_name);
107 #endif
108 krb5_free_context(ctx);
110 return true;
114 * This is a helper binary to execute smbspool.
116 * It needs to be installed or symlinked as:
117 * /usr/lib/cups/backend/smb
119 * The permissions of the binary need to be set to 0700 so that it is executed
120 * as root. The binary switches to the user which is passed via the environment
121 * variable AUTH_UID, so we can access the kerberos ticket.
123 int main(int argc, char *argv[])
125 char smbspool_cmd[PATH_MAX] = {0};
126 struct passwd *pwd;
127 struct group *g = NULL;
128 char gen_cc[PATH_MAX] = {0};
129 char *env = NULL;
130 char auth_info_required[256] = {0};
131 char device_uri[4096] = {0};
132 uid_t uid = (uid_t)-1;
133 gid_t gid = (gid_t)-1;
134 gid_t groups[1] = { (gid_t)-1 };
135 unsigned long tmp;
136 bool ok;
137 int cmp;
138 int rc;
140 env = getenv("DEVICE_URI");
141 if (env != NULL && strlen(env) > 2) {
142 snprintf(device_uri, sizeof(device_uri), "%s", env);
145 /* We must handle the following values of AUTH_INFO_REQUIRED:
146 * none: Anonymous/guest printing
147 * username,password: A username (of the form "username" or "DOMAIN\username")
148 * and password are required
149 * negotiate: Kerberos authentication
150 * NULL (not set): will never happen when called from cupsd
151 * https://www.cups.org/doc/spec-ipp.html#auth-info-required
152 * https://github.com/apple/cups/issues/5674
154 env = getenv("AUTH_INFO_REQUIRED");
156 /* If not set, then just call smbspool. */
157 if (env == NULL || env[0] == 0) {
158 CUPS_SMB_DEBUG("AUTH_INFO_REQUIRED is not set - "
159 "executing smbspool");
160 /* Pass this printing task to smbspool without Kerberos auth */
161 goto smbspool;
162 } else {
163 CUPS_SMB_DEBUG("AUTH_INFO_REQUIRED=%s", env);
165 /* First test the value of AUTH_INFO_REQUIRED
166 * against known possible values
168 cmp = strcmp(env, "none");
169 if (cmp == 0) {
170 CUPS_SMB_DEBUG("Authenticate using none (anonymous) - "
171 "executing smbspool");
172 goto smbspool;
175 cmp = strcmp(env, "username,password");
176 if (cmp == 0) {
177 CUPS_SMB_DEBUG("Authenticate using username/password - "
178 "executing smbspool");
179 goto smbspool;
182 /* Now, if 'goto smbspool' still has not happened,
183 * there are only two variants left:
184 * 1) AUTH_INFO_REQUIRED is "negotiate" and then
185 * we have to continue working
186 * 2) or it is something not known to us, then Kerberos
187 * authentication is not required, so just also pass
188 * this task to smbspool
190 cmp = strcmp(env, "negotiate");
191 if (cmp != 0) {
192 CUPS_SMB_DEBUG("Value of AUTH_INFO_REQUIRED is not known "
193 "to smbspool_krb5_wrapper, executing smbspool");
194 goto smbspool;
197 snprintf(auth_info_required,
198 sizeof(auth_info_required),
199 "%s",
200 env);
203 uid = getuid();
205 CUPS_SMB_DEBUG("Started with uid=%d\n", uid);
206 if (uid != 0) {
207 goto smbspool;
211 * AUTH_UID gets only set if we have an incoming connection over the
212 * CUPS unix domain socket.
214 env = getenv("AUTH_UID");
215 if (env == NULL) {
216 CUPS_SMB_ERROR("AUTH_UID is not set");
217 fprintf(stderr, "ATTR: auth-info-required=negotiate\n");
218 return CUPS_BACKEND_AUTH_REQUIRED;
221 if (strlen(env) > 10) {
222 CUPS_SMB_ERROR("Invalid AUTH_UID");
223 return CUPS_BACKEND_FAILED;
226 errno = 0;
227 tmp = strtoul(env, NULL, 10);
228 if (errno != 0 || tmp >= UINT32_MAX) {
229 CUPS_SMB_ERROR("Failed to convert AUTH_UID=%s", env);
230 return CUPS_BACKEND_FAILED;
232 uid = (uid_t)tmp;
234 /* If we are printing as the root user, we're done here. */
235 if (uid == 0) {
236 goto smbspool;
239 pwd = getpwuid(uid);
240 if (pwd == NULL) {
241 CUPS_SMB_ERROR("Failed to find system user: %u - %s",
242 uid, strerror(errno));
243 return CUPS_BACKEND_FAILED;
245 gid = pwd->pw_gid;
247 rc = setgroups(0, NULL);
248 if (rc != 0) {
249 CUPS_SMB_ERROR("Failed to clear groups - %s",
250 strerror(errno));
251 return CUPS_BACKEND_FAILED;
255 * We need the primary group of the 'lp' user. This is needed to access
256 * temporary files in /var/spool/cups/.
258 g = getgrnam("lp");
259 if (g == NULL) {
260 CUPS_SMB_ERROR("Failed to find user 'lp' - %s",
261 strerror(errno));
262 return CUPS_BACKEND_FAILED;
265 CUPS_SMB_DEBUG("Adding group 'lp' (%u)", g->gr_gid);
266 groups[0] = g->gr_gid;
267 rc = setgroups(ARRAY_SIZE(groups), groups);
268 if (rc != 0) {
269 CUPS_SMB_ERROR("Failed to set groups for 'lp' - %s",
270 strerror(errno));
271 return CUPS_BACKEND_FAILED;
274 CUPS_SMB_DEBUG("Switching to gid=%d", gid);
275 rc = setgid(gid);
276 if (rc != 0) {
277 CUPS_SMB_ERROR("Failed to switch to gid=%u - %s",
278 gid,
279 strerror(errno));
280 return CUPS_BACKEND_FAILED;
283 CUPS_SMB_DEBUG("Switching to uid=%u", uid);
284 rc = setuid(uid);
285 if (rc != 0) {
286 CUPS_SMB_ERROR("Failed to switch to uid=%u - %s",
287 uid,
288 strerror(errno));
289 return CUPS_BACKEND_FAILED;
292 env = getenv("KRB5CCNAME");
293 if (env != NULL && env[0] != 0) {
294 snprintf(gen_cc, sizeof(gen_cc), "%s", env);
295 CUPS_SMB_DEBUG("User already set KRB5CCNAME [%s] as ccache",
296 gen_cc);
298 goto create_env;
301 ok = kerberos_get_default_ccache(gen_cc, sizeof(gen_cc));
302 if (ok) {
303 CUPS_SMB_DEBUG("Use default KRB5CCNAME [%s]",
304 gen_cc);
305 goto create_env;
308 /* Fallback to a FILE ccache */
309 snprintf(gen_cc, sizeof(gen_cc), "FILE:/tmp/krb5cc_%u", uid);
311 create_env:
313 * Make sure we do not have LD_PRELOAD or other security relevant
314 * environment variables set.
316 #ifdef HAVE_CLEARENV
317 clearenv();
318 #else
319 environ = calloc(3, sizeof(*environ));
320 #endif
322 CUPS_SMB_DEBUG("Setting KRB5CCNAME to '%s'", gen_cc);
323 setenv("KRB5CCNAME", gen_cc, 1);
324 if (device_uri[0] != '\0') {
325 setenv("DEVICE_URI", device_uri, 1);
327 if (auth_info_required[0] != '\0') {
328 setenv("AUTH_INFO_REQUIRED", auth_info_required, 1);
331 smbspool:
332 snprintf(smbspool_cmd,
333 sizeof(smbspool_cmd),
334 "%s/smbspool",
335 get_dyn_BINDIR());
337 return execv(smbspool_cmd, argv);