/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* lib/krb5/os/kuserok.c */ /* * Copyright 1990,1993,2007 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. */ #include "k5-int.h" #if !defined(_WIN32) /* Not yet for Windows */ #include #include #if defined(_AIX) && defined(_IBMR2) #include /* xlc has a bug with "const" */ #define getpwnam(user) getpwnam((char *)user) #endif #define MAX_USERNAME 65 #if defined(__APPLE__) && defined(__MACH__) #include /* XXX */ #define FILE_OWNER_OK(UID) ((UID) == 0 || (UID) == UNKNOWNUID) #else #define FILE_OWNER_OK(UID) ((UID) == 0) #endif enum result { ACCEPT, REJECT, PASS }; /* * Find the k5login filename for luser, either in the user's homedir or in a * configured directory under the username. */ static krb5_error_code get_k5login_filename(krb5_context context, const char *luser, const char *homedir, char **filename_out) { krb5_error_code ret; char *dir, *filename; *filename_out = NULL; ret = profile_get_string(context->profile, KRB5_CONF_LIBDEFAULTS, KRB5_CONF_K5LOGIN_DIRECTORY, NULL, NULL, &dir); if (ret != 0) return ret; if (dir == NULL) { /* Look in the user's homedir. */ if (asprintf(&filename, "%s/.k5login", homedir) < 0) return ENOMEM; } else { /* Look in the configured directory. */ if (asprintf(&filename, "%s/%s", dir, luser) < 0) ret = ENOMEM; profile_release_string(dir); if (ret) return ret; } *filename_out = filename; return 0; } /* * Determine whether principal is authorized to log in as luser according to * the user's k5login file. Return ACCEPT if the k5login file authorizes the * principal, PASS if the k5login file does not exist, or REJECT if the k5login * file exists but does not authorize the principal. If k5login files are * configured to be non-authoritative, pass instead of rejecting. */ static enum result k5login_ok(krb5_context context, krb5_principal principal, const char *luser) { int authoritative = TRUE, gobble; enum result result = REJECT; char *filename = NULL, *princname = NULL; char *newline, linebuf[BUFSIZ], pwbuf[BUFSIZ]; struct stat sbuf; struct passwd pwx, *pwd; FILE *fp = NULL; if (profile_get_boolean(context->profile, KRB5_CONF_LIBDEFAULTS, KRB5_CONF_K5LOGIN_AUTHORITATIVE, NULL, TRUE, &authoritative) != 0) goto cleanup; /* Get the local user's homedir and uid. */ if (k5_getpwnam_r(luser, &pwx, pwbuf, sizeof(pwbuf), &pwd) != 0) goto cleanup; if (get_k5login_filename(context, luser, pwd->pw_dir, &filename) != 0) goto cleanup; if (access(filename, F_OK) != 0) { result = PASS; goto cleanup; } if (krb5_unparse_name(context, principal, &princname) != 0) goto cleanup; fp = fopen(filename, "r"); if (fp == NULL) goto cleanup; set_cloexec_file(fp); /* For security reasons, the .k5login file must be owned either by * the user or by root. */ if (fstat(fileno(fp), &sbuf)) goto cleanup; if (sbuf.st_uid != pwd->pw_uid && !FILE_OWNER_OK(sbuf.st_uid)) goto cleanup; /* Check each line. */ while (result != ACCEPT && (fgets(linebuf, sizeof(linebuf), fp) != NULL)) { newline = strrchr(linebuf, '\n'); if (newline != NULL) *newline = '\0'; if (strcmp(linebuf, princname) == 0) result = ACCEPT; /* Clean up the rest of the line if necessary. */ if (newline == NULL) while (((gobble = getc(fp)) != EOF) && gobble != '\n'); } cleanup: free(princname); free(filename); if (fp != NULL) fclose(fp); /* If k5login files are non-authoritative, never reject. */ return (!authoritative && result == REJECT) ? PASS : result; } /* * Determine whether principal is authorized to log in as luser according to * aname-to-localname translation. Return ACCEPT if principal translates to * luser or PASS if it does not. */ static enum result an2ln_ok(krb5_context context, krb5_principal principal, const char *luser) { krb5_error_code ret; char kuser[MAX_USERNAME]; ret = krb5_aname_to_localname(context, principal, sizeof(kuser), kuser); if (ret != 0) return PASS; return (strcmp(kuser, luser) == 0) ? ACCEPT : PASS; } krb5_boolean KRB5_CALLCONV krb5_kuserok(krb5_context context, krb5_principal principal, const char *luser) { enum result result; result = k5login_ok(context, principal, luser); if (result == PASS) result = an2ln_ok(context, principal, luser); return (result == ACCEPT) ? TRUE : FALSE; } #else /* _WIN32 */ /* * If the given Kerberos name "server" translates to the same name as "luser" * (using * krb5_aname_to_lname()), returns TRUE. */ krb5_boolean KRB5_CALLCONV krb5_kuserok(context, principal, luser) krb5_context context; krb5_principal principal; const char *luser; { char kuser[50]; if (krb5_aname_to_localname(context, principal, sizeof(kuser), kuser)) return FALSE; if (strcmp(kuser, luser) == 0) return TRUE; return FALSE; } #endif /* _WIN32 */