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