commit 3ec524d3aa8f92b150a02062ae8faf0bb2ffaa9d Author: ghudson Date: Fri Oct 1 15:56:30 2010 +0000 ticket: 6792 subject: Implement k5login_directory and k5login_authoritative options Add and document two new options for controlling k5login behavior. git-svn-id: svn://anonsvn.mit.edu:/krb5/trunk@24402 dc483132-0cff-0310-8789-dd5450dbe970 diff --git a/doc/admin.texinfo b/doc/admin.texinfo index 8603b93..2a811de 100644 --- a/doc/admin.texinfo +++ b/doc/admin.texinfo @@ -468,6 +468,20 @@ Sets the maximum allowable amount of clockskew in seconds that the library will tolerate before assuming that a Kerberos message is invalid. The default value is @value{DefaultClockskew}. +@itemx k5login_authoritative +If the value of this relation is true (the default), principals must +be listed in a local user's k5login file to be granted login access, +if a k5login file exists. If the value of this relation is false, a +principal may still be granted login access through other mechanisms +even if a k5login file exists but does not list the principal. + +@itemx k5login_directory +If set, the library will look for a local user's k5login file within the +named directory, with a filename corresponding to the local username. +If not set, the library will look for k5login files in the user's home +directory, with the filename @code{.k5login}. For security reasons, +k5login files must be owned by the local user or by root. + @itemx kdc_timesync If this is set to 1 (for true), then client machines will compute the difference between their time and the time returned by the KDC in the diff --git a/src/config-files/krb5.conf.M b/src/config-files/krb5.conf.M index 2995aa2..e658e89 100644 --- a/src/config-files/krb5.conf.M +++ b/src/config-files/krb5.conf.M @@ -155,6 +155,20 @@ This relation sets the maximum allowable amount of clockskew in seconds that the library will tolerate before assuming that a Kerberos message is invalid. The default value is 300 seconds, or five minutes. +.IP k5login_authoritative +If the value of this relation is true (the default), principals must +be listed in a local user's k5login file to be granted login access, +if a k5login file exists. If the value of this relation is false, a +principal may still be granted login access through other mechanisms +even if a k5login file exists but does not list the principal. + +.IP k5login_directory +If set, the library will look for a local user's k5login file within +the named directory, with a filename corresponding to the local +username. If not set, the library will look for k5login files in the +user's home directory, with the filename .k5login. For security +reasons, k5login files must be owned by the local user or by root. + .IP kdc_timesync If the value of this relation is non-zero (the default), the library will compute the difference between the system clock and the time diff --git a/src/include/k5-int.h b/src/include/k5-int.h index 750f989..f2a037c 100644 --- a/src/include/k5-int.h +++ b/src/include/k5-int.h @@ -222,6 +222,8 @@ typedef INT64_TYPE krb5_int64; #define KRB5_CONF_IPROP_PORT "iprop_port" #define KRB5_CONF_IPROP_SLAVE_POLL "iprop_slave_poll" #define KRB5_CONF_IPROP_LOGFILE "iprop_logfile" +#define KRB5_CONF_K5LOGIN_AUTHORITATIVE "k5login_authoritative" +#define KRB5_CONF_K5LOGIN_DIRECTORY "k5login_directory" #define KRB5_CONF_KADMIND_PORT "kadmind_port" #define KRB5_CONF_KRB524_SERVER "krb524_server" #define KRB5_CONF_KDC "kdc" diff --git a/src/lib/krb5/os/kuserok.c b/src/lib/krb5/os/kuserok.c index 1bc7505..985bb14 100644 --- a/src/lib/krb5/os/kuserok.c +++ b/src/lib/krb5/os/kuserok.c @@ -48,105 +48,138 @@ #define FILE_OWNER_OK(UID) ((UID) == 0) #endif +enum result { ACCEPT, REJECT, PASS }; + /* - * Given a Kerberos principal "principal", and a local username "luser", - * determine whether user is authorized to login according to the - * authorization file ("~luser/.k5login" by default). Returns TRUE - * if authorized, FALSE if not authorized. - * - * If there is no account for "luser" on the local machine, returns - * FALSE. If there is no authorization file, and the given Kerberos - * name "server" translates to the same name as "luser" (using - * krb5_aname_to_lname()), returns TRUE. Otherwise, if the authorization file - * can't be accessed, returns FALSE. Otherwise, the file is read for - * a matching principal name, instance, and realm. If one is found, - * returns TRUE, if none is found, returns FALSE. - * - * The file entries are in the format produced by krb5_unparse_name(), - * one entry per line. - * + * 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; -krb5_boolean KRB5_CALLCONV -krb5_kuserok(krb5_context context, krb5_principal principal, const char *luser) + *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; + enum result result = REJECT; + char *filename = NULL, *princname = NULL; + char gobble, *newline, linebuf[BUFSIZ], pwbuf[BUFSIZ]; struct stat sbuf; - struct passwd *pwd; - char pbuf[MAXPATHLEN]; - krb5_boolean isok = FALSE; - FILE *fp; - char kuser[MAX_USERNAME]; - char *princname; - char linebuf[BUFSIZ]; - char *newline; - int gobble; - char pwbuf[BUFSIZ]; - struct passwd pwx; - int result; - - /* no account => no access */ + 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) - return(FALSE); - result = snprintf(pbuf, sizeof(pbuf), "%s/.k5login", pwd->pw_dir); - if (SNPRINTF_OVERFLOW(result, sizeof(pbuf))) - return(FALSE); - - if (access(pbuf, F_OK)) { /* not accessible */ - /* - * if he's trying to log in as himself, and there is no .k5login file, - * let him. To find out, call - * krb5_aname_to_localname to convert the principal to a name - * which we can string compare. - */ - if (!(krb5_aname_to_localname(context, principal, - sizeof(kuser), kuser)) - && (strcmp(kuser, luser) == 0)) { - return(TRUE); - } - } - if (krb5_unparse_name(context, principal, &princname)) - return(FALSE); /* no hope of matching */ + goto cleanup; + + if (get_k5login_filename(context, luser, pwd->pw_dir, &filename) != 0) + goto cleanup; - /* open ~/.k5login */ - if ((fp = fopen(pbuf, "r")) == NULL) { - free(princname); - return(FALSE); + 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 himself, or by root. Otherwise, don't grant access. - */ - if (fstat(fileno(fp), &sbuf)) { - fclose(fp); - free(princname); - return(FALSE); - } - if (sbuf.st_uid != pwd->pw_uid && !FILE_OWNER_OK(sbuf.st_uid)) { - fclose(fp); - free(princname); - return(FALSE); - } - /* check each line */ - while (!isok && (fgets(linebuf, BUFSIZ, fp) != NULL)) { - /* null-terminate the input string */ - linebuf[BUFSIZ-1] = '\0'; - newline = NULL; - /* nuke the newline if it exists */ - if ((newline = strchr(linebuf, '\n'))) + /* 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)) { - isok = TRUE; - continue; - } - /* clean up the rest of the line if necessary */ - if (!newline) + 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); - fclose(fp); - return(isok); + 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 */ commit 6f46ab42b718410aee67a888b3fefe7df8ce2062 Author: ghudson Date: Sat Oct 2 11:48:06 2010 +0000 ticket: 6792 In the krb5_kuserok implementation, fix an unintentional type change to "gobble" (was an int, was accidentally changed to a char) which could result in an infinite loop. git-svn-id: svn://anonsvn.mit.edu:/krb5/trunk@24413 dc483132-0cff-0310-8789-dd5450dbe970 diff --git a/src/lib/krb5/os/kuserok.c b/src/lib/krb5/os/kuserok.c index 985bb14..e1619f3 100644 --- a/src/lib/krb5/os/kuserok.c +++ b/src/lib/krb5/os/kuserok.c @@ -93,10 +93,10 @@ get_k5login_filename(krb5_context context, const char *luser, static enum result k5login_ok(krb5_context context, krb5_principal principal, const char *luser) { - int authoritative = TRUE; + int authoritative = TRUE, gobble; enum result result = REJECT; char *filename = NULL, *princname = NULL; - char gobble, *newline, linebuf[BUFSIZ], pwbuf[BUFSIZ]; + char *newline, linebuf[BUFSIZ], pwbuf[BUFSIZ]; struct stat sbuf; struct passwd pwx, *pwd; FILE *fp = NULL;