Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > e16d0c94ff2c9e93ba4eea60f7b68478 > files > 71

krb5-1.6.1-70.el5_9.2.src.rpm

Backported to 1.6.1, with changes from r24413 added.

commit 3ec524d3aa8f92b150a02062ae8faf0bb2ffaa9d
Author: ghudson <ghudson@dc483132-0cff-0310-8789-dd5450dbe970>
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/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,104 +48,149 @@
 #define FILE_OWNER_OK(UID)  ((UID) == 0)
 #endif
 
-/*
- * 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.
- *
- */
-
-krb5_boolean KRB5_CALLCONV
-krb5_kuserok(krb5_context context, krb5_principal principal, const char *luser)
-{
-    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;
-
-    /* no account => no access */
-    char pwbuf[BUFSIZ];
-    struct passwd pwx;
-    if (k5_getpwnam_r(luser, &pwx, pwbuf, sizeof(pwbuf), &pwd) != 0)
-	return(FALSE);
-    (void) strncpy(pbuf, pwd->pw_dir, sizeof(pbuf) - 1);
-    pbuf[sizeof(pbuf) - 1] = '\0';
-    (void) strncat(pbuf, "/.k5login", sizeof(pbuf) - 1 - strlen(pbuf));
-
-    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 */
-
-    /* open ~/.k5login */
-    if ((fp = fopen(pbuf, "r")) == NULL) {
-	free(princname);
-	return(FALSE);
-    }
-    /*
-     * 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')))
-	    *newline = '\0';
-	if (!strcmp(linebuf, princname)) {
-	    isok = TRUE;
-	    continue;
-	}
-	/* clean up the rest of the line if necessary */
-	if (!newline)
-	    while (((gobble = getc(fp)) != EOF) && gobble != '\n');
-    }
-    free(princname);
-    fclose(fp);
-    return(isok);
-}
+#define KRB5_CONF_LIBDEFAULTS                 "libdefaults"
+#define KRB5_CONF_K5LOGIN_AUTHORITATIVE       "k5login_authoritative"
+#define KRB5_CONF_K5LOGIN_DIRECTORY           "k5login_directory"
+static void
+set_cloexec_file(FILE *fp)
+{
+	int fl;
+	fl = fcntl(fileno(fp), F_GETFD);
+	fcntl(fileno(fp), F_SETFD, fl | FD_CLOEXEC);
+}
+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 */