diff -up openssh-5.8p2/gss-serv-krb5.c.force_krb openssh-5.8p2/gss-serv-krb5.c --- openssh-5.8p2/gss-serv-krb5.c.force_krb 2006-09-01 07:38:36.000000000 +0200 +++ openssh-5.8p2/gss-serv-krb5.c 2011-05-19 03:41:45.801109545 +0200 @@ -32,7 +32,9 @@ #include #include +#include #include +#include #include "xmalloc.h" #include "key.h" @@ -40,12 +42,11 @@ #include "auth.h" #include "log.h" #include "servconf.h" +#include "misc.h" #include "buffer.h" #include "ssh-gss.h" -extern ServerOptions options; - #ifdef HEIMDAL # include #else @@ -56,6 +57,16 @@ extern ServerOptions options; # endif #endif +extern Authctxt *the_authctxt; +extern ServerOptions options; + +/* all commands are allowed by default */ +char **k5users_allowed_cmds = NULL; + +static int ssh_gssapi_k5login_exists(); +static int ssh_gssapi_krb5_cmdok(krb5_principal, const char *, const char *, + int); + static krb5_context krb_context = NULL; /* Initialise the krb5 library, for the stuff that GSSAPI won't do */ @@ -83,10 +94,11 @@ ssh_gssapi_krb5_init(void) */ static int -ssh_gssapi_krb5_userok(ssh_gssapi_client *client, char *name) +ssh_gssapi_krb5_userok(ssh_gssapi_client *client, char *luser) { krb5_principal princ; int retval; + int k5login_exists; if (ssh_gssapi_krb5_init() == 0) return 0; @@ -97,10 +109,22 @@ ssh_gssapi_krb5_userok(ssh_gssapi_client krb5_get_err_text(krb_context, retval)); return 0; } - if (krb5_kuserok(krb_context, princ, name)) { + /* krb5_kuserok() returns 1 if .k5login DNE and this is self-login. + * We have to make sure to check .k5users in that case. */ + k5login_exists = ssh_gssapi_k5login_exists(); + /* NOTE: .k5login and .k5users must opened as root, not the user, + * because if they are on a krb5-protected filesystem, user credentials + * to access these files aren't available yet. */ + if (krb5_kuserok(krb_context, princ, luser) && k5login_exists) { retval = 1; logit("Authorized to %s, krb5 principal %s (krb5_kuserok)", - name, (char *)client->displayname.value); + luser, (char *)client->displayname.value); + } else if (ssh_gssapi_krb5_cmdok(princ, client->exportedname.value, + luser, k5login_exists)) { + retval = 1; + logit("Authorized to %s, krb5 principal %s " + "(ssh_gssapi_krb5_cmdok)", + luser, (char *)client->displayname.value); } else retval = 0; @@ -108,6 +132,134 @@ ssh_gssapi_krb5_userok(ssh_gssapi_client return retval; } +/* Test for existence of .k5login. + * We need this as part of our .k5users check, because krb5_kuserok() + * returns success if .k5login DNE and user is logging in as himself. + * With .k5login absent and .k5users present, we don't want absence + * of .k5login to authorize self-login. (absence of both is required) + * Returns 1 if .k5login is available, 0 otherwise. + */ +static int +ssh_gssapi_k5login_exists() +{ + char file[MAXPATHLEN]; + struct passwd *pw = the_authctxt->pw; + + snprintf(file, sizeof(file), "%s/.k5login", pw->pw_dir); + return access(file, F_OK) == 0; +} + +/* check .k5users for login or command authorization + * Returns 1 if principal is authorized, 0 otherwise. + * If principal is authorized, (global) k5users_allowed_cmds may be populated. + */ +static int +ssh_gssapi_krb5_cmdok(krb5_principal principal, const char *name, + const char *luser, int k5login_exists) +{ + FILE *fp; + char file[MAXPATHLEN]; + char line[BUFSIZ]; + char kuser[65]; /* match krb5_kuserok() */ + struct stat st; + struct passwd *pw = the_authctxt->pw; + int found_principal = 0; + int ncommands = 0, allcommands = 0; + u_long linenum; + + snprintf(file, sizeof(file), "%s/.k5users", pw->pw_dir); + /* If both .k5login and .k5users DNE, self-login is ok. */ + if (!k5login_exists && (access(file, F_OK) == -1)) { + return (krb5_aname_to_localname(krb_context, principal, + sizeof(kuser), kuser) == 0) && + (strcmp(kuser, luser) == 0); + } + if ((fp = fopen(file, "r")) == NULL) { + int saved_errno = errno; + /* 2nd access check to ease debugging if file perms are wrong. + * But we don't want to report this if .k5users simply DNE. */ + if (access(file, F_OK) == 0) { + logit("User %s fopen %s failed: %s", + pw->pw_name, file, strerror(saved_errno)); + } + return 0; + } + /* .k5users must be owned either by the user or by root */ + if (fstat(fileno(fp), &st) == -1) { + /* can happen, but very wierd error so report it */ + logit("User %s fstat %s failed: %s", + pw->pw_name, file, strerror(errno)); + fclose(fp); + return 0; + } + if (!(st.st_uid == pw->pw_uid || st.st_uid == 0)) { + logit("User %s %s is not owned by root or user", + pw->pw_name, file); + fclose(fp); + return 0; + } + /* .k5users must be a regular file. krb5_kuserok() doesn't do this + * check, but we don't want to be deficient if they add a check. */ + if (!S_ISREG(st.st_mode)) { + logit("User %s %s is not a regular file", pw->pw_name, file); + fclose(fp); + return 0; + } + /* file exists; initialize k5users_allowed_cmds (to none!) */ + k5users_allowed_cmds = xcalloc(++ncommands, + sizeof(*k5users_allowed_cmds)); + + /* Check each line. ksu allows unlimited length lines. We don't. */ + while (!allcommands && read_keyfile_line(fp, file, line, sizeof(line), + &linenum) != -1) { + char *token; + + /* we parse just like ksu, even though we could do better */ + token = strtok(line, " \t\n"); + if (strcmp(name, token) == 0) { + /* we matched on client principal */ + found_principal = 1; + if ((token = strtok(NULL, " \t\n")) == NULL) { + /* only shell is allowed */ + k5users_allowed_cmds[ncommands-1] = + xstrdup(pw->pw_shell); + k5users_allowed_cmds = + xrealloc(k5users_allowed_cmds, ++ncommands, + sizeof(*k5users_allowed_cmds)); + break; + } + /* process the allowed commands */ + while (token) { + if (strcmp(token, "*") == 0) { + allcommands = 1; + break; + } + k5users_allowed_cmds[ncommands-1] = + xstrdup(token); + k5users_allowed_cmds = + xrealloc(k5users_allowed_cmds, ++ncommands, + sizeof(*k5users_allowed_cmds)); + token = strtok(NULL, " \t\n"); + } + } + } + if (k5users_allowed_cmds) { + /* terminate vector */ + k5users_allowed_cmds[ncommands-1] = NULL; + /* if all commands are allowed, free vector */ + if (allcommands) { + int i; + for (i = 0; i < ncommands; i++) { + free(k5users_allowed_cmds[i]); + } + free(k5users_allowed_cmds); + k5users_allowed_cmds = NULL; + } + } + fclose(fp); + return found_principal; +} + /* This writes out any forwarded credentials from the structure populated * during userauth. Called after we have setuid to the user */ diff -up openssh-5.8p2/session.c.force_krb openssh-5.8p2/session.c --- openssh-5.8p2/session.c.force_krb 2011-05-19 03:41:41.000000000 +0200 +++ openssh-5.8p2/session.c 2011-05-19 03:43:32.437173662 +0200 @@ -816,6 +816,29 @@ do_exec(Session *s, const char *command) debug("Forced command (key option) '%.900s'", command); } +#ifdef GSSAPI +#ifdef KRB5 /* k5users_allowed_cmds only available w/ GSSAPI+KRB5 */ + else if (k5users_allowed_cmds) { + const char *match = command; + int allowed = 0, i = 0; + + if (!match) + match = s->pw->pw_shell; + while (k5users_allowed_cmds[i]) { + if (strcmp(match, k5users_allowed_cmds[i++]) == 0) { + debug("Allowed command '%.900s'", match); + allowed = 1; + break; + } + } + if (!allowed) { + debug("command '%.900s' not allowed", match); + return 1; + } + } +#endif +#endif + #ifdef SSH_AUDIT_EVENTS if (s->command != NULL || s->command_handle != -1) fatal("do_exec: command already set"); diff -up openssh-5.8p2/sshd.8.force_krb openssh-5.8p2/sshd.8 --- openssh-5.8p2/sshd.8.force_krb 2011-05-19 03:41:30.582114401 +0200 +++ openssh-5.8p2/sshd.8 2011-05-19 03:41:46.159106308 +0200 @@ -320,6 +320,7 @@ Finally, the server and the client enter The client tries to authenticate itself using host-based authentication, public key authentication, +GSSAPI authentication, challenge-response authentication, or password authentication. .Pp @@ -788,6 +789,12 @@ This file is used in exactly the same wa but allows host-based authentication without permitting login with rlogin/rsh. .Pp +.It Pa ~/.k5login +.It Pa ~/.k5users +These files enforce GSSAPI/Kerberos authentication access control. +Further details are described in +.Xr ksu 1 . +.Pp .It Pa ~/.ssh/ This directory is the default location for all user-specific configuration and authentication information. diff -up openssh-5.8p2/ssh-gss.h.force_krb openssh-5.8p2/ssh-gss.h --- openssh-5.8p2/ssh-gss.h.force_krb 2007-06-12 15:40:39.000000000 +0200 +++ openssh-5.8p2/ssh-gss.h 2011-05-19 03:41:46.302234118 +0200 @@ -48,6 +48,10 @@ #define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name #endif /* GSS_C_NT_... */ #endif /* !HEIMDAL */ + +/* .k5users support */ +extern char **k5users_allowed_cmds; + #endif /* KRB5 */ /* draft-ietf-secsh-gsskeyex-06 */