s3:utils: Remove pointless if-clause for remote_machine
[metze/samba/wip.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_DEBUG("AUTH_INFO_REQUIRED is not set - "
99                                "execute smbspool");
100                 goto smbspool;
101         } else {
102                 CUPS_SMB_DEBUG("AUTH_INFO_REQUIRED=%s", env);
103
104                 cmp = strcmp(env, "username,password");
105                 if (cmp == 0) {
106                         CUPS_SMB_DEBUG("Authenticate using username/password - "
107                                        "execute smbspool");
108                         goto smbspool;
109                 }
110
111                 /* if AUTH_INFO_REQUIRED=none */
112                 cmp = strcmp(env, "negotiate");
113                 if (cmp != 0) {
114                         CUPS_SMB_ERROR("Authentication unsupported");
115                         fprintf(stderr, "ATTR: auth-info-required=negotiate\n");
116                         return CUPS_BACKEND_AUTH_REQUIRED;
117                 }
118         }
119
120         uid = getuid();
121
122         CUPS_SMB_DEBUG("Started with uid=%d\n", uid);
123         if (uid != 0) {
124                 goto smbspool;
125         }
126
127         /*
128          * AUTH_UID gets only set if we have an incoming connection over the
129          * CUPS unix domain socket.
130          */
131         env = getenv("AUTH_UID");
132         if (env == NULL) {
133                 CUPS_SMB_ERROR("AUTH_UID is not set");
134                 fprintf(stderr, "ATTR: auth-info-required=negotiate\n");
135                 return CUPS_BACKEND_AUTH_REQUIRED;
136         }
137
138         if (strlen(env) > 10) {
139                 CUPS_SMB_ERROR("Invalid AUTH_UID");
140                 return CUPS_BACKEND_FAILED;
141         }
142
143         errno = 0;
144         tmp = strtoul(env, NULL, 10);
145         if (errno != 0 || tmp >= UINT32_MAX) {
146                 CUPS_SMB_ERROR("Failed to convert AUTH_UID=%s", env);
147                 return CUPS_BACKEND_FAILED;
148         }
149         uid = (uid_t)tmp;
150
151         pwd = getpwuid(uid);
152         if (pwd == NULL) {
153                 CUPS_SMB_ERROR("Failed to find system user: %u - %s",
154                                uid, strerror(errno));
155                 return CUPS_BACKEND_FAILED;
156         }
157         gid = pwd->pw_gid;
158
159         rc = setgroups(0, NULL);
160         if (rc != 0) {
161                 CUPS_SMB_ERROR("Failed to clear groups - %s",
162                                strerror(errno));
163                 return CUPS_BACKEND_FAILED;
164         }
165
166         CUPS_SMB_DEBUG("Switching to gid=%d", gid);
167         rc = setgid(gid);
168         if (rc != 0) {
169                 CUPS_SMB_ERROR("Failed to switch to gid=%u",
170                                gid,
171                                strerror(errno));
172                 return CUPS_BACKEND_FAILED;
173         }
174
175         CUPS_SMB_DEBUG("Switching to uid=%u", uid);
176         rc = setuid(uid);
177         if (rc != 0) {
178                 CUPS_SMB_ERROR("Failed to switch to uid=%u",
179                                uid,
180                                strerror(errno));
181                 return CUPS_BACKEND_FAILED;
182         }
183
184         env = getenv("KRB5CCNAME");
185         if (env != NULL && env[0] != 0) {
186                 snprintf(gen_cc, sizeof(gen_cc), "%s", env);
187
188                 goto create_env;
189         }
190
191         snprintf(gen_cc, sizeof(gen_cc), "/tmp/krb5cc_%d", uid);
192
193         rc = lstat(gen_cc, &sb);
194         if (rc == 0) {
195                 snprintf(gen_cc, sizeof(gen_cc), "FILE:/tmp/krb5cc_%d", uid);
196         } else {
197                 snprintf(gen_cc, sizeof(gen_cc), "/run/user/%d/krb5cc", uid);
198
199                 rc = lstat(gen_cc, &sb);
200                 if (rc == 0 && S_ISDIR(sb.st_mode)) {
201                         snprintf(gen_cc,
202                                  sizeof(gen_cc),
203                                  "DIR:/run/user/%d/krb5cc",
204                                  uid);
205                 } else {
206 #if defined(__linux__)
207                         snprintf(gen_cc,
208                                  sizeof(gen_cc),
209                                  "KEYRING:persistent:%d",
210                                  uid);
211 #endif
212                 }
213         }
214
215 create_env:
216         /*
217          * Make sure we do not have LD_PRELOAD or other security relevant
218          * environment variables set.
219          */
220 #ifdef HAVE_CLEARENV
221         clearenv();
222 #else
223         {
224                 extern char **environ;
225                 environ = calloc(1, sizeof(*environ));
226         }
227 #endif
228
229         CUPS_SMB_DEBUG("Setting KRB5CCNAME to '%s'", gen_cc);
230         setenv("KRB5CCNAME", gen_cc, 1);
231
232 smbspool:
233         snprintf(smbspool_cmd,
234                  sizeof(smbspool_cmd),
235                  "%s/smbspool",
236                  get_dyn_BINDIR());
237
238         return execv(smbspool_cmd, argv);
239 }