From b9f8e6d956ab7a1283630815fd53c84ef7995a12 Mon Sep 17 00:00:00 2001 From: Nicolas Williams Date: Thu, 8 Dec 2011 10:58:18 -0600 Subject: [PATCH] Add DENY rule for krb5_kuserok() and update manpage --- lib/krb5/krb5.conf.5 | 34 ++++++++++------ lib/krb5/kuserok.c | 97 ++++++++++++++++++++++++++++++++++++-------- 2 files changed, 102 insertions(+), 29 deletions(-) diff --git a/lib/krb5/krb5.conf.5 b/lib/krb5/krb5.conf.5 index 79afc69cf..36469039d 100644 --- a/lib/krb5/krb5.conf.5 +++ b/lib/krb5/krb5.conf.5 @@ -263,21 +263,27 @@ wrong server name into the gss_accept_sec_context call. .It Li k5login_directory = Va directory Alternative location for user .k5login files. Tokens in the form of %{luser} are expanded to the name of the user whose .k5login file is -%needed. +needed. This option is provided for compatibility with MIT krb5 +configuration files. .It Li k5login_authoritative = Va boolean If true then if a principal is not found in k5login files then -krb5_userok() will not fallback on principal to username mapping. +krb5_userok() will not fallback on principal to username mapping. This +option is provided for compatibility with MIT krb5 configuration files. .It Li kuserok = Va rule ... Specifies krb5_kuserok(3) behavior. If multiple values are given, then -krb5_kuserok(3) will try them in order until one succeeds or all fail. -Rules are implemented by plugins, with three built-in plugins described -below. Default: USER-K5LOGIN SIMPLE. +krb5_kuserok(3) will evaluate them in order until one succeeds or all +fail. Rules are implemented by plugins, with three built-in plugins +described below. Default: USER-K5LOGIN SIMPLE DENY. +.It Li kuserok = Va DENY +If set and evaluated then krb5_userok(3) will deny access to the given +username no matter what the principal name might be. .It Li kuserok = Va SIMPLE -If set then krb5_userok(3) will use principal to username mapping (see -auth_to_local below). If the principal maps to the requested username -then access is allowed. +If set and evaluated then krb5_userok(3) will use principal to username +mapping (see auth_to_local below). If the principal maps to the +requested username then access is allowed. .It Li kuserok = Va SYSTEM-K5LOGIN[:directory] -If set then krb5_userok(3) will use k5login files named after the +If set and evaluated then krb5_userok(3) will use k5login files named +after the .Va luser argument to krb5_kuserok(3) in the given directory or in /etc/k5login.d/. If a directory is given then tokens will be expanded; @@ -286,11 +292,13 @@ the %{luser} token will be replaced with the argument to krb5_kuserok(3). K5login files are text files, with each line containing just a principal name; principals apearing in a user's k5login file are permitted access to the user's account. Note: this rule -performs no ownership nor permissions checks on k5login files. +performs no ownership nor permissions checks on k5login files; proper +ownership and permissions/ACLs are expected due to the system k5login +location being a system location. .It Li kuserok = Va USER-K5LOGIN -If set then krb5_userok(3) will use ~luser/.k5login and -~luser/.k5login.d/*. User k5login files and directories must be owned -by the user and must not have world nor group write permissions. +If set and evaluated then krb5_userok(3) will use ~luser/.k5login and +~luser/.k5login.d/*. User k5login files and directories must be owned by +the user and must not have world nor group write permissions. .It Li aname2lname-text-db = Va filename The named file must be a sorted (in increasing order) text file where every line consists of an unparsed principal name optionally followed by diff --git a/lib/krb5/kuserok.c b/lib/krb5/kuserok.c index 90eaa0bb1..65007881b 100644 --- a/lib/krb5/kuserok.c +++ b/lib/krb5/kuserok.c @@ -70,6 +70,7 @@ static krb5_error_code plugin_reg_ret; static krb5plugin_kuserok_ftable kuserok_simple_plug; static krb5plugin_kuserok_ftable kuserok_sys_k5login_plug; static krb5plugin_kuserok_ftable kuserok_user_k5login_plug; +static krb5plugin_kuserok_ftable kuserok_deny_plug; static void reg_def_plugins_once(void *ctx) @@ -88,6 +89,10 @@ reg_def_plugins_once(void *ctx) KRB5_PLUGIN_KUSEROK, &kuserok_user_k5login_plug); if (!plugin_reg_ret) plugin_reg_ret = ret; + ret = krb5_plugin_register(context, PLUGIN_TYPE_DATA, + KRB5_PLUGIN_KUSEROK, &kuserok_deny_plug); + if (!plugin_reg_ret) + plugin_reg_ret = ret; } /** @@ -123,6 +128,8 @@ check_owner(krb5_context context, krb5_boolean is_system_location, */ if (is_system_location || owner == NULL) return 0; + krb5_set_error_message(context, EACCES, + "User k5login files not supported on Windows"); return EACCES; #else struct stat st; @@ -134,37 +141,67 @@ check_owner(krb5_context context, krb5_boolean is_system_location, #endif #ifdef POSIX_GETPWNAM_R - if (owner != NULL && getpwnam_r(owner, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0) + if (owner != NULL && getpwnam_r(owner, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0) { + krb5_set_error_message(context, errno, "User unknown (getpwnam_r())"); return EACCES; + } #else pwd = getpwnam(luser); - if (owner != NULL && pwd == NULL) + if (owner != NULL && pwd == NULL) { + krb5_set_error_message(context, errno, "User unknown (getpwnam())"); return EACCES; + } #endif if (dir) { - if (fstat(dirfd(dir), &st) == -1) + if (fstat(dirfd(dir), &st) == -1) { + krb5_set_error_message(context, errno, "fstat() of k5login.d failed"); return errno; - if (!S_ISDIR(st.st_mode)) + } + if (!S_ISDIR(st.st_mode)) { + krb5_set_error_message(context, ENOTDIR, "k5login.d not a directory"); return ENOTDIR; - if (st.st_dev != dirlstat->st_dev || st.st_ino != dirlstat->st_ino) + } + if (st.st_dev != dirlstat->st_dev || st.st_ino != dirlstat->st_ino) { + krb5_set_error_message(context, EACCES, + "k5login.d was renamed during kuserok " + "operation"); return EACCES; - if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) + } + if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) { + krb5_set_error_message(context, EACCES, + "k5login.d has world and/or group write " + "permissions"); return EACCES; /* XXX We should have a better code */ - if (pwd != NULL && pwd->pw_uid != st.st_uid && st.st_uid != 0) + } + if (pwd != NULL && pwd->pw_uid != st.st_uid && st.st_uid != 0) { + krb5_set_error_message(context, EACCES, + "k5login.d not owned by the user or root"); return EACCES; + } if (file == NULL) return 0; } if (file) { - if (fstat(fileno(file), &st) == -1) + if (fstat(fileno(file), &st) == -1) { + krb5_set_error_message(context, errno, "fstat() of k5login failed"); return errno; - if (S_ISDIR(st.st_mode)) + } + if (S_ISDIR(st.st_mode)) { + krb5_set_error_message(context, EISDIR, "k5login is a directory"); return EISDIR; - if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) + } + if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) { + krb5_set_error_message(context, EISDIR, + "k5login has world and/or group write " + "permissions"); return EACCES; /* XXX We should have a better code */ + } if (pwd == NULL || pwd->pw_uid == st.st_uid || st.st_uid == 0) return 0; } + + krb5_set_error_message(context, EACCES, + "k5login not owned by the user or root"); return EACCES; } @@ -244,11 +281,15 @@ check_directory(krb5_context context, if (lstat(dirname, &st) < 0) return errno; - if (!S_ISDIR(st.st_mode)) + if (!S_ISDIR(st.st_mode)) { + krb5_set_error_message(context, ENOTDIR, "k5login.d not a directory"); return ENOTDIR; + } - if ((d = opendir(dirname)) == NULL) + if ((d = opendir(dirname)) == NULL) { + krb5_set_error_message(context, ENOTDIR, "Could not open k5login.d"); return errno; + } ret = check_owner(context, is_system_location, d, &st, NULL, owner); if (ret) @@ -293,7 +334,7 @@ check_an2ln(krb5_context context, lname = malloc(strlen(luser) + 1); if (lname == NULL) - return ENOMEM; + return krb5_enomem(context); ret = krb5_aname_to_localname(context, principal, strlen(luser)+1, lname); if (ret) goto out; @@ -512,20 +553,24 @@ kuserok_user_k5login_plug_f(void *plug_ctx, krb5_context context, profile_dir = k5login_dir; if (profile_dir == NULL) { #ifdef POSIX_GETPWNAM_R - if (getpwnam_r(luser, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0) + if (getpwnam_r(luser, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0) { + krb5_set_error_message(context, errno, "User unknown (getpwnam_r())"); return KRB5_PLUGIN_NO_HANDLE; + } #else pwd = getpwnam (luser); #endif - if (pwd == NULL) + if (pwd == NULL) { + krb5_set_error_message(context, errno, "User unknown (getpwnam())"); return KRB5_PLUGIN_NO_HANDLE; + } profile_dir = pwd->pw_dir; } #define KLOGIN "/.k5login" if (asprintf(&path, "%s/.k5login.d", profile_dir) == -1) - return ENOMEM; + return krb5_enomem(context); ret = _krb5_expand_path_tokensv(context, path, &path_exp, "luser", luser, NULL); @@ -565,6 +610,19 @@ kuserok_user_k5login_plug_f(void *plug_ctx, krb5_context context, #endif } +static krb5_error_code +kuserok_deny_plug_f(void *plug_ctx, krb5_context context, const char *rule, + unsigned int flags, const char *k5login_dir, + const char *luser, krb5_const_principal principal, + krb5_boolean *result) +{ + if (strcmp(rule, "DENY") != 0) + return KRB5_PLUGIN_NO_HANDLE; + + *result = FALSE; + return 0; +} + static krb5_error_code kuser_ok_null_plugin_init(krb5_context context, void **ctx) { @@ -599,3 +657,10 @@ static krb5plugin_kuserok_ftable kuserok_user_k5login_plug = { kuserok_user_k5login_plug_f, }; +static krb5plugin_kuserok_ftable kuserok_deny_plug = { + KRB5_PLUGIN_KUSEROK_VERSION_0, + kuser_ok_null_plugin_init, + kuser_ok_null_plugin_fini, + kuserok_deny_plug_f, +}; +