Modify smbspool_krb5_wrapper to just fall through to smbspool if AUTH_INFO_REQUIRED...
[samba.git] / source3 / client / smbspool_krb5_wrapper.c
1 /*
2  * Unix SMB/CIFS implementation.
3  *
4  * CUPS printing backend helper to execute smbspool
5  *
6  * Copyright (C) 2010-2011 Andreas Schneider <asn@samba.org>
7  *
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.
12  *
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.
17  *
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/>.
20  */
21
22 #include "includes.h"
23 #include "system/filesys.h"
24 #include "system/passwd.h"
25
26 #include <errno.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include <cups/backend.h>
31
32 #include "dynconfig/dynconfig.h"
33
34 #undef calloc
35
36 enum cups_smb_dbglvl_e {
37         CUPS_SMB_LOG_DEBUG = 0,
38         CUPS_SMB_LOG_ERROR,
39 };
40 static void cups_smb_debug(enum cups_smb_dbglvl_e lvl, const char *format, ...);
41
42 #define CUPS_SMB_DEBUG(...) cups_smb_debug(CUPS_SMB_LOG_DEBUG, __VA_ARGS__)
43 #define CUPS_SMB_ERROR(...) cups_smb_debug(CUPS_SMB_LOG_DEBUG, __VA_ARGS__)
44
45 static void cups_smb_debug(enum cups_smb_dbglvl_e lvl, const char *format, ...)
46 {
47         const char *prefix = "DEBUG";
48         char buffer[1024];
49         va_list va;
50
51         va_start(va, format);
52         vsnprintf(buffer, sizeof(buffer), format, va);
53         va_end(va);
54
55         switch (lvl) {
56         case CUPS_SMB_LOG_DEBUG:
57                 prefix = "DEBUG";
58                 break;
59         case CUPS_SMB_LOG_ERROR:
60                 prefix = "ERROR";
61                 break;
62         }
63
64         fprintf(stderr,
65                 "%s: SMBSPOOL_KRB5 - %s\n",
66                 prefix,
67                 buffer);
68 }
69
70 /*
71  * This is a helper binary to execute smbspool.
72  *
73  * It needs to be installed or symlinked as:
74  *      /usr/lib/cups/backend/smb
75  *
76  * The permissions of the binary need to be set to 0700 so that it is executed
77  * as root. The binary switches to the user which is passed via the environment
78  * variable AUTH_UID, so we can access the kerberos ticket.
79  */
80 int main(int argc, char *argv[])
81 {
82         char smbspool_cmd[PATH_MAX] = {0};
83         struct passwd *pwd;
84         char gen_cc[PATH_MAX] = {0};
85         struct stat sb;
86         char *env;
87         uid_t uid = (uid_t)-1;
88         gid_t gid = (gid_t)-1;
89         unsigned long tmp;
90         int cmp;
91         int rc;
92
93         /* Check if AuthInfoRequired is set to negotiate */
94         env = getenv("AUTH_INFO_REQUIRED");
95
96         /* If not set, then just call smbspool. */
97         if (env == NULL) {
98                 CUPS_SMB_ERROR("AUTH_INFO_REQUIRED is not set");
99                 goto smbspool;
100         } else {
101                 CUPS_SMB_DEBUG("AUTH_INFO_REQUIRED=%s", env);
102                 cmp = strcmp(env, "negotiate");
103                 /* If AUTH_INFO_REQUIRED != "negotiate" then call smbspool. */
104                 if (cmp != 0) {
105                           CUPS_SMB_ERROR(
106                             "AUTH_INFO_REQUIRED is not set to negotiate");
107                           goto smbspool;
108                 }
109         }
110
111         uid = getuid();
112
113         CUPS_SMB_DEBUG("Started with uid=%d\n", uid);
114         if (uid != 0) {
115                 goto smbspool;
116         }
117
118         /*
119          * AUTH_UID gets only set if we have an incoming connection over the
120          * CUPS unix domain socket.
121          */
122         env = getenv("AUTH_UID");
123         if (env == NULL) {
124                 CUPS_SMB_ERROR("AUTH_UID is not set");
125                 fprintf(stderr, "ATTR: auth-info-required=negotiate\n");
126                 return CUPS_BACKEND_AUTH_REQUIRED;
127         }
128
129         if (strlen(env) > 10) {
130                 CUPS_SMB_ERROR("Invalid AUTH_UID");
131                 return CUPS_BACKEND_FAILED;
132         }
133
134         errno = 0;
135         tmp = strtoul(env, NULL, 10);
136         if (errno != 0 || tmp >= UINT32_MAX) {
137                 CUPS_SMB_ERROR("Failed to convert AUTH_UID=%s", env);
138                 return CUPS_BACKEND_FAILED;
139         }
140         uid = (uid_t)tmp;
141
142         pwd = getpwuid(uid);
143         if (pwd == NULL) {
144                 CUPS_SMB_ERROR("Failed to find system user: %u - %s",
145                                uid, strerror(errno));
146                 return CUPS_BACKEND_FAILED;
147         }
148         gid = pwd->pw_gid;
149
150         rc = setgroups(0, NULL);
151         if (rc != 0) {
152                 CUPS_SMB_ERROR("Failed to clear groups - %s",
153                                strerror(errno));
154                 return CUPS_BACKEND_FAILED;
155         }
156
157         CUPS_SMB_DEBUG("Switching to gid=%d", gid);
158         rc = setgid(gid);
159         if (rc != 0) {
160                 CUPS_SMB_ERROR("Failed to switch to gid=%u",
161                                gid,
162                                strerror(errno));
163                 return CUPS_BACKEND_FAILED;
164         }
165
166         CUPS_SMB_DEBUG("Switching to uid=%u", uid);
167         rc = setuid(uid);
168         if (rc != 0) {
169                 CUPS_SMB_ERROR("Failed to switch to uid=%u",
170                                uid,
171                                strerror(errno));
172                 return CUPS_BACKEND_FAILED;
173         }
174
175         snprintf(gen_cc, sizeof(gen_cc), "/tmp/krb5cc_%d", uid);
176
177         rc = lstat(gen_cc, &sb);
178         if (rc == 0) {
179                 snprintf(gen_cc, sizeof(gen_cc), "FILE:/tmp/krb5cc_%d", uid);
180         } else {
181                 snprintf(gen_cc, sizeof(gen_cc), "/run/user/%d/krb5cc", uid);
182
183                 rc = lstat(gen_cc, &sb);
184                 if (rc == 0 && S_ISDIR(sb.st_mode)) {
185                         snprintf(gen_cc,
186                                  sizeof(gen_cc),
187                                  "DIR:/run/user/%d/krb5cc",
188                                  uid);
189                 } else {
190 #if defined(__linux__)
191                         snprintf(gen_cc,
192                                  sizeof(gen_cc),
193                                  "KEYRING:persistent:%d",
194                                  uid);
195 #endif
196                 }
197         }
198
199         /*
200          * Make sure we do not have LD_PRELOAD or other security relevant
201          * environment variables set.
202          */
203 #ifdef HAVE_CLEARENV
204         clearenv();
205 #else
206         {
207                 extern char **environ;
208                 environ = calloc(1, sizeof(*environ));
209         }
210 #endif
211
212         CUPS_SMB_DEBUG("Setting KRB5CCNAME to '%s'", gen_cc);
213         setenv("KRB5CCNAME", gen_cc, 1);
214
215 smbspool:
216         snprintf(smbspool_cmd,
217                  sizeof(smbspool_cmd),
218                  "%s/smbspool",
219                  get_dyn_BINDIR());
220
221         return execv(smbspool_cmd, argv);
222 }