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 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 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 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 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 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