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