Patch attached.
diff -uNrp openssh-5.8p1.orig/gss-serv-krb5.c openssh-5.8p1/gss-serv-krb5.c
--- openssh-5.8p1.orig/gss-serv-krb5.c  2006-08-31 22:38:36.000000000 -0700
+++ openssh-5.8p1/gss-serv-krb5.c       2011-02-10 15:03:29.000000000 -0800
@@ -32,7 +32,9 @@
 #include <sys/types.h>
 
 #include <stdarg.h>
+#include <stdio.h>
 #include <string.h>
+#include <unistd.h>
 
 #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 <krb5.h>
 #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 -uNrp openssh-5.8p1.orig/session.c openssh-5.8p1/session.c
--- openssh-5.8p1.orig/session.c        2010-11-30 17:02:59.000000000 -0800
+++ openssh-5.8p1/session.c     2011-02-10 14:48:46.000000000 -0800
@@ -807,6 +807,28 @@ do_exec(Session *s, const char *command)
                        s->is_subsystem = SUBSYSTEM_EXT;
                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 (command != NULL)
diff -uNrp openssh-5.8p1.orig/ssh-gss.h openssh-5.8p1/ssh-gss.h
--- openssh-5.8p1.orig/ssh-gss.h        2007-06-12 06:40:39.000000000 -0700
+++ openssh-5.8p1/ssh-gss.h     2011-02-10 14:48:46.000000000 -0800
@@ -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 */
diff -uNrp openssh-5.8p1.orig/sshd.8 openssh-5.8p1/sshd.8
--- openssh-5.8p1.orig/sshd.8   2010-11-04 16:20:14.000000000 -0700
+++ openssh-5.8p1/sshd.8        2011-02-10 14:48:46.000000000 -0800
@@ -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.
________________________________________________
Kerberos mailing list           [email protected]
https://mailman.mit.edu/mailman/listinfo/kerberos

Reply via email to