1 /*
2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6
7 /*
8 * lib/krb5/os/kuserok.c
9 *
10 * Copyright 1990,1993 by the Massachusetts Institute of Technology.
11 * All Rights Reserved.
12 *
13 * Export of this software from the United States of America may
14 * require a specific license from the United States Government.
15 * It is the responsibility of any person or organization contemplating
16 * export to obtain such a license before exporting.
17 *
18 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
19 * distribute this software and its documentation for any purpose and
20 * without fee is hereby granted, provided that the above copyright
21 * notice appear in all copies and that both that copyright notice and
22 * this permission notice appear in supporting documentation, and that
23 * the name of M.I.T. not be used in advertising or publicity pertaining
24 * to distribution of the software without specific, written prior
25 * permission. Furthermore if you modify this software you must label
26 * your software as modified software and not distribute it in such a
27 * fashion that it might be confused with the original M.I.T. software.
28 * M.I.T. makes no representations about the suitability of
29 * this software for any purpose. It is provided "as is" without express
30 * or implied warranty.
31 *
32 *
33 * krb5_kuserok()
34 */
35
36 #include "k5-int.h"
37 #if !defined(_WIN32) /* Not yet for Windows */
38 #include <stdio.h>
39 #include <string.h>
40 #include <stdlib.h>
41 #include <pwd.h>
42 #include <libintl.h>
43 #include <gssapi/gssapi.h>
44 #include <gssapi/gssapi_ext.h>
45 #include <gssapi_krb5.h>
46 #include <gssapiP_krb5.h>
47 #include <syslog.h>
48
49 #if defined(_AIX) && defined(_IBMR2)
50 #include <sys/access.h>
51 /* xlc has a bug with "const" */
52 #define getpwnam(user) getpwnam((char *)user)
53 #endif
54
55 #define MAX_USERNAME 65
56 #define CACHE_FILENAME_LEN 35
57
58 #if defined(__APPLE__) && defined(__MACH__)
59 #include <hfs/hfs_mount.h> /* XXX */
60 #define FILE_OWNER_OK(UID) ((UID) == 0 || (UID) == UNKNOWNUID)
61 #else
62 #define FILE_OWNER_OK(UID) ((UID) == 0)
63 #endif
64
65 /* Solaris Kerberos */
66 extern void
67 gsscred_set_options();
68
69 extern OM_uint32
70 gsscred_name_to_unix_cred_ext();
71
72 extern int
73 safechown(const char *src, uid_t uid, gid_t gid, int mode);
74
75 extern const char *error_message(long);
76
77
78 krb5_data tgtname = {
79 0,
80 KRB5_TGS_NAME_SIZE,
81 KRB5_TGS_NAME
82 };
83
84 /* Solaris Kerberos */
85 static krb5_error_code
krb5_move_ccache(krb5_context kcontext,krb5_principal client,struct passwd * pwd)86 krb5_move_ccache(krb5_context kcontext, krb5_principal client,
87 struct passwd *pwd)
88 {
89 char *name = 0;
90 static char ccache_name_buf[CACHE_FILENAME_LEN];
91 krb5_ccache ccache = NULL;
92 krb5_error_code retval;
93
94 name = getenv(KRB5_ENV_CCNAME);
95 if (name == 0)
96 /*
97 * This means that there was no forwarding
98 * of creds
99 */
100 return (0);
101 else {
102 /*
103 * creds have been forwarded and stored in
104 * KRB5_ENV_CCNAME and now we need to store it
105 * under uid
106 */
107
108 krb5_creds mcreds, save_v5creds;
109
110 memset(&mcreds, 0, sizeof (mcreds));
111 memset(&save_v5creds, 0, sizeof (save_v5creds));
112
113 mcreds.client = client;
114 retval = krb5_build_principal_ext(kcontext, &mcreds.server,
115 krb5_princ_realm(kcontext, client)->length,
116 krb5_princ_realm(kcontext, client)->data,
117 tgtname.length, tgtname.data,
118 krb5_princ_realm(kcontext, client)->length,
119 krb5_princ_realm(kcontext, client)->data,
120 0);
121 if (retval) {
122 syslog(LOG_ERR,
123 gettext("KRB5: %s while creating"
124 "V5 krbtgt principal "),
125 error_message(retval));
126 return (retval);
127 }
128
129 mcreds.ticket_flags = 0;
130 retval = krb5_cc_default(kcontext, &ccache);
131 if (retval) {
132 syslog(LOG_ERR,
133 gettext("KRB5: %s while getting "
134 "default cache "),
135 error_message(retval));
136 return (retval);
137 }
138
139 retval = krb5_cc_retrieve_cred(kcontext, ccache,
140 0,
141 &mcreds, &save_v5creds);
142 if (retval) {
143 syslog(LOG_ERR,
144 gettext("KRB5: %s while retrieving "
145 "cerdentials "),
146 error_message(retval));
147 return (retval);
148 }
149 /*
150 * reset the env variable and recreate the
151 * cache using the default cache name
152 */
153 retval = krb5_cc_destroy(kcontext, ccache);
154 if (retval) {
155 syslog(LOG_ERR,
156 gettext("KRB5: %s while destroying cache "),
157 error_message(retval));
158 return (retval);
159 }
160 krb5_unsetenv(KRB5_ENV_CCNAME);
161 snprintf(ccache_name_buf,
162 CACHE_FILENAME_LEN,
163 "FILE:/tmp/krb5cc_%d", pwd->pw_uid);
164 krb5_setenv(KRB5_ENV_CCNAME, ccache_name_buf, 1);
165 retval = krb5_cc_resolve(kcontext, ccache_name_buf, &ccache);
166 if (retval) {
167 syslog(LOG_ERR,
168 gettext("KRB5: %s while resolving cache "),
169 error_message(retval));
170 return (retval);
171 }
172 retval = krb5_cc_initialize(kcontext, ccache, client);
173 if (retval) {
174 syslog(LOG_ERR,
175 gettext("KRB5: %s while initializing cache "),
176 error_message(retval));
177 return (retval);
178 }
179 retval = krb5_cc_store_cred(kcontext, ccache, &save_v5creds);
180 if (retval) {
181 syslog(LOG_ERR,
182 gettext("KRB5: %s while storing creds "),
183 error_message(retval));
184 return (retval);
185 }
186 snprintf(ccache_name_buf,
187 CACHE_FILENAME_LEN,
188 "/tmp/krb5cc_%d", pwd->pw_uid);
189 if (safechown(ccache_name_buf, pwd->pw_uid,
190 pwd->pw_gid, -1) == -1) {
191 syslog(LOG_ERR,
192 gettext("KRB5: Can not change "
193 "ownership of cache file, "
194 "possible security breach\n"));
195 }
196 }
197
198 return (0);
199 }
200
201
202 /*
203 * Solaris Kerberos:
204 * krb5_gsscred: Given a kerberos principal try to find the corresponding
205 * local uid via the gss cred table. Return TRUE if the uid was found in the
206 * cred table, otherwise return FALSE.
207 */
208 static krb5_boolean
krb5_gsscred(krb5_principal principal,uid_t * uid)209 krb5_gsscred(krb5_principal principal, uid_t *uid)
210 {
211 OM_uint32 minor, major;
212 gss_name_t name;
213 gss_buffer_desc name_buf;
214
215 name_buf.value = &principal;
216 name_buf.length = sizeof (principal);
217
218 /*
219 * Convert the kerb principal in to a gss name
220 */
221 major = gss_import_name(&minor, &name_buf,
222 (gss_OID)gss_nt_krb5_principal, &name);
223
224 if (major != GSS_S_COMPLETE)
225 return (FALSE);
226
227 gsscred_set_options();
228
229 /*
230 * Get the uid mapping from the gsscred table.
231 * (but set flag to not call back into this mech as we do krb5
232 * auth_to_local name mapping from this module).
233 */
234 major = gsscred_name_to_unix_cred_ext(name, (gss_OID)gss_mech_krb5,
235 uid, 0, 0, 0, 0);
236
237 (void) gss_release_name(&minor, &name);
238
239 if (major != GSS_S_COMPLETE)
240 return (FALSE);
241
242 return (TRUE);
243 }
244
245 /*
246 * Given a Kerberos principal "principal", and a local username "luser",
247 * determine whether user is authorized to login according to the
248 * authorization file ("~luser/.k5login" by default). Returns TRUE
249 * if authorized, FALSE if not authorized.
250 *
251 * If there is no account for "luser" on the local machine, returns
252 * FALSE. If there is no authorization file, and the given Kerberos
253 * name "server" translates to the same name as "luser" (using
254 * krb5_aname_to_lname()), returns TRUE. Otherwise, if the authorization file
255 * can't be accessed, returns FALSE. Otherwise, the file is read for
256 * a matching principal name, instance, and realm. If one is found,
257 * returns TRUE, if none is found, returns FALSE.
258 *
259 * The file entries are in the format produced by krb5_unparse_name(),
260 * one entry per line.
261 *
262 */
263
264 krb5_boolean KRB5_CALLCONV
krb5_kuserok(krb5_context context,krb5_principal principal,const char * luser)265 krb5_kuserok(krb5_context context, krb5_principal principal, const char *luser)
266 {
267 struct stat sbuf;
268 struct passwd *pwd;
269 char pbuf[MAXPATHLEN];
270 krb5_boolean isok = FALSE;
271 FILE *fp;
272 char kuser[MAX_USERNAME];
273 char *princname;
274 char linebuf[BUFSIZ];
275 char *newline;
276 /* Solaris Kerberos */
277 uid_t uid;
278 int gobble;
279
280 /* no account => no access */
281 char pwbuf[BUFSIZ];
282 struct passwd pwx;
283 if (k5_getpwnam_r(luser, &pwx, pwbuf, sizeof(pwbuf), &pwd) != 0)
284 return(FALSE);
285 (void) strncpy(pbuf, pwd->pw_dir, sizeof(pbuf) - 1);
286 pbuf[sizeof(pbuf) - 1] = '\0';
287 (void) strncat(pbuf, "/.k5login", sizeof(pbuf) - 1 - strlen(pbuf));
288
289 if (access(pbuf, F_OK)) { /* not accessible */
290 /*
291 * if he's trying to log in as himself, and there is no .k5login file,
292 * let him. First, have krb5 check it's rules. If no success,
293 * search the gsscred table (the sequence here should be consistent
294 * with the uid mappings done for gssd).
295 */
296 if (!(krb5_aname_to_localname(context, principal,
297 sizeof(kuser), kuser))
298 && (strcmp(kuser, luser) == 0)) {
299 /* Solaris Kerberos */
300 if (krb5_move_ccache(context, principal, pwd))
301 return (FALSE);
302 return(TRUE);
303 }
304
305 if (krb5_gsscred(principal, &uid)) {
306 #ifdef DEBUG
307 char *princname;
308
309 (void)krb5_unparse_name(context, principal, &princname);
310 syslog(LOG_DEBUG, "gsscred mapped %s to %d expecting %d (%s)\n",
311 princname, uid, pwd->pw_uid, luser);
312 free(princname);
313 #endif
314 if (uid == pwd->pw_uid) {
315 if (krb5_move_ccache(context, principal, pwd))
316 return (FALSE);
317 return (TRUE);
318 }
319 }
320
321 }
322 if (krb5_unparse_name(context, principal, &princname))
323 return(FALSE); /* no hope of matching */
324
325 /* open ~/.k5login */
326 /* Solaris Kerberos */
327 if ((fp = fopen(pbuf, "rF")) == NULL) {
328 free(princname);
329 return(FALSE);
330 }
331 /*
332 * For security reasons, the .k5login file must be owned either by
333 * the user himself, or by root. Otherwise, don't grant access.
334 */
335 if (fstat(fileno(fp), &sbuf)) {
336 fclose(fp);
337 free(princname);
338 return(FALSE);
339 }
340 if (sbuf.st_uid != pwd->pw_uid && !FILE_OWNER_OK(sbuf.st_uid)) {
341 fclose(fp);
342 free(princname);
343 return(FALSE);
344 }
345
346 /* check each line */
347 while (!isok && (fgets(linebuf, BUFSIZ, fp) != NULL)) {
348 /* null-terminate the input string */
349 linebuf[BUFSIZ-1] = '\0';
350 newline = NULL;
351 /* nuke the newline if it exists */
352 if ((newline = strchr(linebuf, '\n')))
353 *newline = '\0';
354 if (!strcmp(linebuf, princname)) {
355 isok = TRUE;
356 /* Solaris Kerberos */
357 if (krb5_move_ccache(context, principal, pwd))
358 return (FALSE);
359 continue;
360 }
361 /* clean up the rest of the line if necessary */
362 if (!newline)
363 while (((gobble = getc(fp)) != EOF) && gobble != '\n');
364 }
365 free(princname);
366 fclose(fp);
367 return(isok);
368 }
369
370 /* Solaris Kerberos */
371 OM_uint32
krb5_gss_userok(OM_uint32 * minor,const gss_name_t pname,const char * user,int * user_ok)372 krb5_gss_userok(OM_uint32 *minor,
373 const gss_name_t pname,
374 const char *user,
375 int *user_ok)
376 {
377 krb5_context ctxt;
378 OM_uint32 kret;
379
380 if (pname == NULL || user == NULL)
381 return (GSS_S_CALL_INACCESSIBLE_READ);
382
383 if (minor == NULL || user_ok == NULL)
384 return (GSS_S_CALL_INACCESSIBLE_WRITE);
385
386 *user_ok = 0;
387
388 kret = krb5_gss_init_context(&ctxt);
389 if (kret) {
390 *minor = kret;
391 return (GSS_S_FAILURE);
392 }
393
394 if (! kg_validate_name(pname)) {
395 *minor = (OM_uint32) G_VALIDATE_FAILED;
396 krb5_free_context(ctxt);
397 return (GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME);
398 }
399
400 if (krb5_kuserok(ctxt, (krb5_principal) pname, user)) {
401 *user_ok = 1;
402 }
403
404 krb5_free_context(ctxt);
405 return (GSS_S_COMPLETE);
406 }
407
408 #else /* _WIN32 */
409
410 /*
411 * If the given Kerberos name "server" translates to the same name as "luser"
412 * (using * krb5_aname_to_lname()), returns TRUE.
413 */
414 krb5_boolean KRB5_CALLCONV
krb5_kuserok(context,principal,luser)415 krb5_kuserok(context, principal, luser)
416 krb5_context context;
417 krb5_principal principal;
418 const char *luser;
419 {
420 char kuser[50];
421
422 if (krb5_aname_to_localname(context, principal, sizeof(kuser), kuser))
423 return FALSE;
424
425 if (strcmp(kuser, luser) == 0)
426 return TRUE;
427
428 return FALSE;
429 }
430 #endif /* _WIN32 */
431