PATCH: krb4/krb5/... names/patterns in auth_keys entries

Nicolas Williams Nicolas.Williams at ubsw.com
Fri Jan 25 03:54:52 EST 2002


This patch (to OpenSSH 3.0.2p1) adds support for using krb4, krb5 and
other principal names in authorized_keys entries.

It's a sort of replacement for .klogin and .k5login, but it's much more
general than .k*login as it applies to any authentication mechanism
where a name is associated with the ssh client and it supports name
patterns and all the normal authorized_keys entry options we're used to.

Now you can have entries like these in your authorized_keys files:

ssh-ext-named:krb5 someuser at SOMEREALM
deny-access ssh-ext-named:krb5 joe/superroot at SOMEREALM
ssh-ext-name-pat:krb5 */superroot at SOMERALM
command=/local/bin/inventory ssh-name-pat:krb5 inventory/*.mydomain at MYREALM
...

Double quotes can be used when key names contain whitespace and '\' can
be used inside double-quotes to quote quote (use '\\' for '\')...

This feature applies to SSHv1 krb4 and krb5 auth. It also applies to
SSHv2, with Simon Wilkinson's gsskeyex patch, to GSS-API authentication,
either with the Kerberos V GSS mechanism or the GSI GSS mechanism.

Features added:

 - ssh-named		key entry type for authorized_keys files
 - ssh-name-pat 	key entry type for authorized_keys files

 - deny-access		option for authorized_keys files

 - SSH_AUTH_NAME	env var added by sshd for clients with named keys
 - SSH_AUTH_NAME_TYPE	env var added by sshd for clients with named keys

I really hope that this feature or a variation thereof will find its way
into OpenSSH. In conjunction with Kerberos (IV or V) it can be extremely
useful:

 - key management is simplified: key management is done at the KDC and
   there is no need to edit authorized_keys files all over to change or
   revoke keys!

 - authorized_keys is much more featureful than .klogin and .k5login
   are, regardless of Kerberos implementation source (KTH, Heimdal, MIT,
   SEAM, all implement pretty much the same all-or-nothing
   .klogin/.k5login functionality).

 - patterns can be very useful. For example:

   command=/local/bin/inventory ssh-name-pat:krb5 inventory/*.mydomain at MYREALM

   which allows me to setup new inventory master hosts without having to
   change ~root/.authorized_keys on all thousands of servers.

Below you should find two versions of this patch, one against OpenSSH
3.0.2p1, the other against 3.0.2p1 + Simon Willkinson's GSS-API patches.

Files modified:

 - key.h
    - added KEY_NAME key type
    - added KEY_NAME_PAT key type
    - added name, name_len and name_type fields to the Key struct
    - added prototype for key_match()

 - key.c
    - added initialization/finalization of new Key fields to key_new()/key_free()
    - added named/pattern key type support to a variety of functions,
      including key_read() and key_write(), among others
    - added key_match() implementation

 - auth-options.h
    - added void auth_set_key_env(Key *) prototype

 - auth-options.c
    - added auth_set_key_env() implementation
    - modified auth_parse_options() to return (-1) when new deny-access
      option is encountered

 - auth-rsa.c
    - modified auth_parse_options() return value check according to the
      change made to auth_parse_options()

 - auth2.c
    - user_key_allowed() is not static now
    - modified user_key_allowed2() to:
       - try key_match() if key_equal() fails
       - check the result of auth_parse_options() for negative, 0, or
         positive values.
    - modified userauth_pubkey() to check for a positive return from
      user_key_allowed()

 - sshd.8
    - added documentation for new key types and the new auth_keys option

 - auth-krb4.c
    - modified auth_krb4() to build a Key struct and call user_key_allowed()

 - auth-krb5.c
    - modified auth_krb5() to build a Key struct and call user_key_allowed()

 - gss-serv.c
    - modified ssh_gssapi_krb5_userok() to build a Key struct and 
      call user_key_allowed()


Comments?


Nico
--
-DISCLAIMER: an automatically appended disclaimer may follow. By posting-
-to a public e-mail mailing list I hereby grant permission to distribute-
-and copy this message.-
-------------- next part --------------
Index: 3_0_2p1.1/sshd.8
--- 3_0_2p1.1/sshd.8 Thu, 10 Jan 2002 14:11:10 -0500
+++ 3_0_2p1_w_named_keys.1(w)/sshd.8 Thu, 24 Jan 2002 10:52:24 -0500
@@ -932,7 +932,8 @@
 is the default file that lists the public keys that are
 permitted for RSA authentication in protocol version 1
 and for public key authentication (PubkeyAuthentication)
-in protocol version 2.
+in protocol version 2. It can also list key names or key patterns
+for external authentication systems, such as krb4, krb5, gsi, etc...
 .Cm AuthorizedKeysFile
 may be used to specify an alternative file.
 .Pp
@@ -955,7 +956,19 @@
 For protocol version 2 the keytype is
 .Dq ssh-dss
 or
-.Dq ssh-rsa .
+.Dq ssh-rsa
+or
+.Dq ssh-named:<keytype>
+or
+.Dq ssh-name-pat:<keytype> .
+.Pp
+Named keys and key name patterns follow the latter two, in double
+quotes if they contain whitespace. Named key types may include:
+.Dq krb4 ,
+.Dq krb5
+and/or
+.Dq gsi ,
+depending on what features are compiled in to OpenSSH.
 .Pp
 Note that lines in this file are usually several hundred bytes long
 (because of the size of the RSA key modulus).
@@ -1017,6 +1030,10 @@
 This option is automatically disabled if
 .Cm UseLogin
 is enabled.
+.It Cm deny-access
+This option ends authorized_keys2 processing if the key matches. This
+option is only really useful with named key and named key pattern
+entries.
 .It Cm no-port-forwarding
 Forbids TCP/IP forwarding when this key is used for authentication.
 Any port forward requests by the client will return an error.
Index: 3_0_2p1.1/key.h
--- 3_0_2p1.1/key.h Wed, 21 Nov 2001 10:38:46 -0500
+++ 3_0_2p1_w_named_keys.1(w)/key.h Thu, 24 Jan 2002 10:52:24 -0500
@@ -34,7 +34,9 @@
 	KEY_RSA1,
 	KEY_RSA,
 	KEY_DSA,
-	KEY_UNSPEC
+	KEY_UNSPEC,
+	KEY_NAME,
+	KEY_NAME_PAT
 };
 enum fp_type {
 	SSH_FP_SHA1,
@@ -53,12 +55,15 @@
 	int	 flags;
 	RSA	*rsa;
 	DSA	*dsa;
+	u_char	*name;
+	char	*name_type;
 };
 
 Key	*key_new(int);
 Key	*key_new_private(int);
 void	 key_free(Key *);
 int	 key_equal(Key *, Key *);
+int	 key_match(Key *a, Key *b);
 char	*key_fingerprint(Key *, enum fp_type, enum fp_rep);
 char	*key_type(Key *);
 int	 key_write(Key *, FILE *);
Index: 3_0_2p1.1/key.c
--- 3_0_2p1.1/key.c Wed, 21 Nov 2001 10:38:46 -0500
+++ 3_0_2p1_w_named_keys.1(w)/key.c Thu, 24 Jan 2002 10:52:24 -0500
@@ -57,6 +57,8 @@
 	k->flags = 0;
 	k->dsa = NULL;
 	k->rsa = NULL;
+	k->name = NULL;
+	k->name_type = NULL;
 	switch (k->type) {
 	case KEY_RSA1:
 	case KEY_RSA:
@@ -73,6 +75,8 @@
 		dsa->pub_key = BN_new();
 		k->dsa = dsa;
 		break;
+	case KEY_NAME:
+	case KEY_NAME_PAT:
 	case KEY_UNSPEC:
 		break;
 	default:
@@ -120,6 +124,13 @@
 			DSA_free(k->dsa);
 		k->dsa = NULL;
 		break;
+	case KEY_NAME:
+	case KEY_NAME_PAT:
+		if (k->name != NULL)
+			xfree(k->name);
+		if (k->name_type != NULL)
+			xfree(k->name_type);
+		break;
 	case KEY_UNSPEC:
 		break;
 	default:
@@ -131,8 +142,9 @@
 int
 key_equal(Key *a, Key *b)
 {
-	if (a == NULL || b == NULL || a->type != b->type)
+	if (a == NULL || b == NULL || a->type != b->type) {
 		return 0;
+	}
 	switch (a->type) {
 	case KEY_RSA1:
 	case KEY_RSA:
@@ -147,12 +159,65 @@
 		    BN_cmp(a->dsa->g, b->dsa->g) == 0 &&
 		    BN_cmp(a->dsa->pub_key, b->dsa->pub_key) == 0;
 		break;
+	case KEY_NAME:
+		if ((a->name_type == NULL && b->name_type == NULL) ||
+		    (a->name_type == b->name_type))
+			return (strcmp(a->name, b->name) == 0);
+		if (a->name_type == NULL || b->name_type == NULL)
+			return 0;
+		if (strcmp(a->name_type, b->name_type) == 0)
+			return (strcmp(a->name, b->name) == 0);
+		break;
+	case KEY_NAME_PAT:
+		return 0;
+		break;
 	default:
 		fatal("key_equal: bad key type %d", a->type);
 		break;
 	}
 	return 0;
 }
+int
+key_match(Key *a, Key *b)
+{
+	debug3("key_match: trying to match %x and %x", a, b);
+	if (a == NULL || b == NULL)
+		return 0;
+
+	debug3("key_match: trying to match key types %d and %d -- KEY_NAME_PAT == %d", a->type, b->type, KEY_NAME_PAT);
+	/* One key must be a name pattern, the other must be a name */
+	if (!(a->type == KEY_NAME_PAT && b->type == KEY_NAME) &&
+	    !(b->type == KEY_NAME_PAT && a->type == KEY_NAME))
+		return 0;
+	
+	/* Both keys must have name types, or both must not */
+	/* or one key must have '*' as its name type        */
+	if ((a->name_type == NULL && b->name_type != NULL) ||
+	    (b->name_type == NULL && a->name_type != NULL)) {
+
+		debug3("key_match: foo");
+		if (a->name_type != NULL && *(a->name_type) != '*')
+			return 0;
+		if (b->name_type != NULL && *(b->name_type) != '*')
+			return 0;
+	}
+
+	/* Name type "*" matches any name type */
+	/* Otherwise name types must match */
+	if ((a->name_type != NULL && strcmp(a->name_type, b->name_type) != 0) &&
+	    (*(a->name_type) != '*' || *(b->name_type) != '*')) {
+		debug3("key_match: a->name_type == %s", a->name_type ? a->name_type : "");
+		debug3("key_match: b->name_type == %s", b->name_type ? b->name_type : "");
+		return 0;
+	    }
+
+	debug3("key_match: trying to match %s WITH %s", a->name, b->name);
+	if (a->type == KEY_NAME_PAT)
+		return match_pattern(b->name, a->name);
+	else
+		return match_pattern(a->name, b->name);
+}
+
 
 static u_char*
 key_fingerprint_raw(Key *k, enum fp_type dgst_type, size_t *dgst_raw_length)
@@ -161,7 +226,7 @@
 	EVP_MD_CTX ctx;
 	u_char *blob = NULL;
 	u_char *retval = NULL;
-	int len = 0;
+	u_int len = 0;
 	int nlen, elen;
 
 	*dgst_raw_length = 0;
@@ -364,11 +429,12 @@
 {
 	Key *k;
 	int success = -1;
-	char *cp, *space;
+	char *cp, *space, *name_type;
 	int len, n, type;
 	u_int bits;
-	u_char *blob;
+	u_char *blob = NULL;
 
+	name_type = NULL;
 	cp = *cpp;
 
 	switch(ret->type) {
@@ -391,6 +457,8 @@
 	case KEY_UNSPEC:
 	case KEY_RSA:
 	case KEY_DSA:
+	case KEY_NAME:
+	case KEY_NAME_PAT:
 		space = strchr(cp, ' ');
 		if (space == NULL) {
 			debug3("key_read: no space");
@@ -398,6 +466,19 @@
 		}
 		*space = '\0';
 		type = key_type_from_name(cp);
+		if ((type == KEY_NAME) || (type == KEY_NAME_PAT)) {
+			char * colon = NULL;
+			
+			colon = strchr(cp, ':');
+
+			debug3("key_read: reading named%s key",
+			       (type == KEY_NAME) ? "" : " pattern");
+
+			if (colon != NULL && *(++colon) != '\0') {
+				name_type = xstrdup(colon);
+			} else
+				name_type == NULL;
+		}
 		*space = ' ';
 		if (type == KEY_UNSPEC) {
 			debug3("key_read: no key found");
@@ -410,31 +491,83 @@
 		}
 		if (ret->type == KEY_UNSPEC) {
 			ret->type = type;
+		} else if (ret->type == KEY_NAME && type == KEY_NAME_PAT) {
+			ret->type = type;
 		} else if (ret->type != type) {
 			/* is a key, but different type */
 			debug3("key_read: type mismatch");
 			return -1;
 		}
-		len = 2*strlen(cp);
-		blob = xmalloc(len);
-		n = uudecode(cp, blob, len);
-		if (n < 0) {
-			error("key_read: uudecode %s failed", cp);
-			return -1;
+		if ((ret->type == KEY_NAME) || (ret->type == KEY_NAME_PAT)) {
+			char *p, *p1, ch;
+
+			if (cp == NULL || *cp == '\0')
+				return -1;
+
+			debug3("key_read: reading named key %s", cp);
+			k = key_new(ret->type);
+			/* Skip whitespace */
+			for ( ; (*cp != '\0') && isspace(*cp) && (*cp != '\n') ; cp++ )
+				;
+			if (*cp == '"') {
+				k->name = (unsigned char *) xstrdup(cp+1);
+				p = k->name;
+				for ( p1 = cp+1 ; *p1 != '\0' ; p1++ ) {
+					if (*p1 == '\\')
+						p1++;
+					else if (*p1 == '"')
+						break;
+					*p++ = *p1;
+				}
+				*p = '\0';
+				debug3("key_read: quoted key: %s", k->name);
+			} else {
+				/* Ignore trailing whitespace */
+				for ( p = cp ; *p != '\0' && !isspace(*p) ; p++ )
+					;
+				ch = *p;
+				*p = '\0';
+				k->name = (unsigned char *) xstrdup(cp);
+				*p = ch;
+				debug3("key_read: key: %s", k->name);
+			}
+			k->name_type = name_type;
+		} else {
+			len = 2*strlen(cp);
+			blob = xmalloc(len);
+			n = uudecode(cp, blob, len);
+			if (n < 0) {
+				error("key_read: uudecode %s failed", cp);
+				return -1;
+			}
+			k = key_from_blob(blob, n);
 		}
-		k = key_from_blob(blob, n);
 		if (k == NULL) {
 			error("key_read: key_from_blob %s failed", cp);
 			return -1;
 		}
-		xfree(blob);
+		if (blob != NULL)
+			xfree(blob);
 		if (k->type != type) {
-			error("key_read: type mismatch: encoding error");
-			key_free(k);
-			return -1;
+			if (! ((ret->type == KEY_NAME) &&
+			       type ==  KEY_NAME_PAT)) {
+				error("key_read: type mismatch: encoding error");
+				key_free(k);
+				return -1;
+			}
 		}
 /*XXXX*/
-		if (ret->type == KEY_RSA) {
+		if ((ret->type == KEY_NAME) || (ret->type == KEY_NAME_PAT)) {
+			/*
+			if (ret->name != NULL)
+				xfree(ret->name);
+			 */
+			ret->name = k->name;
+			ret->name_type = k->name_type;
+			k->name = NULL;
+			k->name_type = NULL;
+			success = 1;
+		} else if (ret->type == KEY_RSA) {
 			if (ret->rsa != NULL)
 				RSA_free(ret->rsa);
 			ret->rsa = k->rsa;
@@ -488,7 +621,7 @@
 		}
 	} else if ((key->type == KEY_DSA && key->dsa != NULL) ||
 	    (key->type == KEY_RSA && key->rsa != NULL)) {
-		int len, n;
+		u_int len, n;
 		u_char *blob, *uu;
 		key_to_blob(key, &blob, &len);
 		uu = xmalloc(2*len);
@@ -499,6 +632,12 @@
 		}
 		xfree(blob);
 		xfree(uu);
+	} else if (key->type == KEY_NAME && key->name != NULL) {
+		fprintf(f, "%s ", key_ssh_name(key));
+		if (key->name_type != NULL)
+			fprintf(f, ":%s", key->name_type);
+		else
+			fprintf(f, " \"%s\"", key->name);
 	}
 	return success;
 }
@@ -515,6 +654,12 @@
 	case KEY_DSA:
 		return "DSA";
 		break;
+	case KEY_NAME:
+		return "Named";
+		break;
+	case KEY_NAME_PAT:
+		return "Name_Pattern";
+		break;
 	}
 	return "unknown";
 }
@@ -528,6 +673,12 @@
 	case KEY_DSA:
 		return "ssh-dss";
 		break;
+	case KEY_NAME:
+		return "ssh-named";
+		break;
+	case KEY_NAME_PAT:
+		return "ssh-name-pat";
+		break;
 	}
 	return "ssh-unknown";
 }
@@ -605,6 +756,14 @@
 		BN_copy(n->rsa->n, k->rsa->n);
 		BN_copy(n->rsa->e, k->rsa->e);
 		break;
+	case KEY_NAME:
+	case KEY_NAME_PAT:
+		n = key_new(k->type);
+		n->name = xstrdup(k->name);
+		if (k->name_type) {
+			n->name_type = xstrdup(k->name_type);
+		}
+		break;
 	default:
 		fatal("key_from_private: unknown type %d", k->type);
 		break;
@@ -625,6 +784,26 @@
 		return KEY_RSA;
 	} else if (strcmp(name, "ssh-dss") == 0){
 		return KEY_DSA;
+	} else if (strcmp(name, "ssh-named") == 0){
+		return KEY_NAME;
+	} else if (strncmp(name, "ssh-named:", strlen("ssh-named:")) == 0){
+		return KEY_NAME;
+	} else if (strcmp(name, "ssh-name-pat") == 0){
+		return KEY_NAME_PAT;
+	} else if (strncmp(name, "ssh-name-pat:",
+			   strlen("ssh-name-pat:")) == 0){
+		return KEY_NAME_PAT;
+	/* Backwards compatibility for previous named key entry forms at UBSW */
+	} else if (strcmp(name, "ssh-ext-named") == 0){
+		return KEY_NAME;
+	} else if (strncmp(name, "ssh-ext-named:",
+			   strlen("ssh-ext-named:")) == 0){
+		return KEY_NAME;
+	} else if (strcmp(name, "ssh-ext-name-pat") == 0){
+		return KEY_NAME_PAT;
+	} else if (strncmp(name, "ssh-ext-name-pat:",
+			   strlen("ssh-ext-name-pat:")) == 0){
+		return KEY_NAME_PAT;
 	}
 	debug2("key_type_from_name: unknown key type '%s'", name);
 	return KEY_UNSPEC;
Index: 3_0_2p1.1/auth2.c
--- 3_0_2p1.1/auth2.c Wed, 21 Nov 2001 10:38:46 -0500
+++ 3_0_2p1_w_named_keys.1(w)/auth2.c Thu, 24 Jan 2002 10:52:24 -0500
@@ -76,7 +76,7 @@
 /* helper */
 static Authmethod *authmethod_lookup(const char *);
 static char *authmethods_get(void);
-static int user_key_allowed(struct passwd *, Key *);
+int user_key_allowed(struct passwd *, Key *);
 static int hostbased_key_allowed(struct passwd *, const char *, char *, Key *);
 
 /* auth */
@@ -476,7 +476,7 @@
 			buffer_dump(&b);
 #endif
 			/* test for correct signature */
-			if (user_key_allowed(authctxt->pw, key) &&
+			if (user_key_allowed(authctxt->pw, key) > 0 &&
 			    key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1)
 				authenticated = 1;
 			buffer_clear(&b);
@@ -493,7 +493,7 @@
 			 * if a user is not allowed to login. is this an
 			 * issue? -markus
 			 */
-			if (user_key_allowed(authctxt->pw, key)) {
+			if (user_key_allowed(authctxt->pw, key) > 0) {
 				packet_start(SSH2_MSG_USERAUTH_PK_OK);
 				packet_put_string(pkalg, alen);
 				packet_put_string(pkblob, blen);
@@ -719,24 +719,31 @@
 				continue;
 			}
 		}
-		if (key_equal(found, key) &&
-		    auth_parse_options(pw, options, file, linenum) == 1) {
-			found_key = 1;
-			debug("matching key found: file %s, line %lu",
-			    file, linenum);
-			break;
+		if (key_equal(found, key) || key_match(found, key)) {
+			found_key = auth_parse_options(pw, options, file, linenum);
+			if (found_key != 0)
+				break;
+			auth_clear_options();
 		}
 	}
 	restore_uid();
 	fclose(f);
 	key_free(found);
-	if (!found_key)
+	if (found_key == 0)
 		debug2("key not found");
+	else if (found_key > 0) {
+		debug("matching key found: file %s, line %lu",
+		      file, linenum);
+		auth_set_key_env(key);
+	} else
+		debug("matching deny key found: file %s, line %lu",
+		      file, linenum);
+
 	return found_key;
 }
 
 /* check whether given key is in .ssh/authorized_keys* */
-static int
+int
 user_key_allowed(struct passwd *pw, Key *key)
 {
 	int success;
@@ -745,7 +752,7 @@
 	file = authorized_keys_file(pw);
 	success = user_key_allowed2(pw, key, file);
 	xfree(file);
-	if (success)
+	if (success != 0)
 		return success;
 
 	/* try suffix "2" for backward compat, too */
Index: 3_0_2p1.1/auth-rsa.c
--- 3_0_2p1.1/auth-rsa.c Wed, 21 Nov 2001 10:38:46 -0500
+++ 3_0_2p1_w_named_keys.1(w)/auth-rsa.c Thu, 24 Jan 2002 10:52:24 -0500
@@ -232,7 +232,7 @@
 		 * If our options do not allow this key to be used,
 		 * do not send challenge.
 		 */
-		if (!auth_parse_options(pw, options, file, linenum))
+		if (auth_parse_options(pw, options, file, linenum) < 1)
 			continue;
 
 		/* Perform the challenge-response dialog for this key. */
Index: 3_0_2p1.1/auth-options.h
--- 3_0_2p1.1/auth-options.h Wed, 21 Nov 2001 10:38:46 -0500
+++ 3_0_2p1_w_named_keys.1(w)/auth-options.h Thu, 24 Jan 2002 10:52:24 -0500
@@ -16,6 +16,8 @@
 #ifndef AUTH_OPTIONS_H
 #define AUTH_OPTIONS_H
 
+#include "key.h"
+
 /* Linked list of custom environment strings */
 struct envstring {
 	struct envstring *next;
@@ -31,6 +33,8 @@
 extern struct envstring *custom_environment;
 
 int	auth_parse_options(struct passwd *, char *, char *, u_long);
+void	auth_set_key_env(Key *k);
 void	auth_clear_options(void);
+
 
 #endif
Index: 3_0_2p1.1/auth-options.c
--- 3_0_2p1.1/auth-options.c Wed, 21 Nov 2001 10:38:46 -0500
+++ 3_0_2p1_w_named_keys.1(w)/auth-options.c Thu, 24 Jan 2002 10:52:24 -0500
@@ -56,8 +56,43 @@
 	channel_clear_permitted_opens();
 }
 
+void auth_set_key_env(Key *k)
+{
+	struct envstring *new_env;
+	char *s;
+	int len;
+
+	if (k->type != KEY_NAME)
+		return;
+
+	len = strlen("SSH_AUTH_NAME=");
+	len += strlen(k->name) + 1;
+	s = xmalloc(len);
+	snprintf(s, len, "SSH_AUTH_NAME=%s", k->name);
+	debug3("auth_set_key_env: Adding to the environment: %.*s", len, s);
+	new_env = xmalloc(sizeof(struct envstring));
+	new_env->s = s;
+	new_env->next = custom_environment;
+	custom_environment = new_env;
+
+	if (k->name_type == NULL)
+		return;
+
+	len = strlen("SSH_AUTH_NAME_TYPE=");
+	len += strlen(k->name_type) + 1;
+	s = xmalloc(len);
+	snprintf(s, len, "SSH_AUTH_NAME_TYPE=%s", k->name_type);
+
+	new_env = xmalloc(sizeof(struct envstring));
+	new_env->s = s;
+	new_env->next = custom_environment;
+	custom_environment = new_env;
+
+	return;
+}
+
 /*
- * return 1 if access is granted, 0 if not.
+ * return 1 if access is granted, 0 if not, -1 if access explicitly denied
  * side effect: sets key option flags
  */
 int
@@ -73,6 +108,12 @@
 		return 1;
 
 	while (*opts && *opts != ' ' && *opts != '\t') {
+		cp = "deny-access";
+		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
+			log("Authentication successful, but authorization denied");
+			packet_send_debug("Permission denied");
+			return -1;
+		}
 		cp = "no-port-forwarding";
 		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
 			packet_send_debug("Port forwarding disabled.");
Index: 3_0_2p1.1/auth-krb4.c
--- 3_0_2p1.1/auth-krb4.c Wed, 21 Nov 2001 10:38:46 -0500
+++ 3_0_2p1_w_named_keys.1(w)/auth-krb4.c Thu, 24 Jan 2002 10:52:24 -0500
@@ -40,6 +40,7 @@
 
 #ifdef KRB4
 extern ServerOptions options;
+int user_key_allowed(struct passwd *, Key *);
 
 static int
 krb4_init(void *context)
@@ -220,6 +221,7 @@
 	socklen_t slen;
 	u_int cksum;
 	int r, s;
+	Key k;
 	
 	s = packet_get_connection_in();
 	
@@ -249,12 +251,27 @@
 	    *adat.pinst ? "." : "", adat.pinst, adat.prealm);
 	
 	/* Check ~/.klogin authorization now. */
-	if (kuserok(&adat, authctxt->user) != KSUCCESS) {
-		log("Kerberos v4 .klogin authorization failed for %s to "
-		    "account %s", *client, authctxt->user);
+	k.type = KEY_NAME;
+	k.name = *client;
+	k.name_type = "krb4";
+
+	r = user_key_allowed(authctxt->pw, &k);
+
+	if (r < 0) {
+		log("Kerberos v4 %s authorization failed for %s to "
+		    "account %s", "authorized_keys", *client, authctxt->user);
 		xfree(*client);
 		return (0);
 	}
+
+	if (r == 0 && kuserok(&adat, authctxt->user) != KSUCCESS) {
+		log("Kerberos v4 %s authorization failed for %s to "
+		    "account %s", (r == 0) ? ".klogin" : "authorized_keys",
+		    *client, authctxt->user);
+		xfree(*client);
+		return (0);
+	}
+
 	/* Increment the checksum, and return it encrypted with the
 	   session key. */
 	cksum = adat.checksum + 1;
Index: 3_0_2p1.1/auth-krb5.c
--- 3_0_2p1.1/auth-krb5.c Wed, 21 Nov 2001 10:38:46 -0500
+++ 3_0_2p1_w_named_keys.1(w)/auth-krb5.c Thu, 24 Jan 2002 10:52:24 -0500
@@ -19,6 +19,7 @@
 #include <krb5.h>
 
 extern ServerOptions	 options;
+int user_key_allowed(struct passwd *, Key *);
 
 static int
 krb5_init(void *context)
@@ -52,6 +53,8 @@
 	krb5_principal server;
 	krb5_data reply;
 	krb5_ticket *ticket;
+	Key k;
+	char *client_name;
 	int fd, ret;
 
 	ret = 0;
@@ -95,14 +98,30 @@
 	if (problem)
 		goto err;
 	
+	if (!krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user,
+			&client_name))
+		goto err;
+
 	/* Check .k5login authorization now. */
-	if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user,
-	    authctxt->pw->pw_name))
+	k.type = KEY_NAME;
+	k.name = client_name;
+	k.name_type = "krb5";
+
+	ret = user_key_allowed(authctxt->pw, &k);
+	if (ret < 0) {
+		ret = 0;
 		goto err;
+	}
+	if (ret == 0 && !krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user,
+	    authctxt->pw->pw_name)) {
+		log("SSHv1 Kerberos v5 %s authorization failed for %s to "
+		    "account %s", (ret == 0) ? ".k5login" : "authorized_keys",
+		    *client, authctxt->user);
+		goto err;
+	}
 	
 	if (client)
-		krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user,
-		    client);
+		*client = client_name;
 	
 	packet_start(SSH_SMSG_AUTH_KERBEROS_RESPONSE);
 	packet_put_string((char *) reply.data, reply.length);
-------------- next part --------------
Index: 3_0_2p1_w_gss.1/sshd.8
--- 3_0_2p1_w_gss.1/sshd.8 Thu, 10 Jan 2002 14:22:14 -0500
+++ 3_0_2p1_w_gss_w_named_keys.14(w)/sshd.8 Fri, 18 Jan 2002 14:15:53 -0500
@@ -951,7 +951,8 @@
 is the default file that lists the public keys that are
 permitted for RSA authentication in protocol version 1
 and for public key authentication (PubkeyAuthentication)
-in protocol version 2.
+in protocol version 2. It can also list key names or key patterns
+for external authentication systems, such as krb4, krb5, gsi, etc...
 .Cm AuthorizedKeysFile
 may be used to specify an alternative file.
 .Pp
@@ -974,7 +975,19 @@
 For protocol version 2 the keytype is
 .Dq ssh-dss
 or
-.Dq ssh-rsa .
+.Dq ssh-rsa
+or
+.Dq ssh-named:<keytype>
+or
+.Dq ssh-name-pat:<keytype> .
+.Pp
+Named keys and key name patterns follow the latter two, in double
+quotes if they contain whitespace. Named key types may include:
+.Dq krb4 ,
+.Dq krb5
+and/or
+.Dq gsi ,
+depending on what features are compiled in to OpenSSH.
 .Pp
 Note that lines in this file are usually several hundred bytes long
 (because of the size of the RSA key modulus).
@@ -1036,6 +1049,10 @@
 This option is automatically disabled if
 .Cm UseLogin
 is enabled.
+.It Cm deny-access
+This option ends authorized_keys2 processing if the key matches. This
+option is only really useful with named key and named key pattern
+entries.
 .It Cm no-port-forwarding
 Forbids TCP/IP forwarding when this key is used for authentication.
 Any port forward requests by the client will return an error.
Index: 3_0_2p1_w_gss.1/key.h
--- 3_0_2p1_w_gss.1/key.h Thu, 10 Jan 2002 14:22:14 -0500
+++ 3_0_2p1_w_gss_w_named_keys.14(w)/key.h Fri, 18 Jan 2002 12:24:41 -0500
@@ -35,7 +35,9 @@
 	KEY_RSA,
 	KEY_DSA,
 	KEY_NULL,
-	KEY_UNSPEC
+	KEY_UNSPEC,
+	KEY_NAME,
+	KEY_NAME_PAT
 };
 enum fp_type {
 	SSH_FP_SHA1,
@@ -54,12 +56,15 @@
 	int	 flags;
 	RSA	*rsa;
 	DSA	*dsa;
+	u_char	*name;
+	char	*name_type;
 };
 
 Key	*key_new(int);
 Key	*key_new_private(int);
 void	 key_free(Key *);
 int	 key_equal(Key *, Key *);
+int	 key_match(Key *a, Key *b);
 char	*key_fingerprint(Key *, enum fp_type, enum fp_rep);
 char	*key_type(Key *);
 int	 key_write(Key *, FILE *);
Index: 3_0_2p1_w_gss.1/key.c
--- 3_0_2p1_w_gss.1/key.c Thu, 10 Jan 2002 14:22:14 -0500
+++ 3_0_2p1_w_gss_w_named_keys.14(w)/key.c Fri, 18 Jan 2002 17:21:17 -0500
@@ -57,6 +57,8 @@
 	k->flags = 0;
 	k->dsa = NULL;
 	k->rsa = NULL;
+	k->name = NULL;
+	k->name_type = NULL;
 	switch (k->type) {
 	case KEY_RSA1:
 	case KEY_RSA:
@@ -73,6 +75,8 @@
 		dsa->pub_key = BN_new();
 		k->dsa = dsa;
 		break;
+	case KEY_NAME:
+	case KEY_NAME_PAT:
 	case KEY_UNSPEC:
 		break;
 	default:
@@ -120,6 +124,13 @@
 			DSA_free(k->dsa);
 		k->dsa = NULL;
 		break;
+	case KEY_NAME:
+	case KEY_NAME_PAT:
+		if (k->name != NULL)
+			xfree(k->name);
+		if (k->name_type != NULL)
+			xfree(k->name_type);
+		break;
 	case KEY_UNSPEC:
 		break;
 	default:
@@ -131,8 +142,9 @@
 int
 key_equal(Key *a, Key *b)
 {
-	if (a == NULL || b == NULL || a->type != b->type)
+	if (a == NULL || b == NULL || a->type != b->type) {
 		return 0;
+	}
 	switch (a->type) {
 	case KEY_RSA1:
 	case KEY_RSA:
@@ -147,12 +159,65 @@
 		    BN_cmp(a->dsa->g, b->dsa->g) == 0 &&
 		    BN_cmp(a->dsa->pub_key, b->dsa->pub_key) == 0;
 		break;
+	case KEY_NAME:
+		if ((a->name_type == NULL && b->name_type == NULL) ||
+		    (a->name_type == b->name_type))
+			return (strcmp(a->name, b->name) == 0);
+		if (a->name_type == NULL || b->name_type == NULL)
+			return 0;
+		if (strcmp(a->name_type, b->name_type) == 0)
+			return (strcmp(a->name, b->name) == 0);
+		break;
+	case KEY_NAME_PAT:
+		return 0;
+		break;
 	default:
 		fatal("key_equal: bad key type %d", a->type);
 		break;
 	}
 	return 0;
 }
+int
+key_match(Key *a, Key *b)
+{
+	debug3("key_match: trying to match %x and %x", a, b);
+	if (a == NULL || b == NULL)
+		return 0;
+
+	debug3("key_match: trying to match key types %d and %d -- KEY_NAME_PAT == %d", a->type, b->type, KEY_NAME_PAT);
+	/* One key must be a name pattern, the other must be a name */
+	if (!(a->type == KEY_NAME_PAT && b->type == KEY_NAME) &&
+	    !(b->type == KEY_NAME_PAT && a->type == KEY_NAME))
+		return 0;
+	
+	/* Both keys must have name types, or both must not */
+	/* or one key must have '*' as its name type        */
+	if ((a->name_type == NULL && b->name_type != NULL) ||
+	    (b->name_type == NULL && a->name_type != NULL)) {
+
+		debug3("key_match: foo");
+		if (a->name_type != NULL && *(a->name_type) != '*')
+			return 0;
+		if (b->name_type != NULL && *(b->name_type) != '*')
+			return 0;
+	}
+
+	/* Name type "*" matches any name type */
+	/* Otherwise name types must match */
+	if ((a->name_type != NULL && strcmp(a->name_type, b->name_type) != 0) &&
+	    (*(a->name_type) != '*' || *(b->name_type) != '*')) {
+		debug3("key_match: a->name_type == %s", a->name_type ? a->name_type : "");
+		debug3("key_match: b->name_type == %s", b->name_type ? b->name_type : "");
+		return 0;
+	    }
+
+	debug3("key_match: trying to match %s WITH %s", a->name, b->name);
+	if (a->type == KEY_NAME_PAT)
+		return match_pattern(b->name, a->name);
+	else
+		return match_pattern(a->name, b->name);
+}
+
 
 static u_char*
 key_fingerprint_raw(Key *k, enum fp_type dgst_type, size_t *dgst_raw_length)
@@ -161,7 +226,7 @@
 	EVP_MD_CTX ctx;
 	u_char *blob = NULL;
 	u_char *retval = NULL;
-	int len = 0;
+	u_int len = 0;
 	int nlen, elen;
 
 	*dgst_raw_length = 0;
@@ -364,11 +429,12 @@
 {
 	Key *k;
 	int success = -1;
-	char *cp, *space;
+	char *cp, *space, *name_type;
 	int len, n, type;
 	u_int bits;
-	u_char *blob;
+	u_char *blob = NULL;
 
+	name_type = NULL;
 	cp = *cpp;
 
 	switch(ret->type) {
@@ -391,6 +457,8 @@
 	case KEY_UNSPEC:
 	case KEY_RSA:
 	case KEY_DSA:
+	case KEY_NAME:
+	case KEY_NAME_PAT:
 		space = strchr(cp, ' ');
 		if (space == NULL) {
 			debug3("key_read: no space");
@@ -398,6 +466,19 @@
 		}
 		*space = '\0';
 		type = key_type_from_name(cp);
+		if ((type == KEY_NAME) || (type == KEY_NAME_PAT)) {
+			char * colon = NULL;
+			
+			colon = strchr(cp, ':');
+
+			debug3("key_read: reading named%s key",
+			       (type == KEY_NAME) ? "" : " pattern");
+
+			if (colon != NULL && *(++colon) != '\0') {
+				name_type = xstrdup(colon);
+			} else
+				name_type == NULL;
+		}
 		*space = ' ';
 		if (type == KEY_UNSPEC) {
 			debug3("key_read: no key found");
@@ -410,31 +491,83 @@
 		}
 		if (ret->type == KEY_UNSPEC) {
 			ret->type = type;
+		} else if (ret->type == KEY_NAME && type == KEY_NAME_PAT) {
+			ret->type = type;
 		} else if (ret->type != type) {
 			/* is a key, but different type */
 			debug3("key_read: type mismatch");
 			return -1;
 		}
-		len = 2*strlen(cp);
-		blob = xmalloc(len);
-		n = uudecode(cp, blob, len);
-		if (n < 0) {
-			error("key_read: uudecode %s failed", cp);
-			return -1;
+		if ((ret->type == KEY_NAME) || (ret->type == KEY_NAME_PAT)) {
+			char *p, *p1, ch;
+
+			if (cp == NULL || *cp == '\0')
+				return -1;
+
+			debug3("key_read: reading named key %s", cp);
+			k = key_new(ret->type);
+			/* Skip whitespace */
+			for ( ; (*cp != '\0') && isspace(*cp) && (*cp != '\n') ; cp++ )
+				;
+			if (*cp == '"') {
+				k->name = (unsigned char *) xstrdup(cp+1);
+				p = k->name;
+				for ( p1 = cp+1 ; *p1 != '\0' ; p1++ ) {
+					if (*p1 == '\\')
+						p1++;
+					else if (*p1 == '"')
+						break;
+					*p++ = *p1;
+				}
+				*p = '\0';
+				debug3("key_read: quoted key: %s", k->name);
+			} else {
+				/* Ignore trailing whitespace */
+				for ( p = cp ; *p != '\0' && !isspace(*p) ; p++ )
+					;
+				ch = *p;
+				*p = '\0';
+				k->name = (unsigned char *) xstrdup(cp);
+				*p = ch;
+				debug3("key_read: key: %s", k->name);
+			}
+			k->name_type = name_type;
+		} else {
+			len = 2*strlen(cp);
+			blob = xmalloc(len);
+			n = uudecode(cp, blob, len);
+			if (n < 0) {
+				error("key_read: uudecode %s failed", cp);
+				return -1;
+			}
+			k = key_from_blob(blob, n);
 		}
-		k = key_from_blob(blob, n);
 		if (k == NULL) {
 			error("key_read: key_from_blob %s failed", cp);
 			return -1;
 		}
-		xfree(blob);
+		if (blob != NULL)
+			xfree(blob);
 		if (k->type != type) {
-			error("key_read: type mismatch: encoding error");
-			key_free(k);
-			return -1;
+			if (! ((ret->type == KEY_NAME) &&
+			       type ==  KEY_NAME_PAT)) {
+				error("key_read: type mismatch: encoding error");
+				key_free(k);
+				return -1;
+			}
 		}
 /*XXXX*/
-		if (ret->type == KEY_RSA) {
+		if ((ret->type == KEY_NAME) || (ret->type == KEY_NAME_PAT)) {
+			/*
+			if (ret->name != NULL)
+				xfree(ret->name);
+			 */
+			ret->name = k->name;
+			ret->name_type = k->name_type;
+			k->name = NULL;
+			k->name_type = NULL;
+			success = 1;
+		} else if (ret->type == KEY_RSA) {
 			if (ret->rsa != NULL)
 				RSA_free(ret->rsa);
 			ret->rsa = k->rsa;
@@ -488,7 +621,7 @@
 		}
 	} else if ((key->type == KEY_DSA && key->dsa != NULL) ||
 	    (key->type == KEY_RSA && key->rsa != NULL)) {
-		int len, n;
+		u_int len, n;
 		u_char *blob, *uu;
 		key_to_blob(key, &blob, &len);
 		uu = xmalloc(2*len);
@@ -499,6 +632,12 @@
 		}
 		xfree(blob);
 		xfree(uu);
+	} else if (key->type == KEY_NAME && key->name != NULL) {
+		fprintf(f, "%s ", key_ssh_name(key));
+		if (key->name_type != NULL)
+			fprintf(f, ":%s", key->name_type);
+		else
+			fprintf(f, " \"%s\"", key->name);
 	}
 	return success;
 }
@@ -515,6 +654,12 @@
 	case KEY_DSA:
 		return "DSA";
 		break;
+	case KEY_NAME:
+		return "Named";
+		break;
+	case KEY_NAME_PAT:
+		return "Name_Pattern";
+		break;
 	}
 	return "unknown";
 }
@@ -528,6 +673,12 @@
 	case KEY_DSA:
 		return "ssh-dss";
 		break;
+	case KEY_NAME:
+		return "ssh-named";
+		break;
+	case KEY_NAME_PAT:
+		return "ssh-name-pat";
+		break;
 	}
 	return "ssh-unknown";
 }
@@ -605,6 +756,14 @@
 		BN_copy(n->rsa->n, k->rsa->n);
 		BN_copy(n->rsa->e, k->rsa->e);
 		break;
+	case KEY_NAME:
+	case KEY_NAME_PAT:
+		n = key_new(k->type);
+		n->name = xstrdup(k->name);
+		if (k->name_type) {
+			n->name_type = xstrdup(k->name_type);
+		}
+		break;
 	default:
 		fatal("key_from_private: unknown type %d", k->type);
 		break;
@@ -625,6 +784,26 @@
 		return KEY_RSA;
 	} else if (strcmp(name, "ssh-dss") == 0){
 		return KEY_DSA;
+	} else if (strcmp(name, "ssh-named") == 0){
+		return KEY_NAME;
+	} else if (strncmp(name, "ssh-named:", strlen("ssh-named:")) == 0){
+		return KEY_NAME;
+	} else if (strcmp(name, "ssh-name-pat") == 0){
+		return KEY_NAME_PAT;
+	} else if (strncmp(name, "ssh-name-pat:",
+			   strlen("ssh-name-pat:")) == 0){
+		return KEY_NAME_PAT;
+	/* Backwards compatibility for previous named key entry forms at UBSW */
+	} else if (strcmp(name, "ssh-ext-named") == 0){
+		return KEY_NAME;
+	} else if (strncmp(name, "ssh-ext-named:",
+			   strlen("ssh-ext-named:")) == 0){
+		return KEY_NAME;
+	} else if (strcmp(name, "ssh-ext-name-pat") == 0){
+		return KEY_NAME_PAT;
+	} else if (strncmp(name, "ssh-ext-name-pat:",
+			   strlen("ssh-ext-name-pat:")) == 0){
+		return KEY_NAME_PAT;
 	} else if (strcmp(name, "null") == 0){
 		return KEY_NULL;
 	}
Index: 3_0_2p1_w_gss.1/auth2.c
--- 3_0_2p1_w_gss.1/auth2.c Thu, 10 Jan 2002 14:22:14 -0500
+++ 3_0_2p1_w_gss_w_named_keys.14(w)/auth2.c Fri, 18 Jan 2002 12:24:41 -0500
@@ -80,7 +80,7 @@
 /* helper */
 static Authmethod *authmethod_lookup(const char *);
 static char *authmethods_get(void);
-static int user_key_allowed(struct passwd *, Key *);
+int user_key_allowed(struct passwd *, Key *);
 static int hostbased_key_allowed(struct passwd *, const char *, char *, Key *);
 
 /* auth */
@@ -499,7 +499,7 @@
 			buffer_dump(&b);
 #endif
 			/* test for correct signature */
-			if (user_key_allowed(authctxt->pw, key) &&
+			if (user_key_allowed(authctxt->pw, key) > 0 &&
 			    key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1)
 				authenticated = 1;
 			buffer_clear(&b);
@@ -516,7 +516,7 @@
 			 * if a user is not allowed to login. is this an
 			 * issue? -markus
 			 */
-			if (user_key_allowed(authctxt->pw, key)) {
+			if (user_key_allowed(authctxt->pw, key) > 0) {
 				packet_start(SSH2_MSG_USERAUTH_PK_OK);
 				packet_put_string(pkalg, alen);
 				packet_put_string(pkblob, blen);
@@ -742,24 +742,31 @@
 				continue;
 			}
 		}
-		if (key_equal(found, key) &&
-		    auth_parse_options(pw, options, file, linenum) == 1) {
-			found_key = 1;
-			debug("matching key found: file %s, line %lu",
-			    file, linenum);
-			break;
+		if (key_equal(found, key) || key_match(found, key)) {
+			found_key = auth_parse_options(pw, options, file, linenum);
+			if (found_key != 0)
+				break;
+			auth_clear_options();
 		}
 	}
 	restore_uid();
 	fclose(f);
 	key_free(found);
-	if (!found_key)
+	if (found_key == 0)
 		debug2("key not found");
+	else if (found_key > 0) {
+		debug("matching key found: file %s, line %lu",
+		      file, linenum);
+		auth_set_key_env(key);
+	} else
+		debug("matching deny key found: file %s, line %lu",
+		      file, linenum);
+
 	return found_key;
 }
 
 /* check whether given key is in .ssh/authorized_keys* */
-static int
+int
 user_key_allowed(struct passwd *pw, Key *key)
 {
 	int success;
@@ -768,7 +775,7 @@
 	file = authorized_keys_file(pw);
 	success = user_key_allowed2(pw, key, file);
 	xfree(file);
-	if (success)
+	if (success != 0)
 		return success;
 
 	/* try suffix "2" for backward compat, too */
Index: 3_0_2p1_w_gss.1/auth-rsa.c
--- 3_0_2p1_w_gss.1/auth-rsa.c Wed, 21 Nov 2001 10:38:46 -0500
+++ 3_0_2p1_w_gss_w_named_keys.14(w)/auth-rsa.c Fri, 18 Jan 2002 12:24:41 -0500
@@ -232,7 +232,7 @@
 		 * If our options do not allow this key to be used,
 		 * do not send challenge.
 		 */
-		if (!auth_parse_options(pw, options, file, linenum))
+		if (auth_parse_options(pw, options, file, linenum) < 1)
 			continue;
 
 		/* Perform the challenge-response dialog for this key. */
Index: 3_0_2p1_w_gss.1/auth-options.h
--- 3_0_2p1_w_gss.1/auth-options.h Wed, 21 Nov 2001 10:38:46 -0500
+++ 3_0_2p1_w_gss_w_named_keys.14(w)/auth-options.h Fri, 18 Jan 2002 12:24:41 -0500
@@ -16,6 +16,8 @@
 #ifndef AUTH_OPTIONS_H
 #define AUTH_OPTIONS_H
 
+#include "key.h"
+
 /* Linked list of custom environment strings */
 struct envstring {
 	struct envstring *next;
@@ -31,6 +33,8 @@
 extern struct envstring *custom_environment;
 
 int	auth_parse_options(struct passwd *, char *, char *, u_long);
+void	auth_set_key_env(Key *k);
 void	auth_clear_options(void);
+
 
 #endif
Index: 3_0_2p1_w_gss.1/auth-options.c
--- 3_0_2p1_w_gss.1/auth-options.c Wed, 21 Nov 2001 10:38:46 -0500
+++ 3_0_2p1_w_gss_w_named_keys.14(w)/auth-options.c Fri, 18 Jan 2002 12:24:41 -0500
@@ -56,8 +56,43 @@
 	channel_clear_permitted_opens();
 }
 
+void auth_set_key_env(Key *k)
+{
+	struct envstring *new_env;
+	char *s;
+	int len;
+
+	if (k->type != KEY_NAME)
+		return;
+
+	len = strlen("SSH_AUTH_NAME=");
+	len += strlen(k->name) + 1;
+	s = xmalloc(len);
+	snprintf(s, len, "SSH_AUTH_NAME=%s", k->name);
+	debug3("auth_set_key_env: Adding to the environment: %.*s", len, s);
+	new_env = xmalloc(sizeof(struct envstring));
+	new_env->s = s;
+	new_env->next = custom_environment;
+	custom_environment = new_env;
+
+	if (k->name_type == NULL)
+		return;
+
+	len = strlen("SSH_AUTH_NAME_TYPE=");
+	len += strlen(k->name_type) + 1;
+	s = xmalloc(len);
+	snprintf(s, len, "SSH_AUTH_NAME_TYPE=%s", k->name_type);
+
+	new_env = xmalloc(sizeof(struct envstring));
+	new_env->s = s;
+	new_env->next = custom_environment;
+	custom_environment = new_env;
+
+	return;
+}
+
 /*
- * return 1 if access is granted, 0 if not.
+ * return 1 if access is granted, 0 if not, -1 if access explicitly denied
  * side effect: sets key option flags
  */
 int
@@ -73,6 +108,12 @@
 		return 1;
 
 	while (*opts && *opts != ' ' && *opts != '\t') {
+		cp = "deny-access";
+		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
+			log("Authentication successful, but authorization denied");
+			packet_send_debug("Permission denied");
+			return -1;
+		}
 		cp = "no-port-forwarding";
 		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
 			packet_send_debug("Port forwarding disabled.");
Index: 3_0_2p1_w_gss.1/auth-krb4.c
--- 3_0_2p1_w_gss.1/auth-krb4.c Wed, 21 Nov 2001 10:38:46 -0500
+++ 3_0_2p1_w_gss_w_named_keys.14(w)/auth-krb4.c Fri, 18 Jan 2002 14:44:13 -0500
@@ -40,6 +40,7 @@
 
 #ifdef KRB4
 extern ServerOptions options;
+int user_key_allowed(struct passwd *, Key *);
 
 static int
 krb4_init(void *context)
@@ -220,6 +221,7 @@
 	socklen_t slen;
 	u_int cksum;
 	int r, s;
+	Key k;
 	
 	s = packet_get_connection_in();
 	
@@ -249,12 +251,27 @@
 	    *adat.pinst ? "." : "", adat.pinst, adat.prealm);
 	
 	/* Check ~/.klogin authorization now. */
-	if (kuserok(&adat, authctxt->user) != KSUCCESS) {
-		log("Kerberos v4 .klogin authorization failed for %s to "
-		    "account %s", *client, authctxt->user);
+	k.type = KEY_NAME;
+	k.name = *client;
+	k.name_type = "krb4";
+
+	r = user_key_allowed(authctxt->pw, &k);
+
+	if (r < 0) {
+		log("Kerberos v4 %s authorization failed for %s to "
+		    "account %s", "authorized_keys", *client, authctxt->user);
 		xfree(*client);
 		return (0);
 	}
+
+	if (r == 0 && kuserok(&adat, authctxt->user) != KSUCCESS) {
+		log("Kerberos v4 %s authorization failed for %s to "
+		    "account %s", (r == 0) ? ".klogin" : "authorized_keys",
+		    *client, authctxt->user);
+		xfree(*client);
+		return (0);
+	}
+
 	/* Increment the checksum, and return it encrypted with the
 	   session key. */
 	cksum = adat.checksum + 1;
Index: 3_0_2p1_w_gss.1/auth-krb5.c
--- 3_0_2p1_w_gss.1/auth-krb5.c Thu, 10 Jan 2002 14:22:14 -0500
+++ 3_0_2p1_w_gss_w_named_keys.14(w)/auth-krb5.c Fri, 18 Jan 2002 14:44:54 -0500
@@ -22,6 +22,7 @@
 #endif /* !HEIMDAL */
 
 extern ServerOptions	 options;
+int user_key_allowed(struct passwd *, Key *);
 
 static int
 krb5_init(void *context)
@@ -55,6 +56,8 @@
 	krb5_principal server;
 	krb5_data reply;
 	krb5_ticket *ticket;
+	Key k;
+	char *client_name;
 	int fd, ret;
 
 	ret = 0;
@@ -110,14 +113,30 @@
 	if (problem)
 		goto err;
 	
+	if (!krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user,
+			&client_name))
+		goto err;
+
 	/* Check .k5login authorization now. */
-	if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user,
-	    authctxt->pw->pw_name))
+	k.type = KEY_NAME;
+	k.name = client_name;
+	k.name_type = "krb5";
+
+	ret = user_key_allowed(authctxt->pw, &k);
+	if (ret < 0) {
+		ret = 0;
 		goto err;
+	}
+	if (ret == 0 && !krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user,
+	    authctxt->pw->pw_name)) {
+		log("SSHv1 Kerberos v5 %s authorization failed for %s to "
+		    "account %s", (ret == 0) ? ".k5login" : "authorized_keys",
+		    *client, authctxt->user);
+		goto err;
+	}
 	
 	if (client)
-		krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user,
-		    client);
+		*client = client_name;
 	
 	packet_start(SSH_SMSG_AUTH_KERBEROS_RESPONSE);
 	packet_put_string((char *) reply.data, reply.length);
Index: 3_0_2p1_w_gss.1/gss-serv.c
--- 3_0_2p1_w_gss.1/gss-serv.c Thu, 10 Jan 2002 15:57:24 -0500
+++ 3_0_2p1_w_gss_w_named_keys.14(w)/gss-serv.c Fri, 18 Jan 2002 12:24:41 -0500
@@ -48,6 +48,7 @@
 extern ServerOptions options;
 extern u_char *session_id2;
 extern int session_id2_len;
+int user_key_allowed(struct passwd *, Key *);
 
 
 typedef struct ssh_gssapi_cred_cache {
@@ -98,24 +99,39 @@
 ssh_gssapi_krb5_userok(char *name) {
 	krb5_principal princ;
 	int retval;
+	char *by;
+	Key k;
 
 	if (ssh_gssapi_krb5_init() == 0)
 		return 0;
-		
+
+	k.type = KEY_NAME;
+	k.name = gssapi_client_name.value;
+	k.name_type = "krb5";
+
 	if ((retval=krb5_parse_name(krb_context, gssapi_client_name.value, 
 				    &princ))) {
 		log("krb5_parse_name(): %.100s", 
 			krb5_get_err_text(krb_context,retval));
 		return 0;
 	}
-	if (krb5_kuserok(krb_context, princ, name)) {
+
+	/* Try authorized_keys first */
+	by = "authorized_keys";
+	retval = user_key_allowed(getpwnam(name), &k);
+	if (retval < 0) {
+		debug("ssh_gssapi_krb5_userok: access denied in %s", by);
+		krb5_free_principal(krb_context, princ);
+		return 0;
+	}
+	if (retval == 0 && krb5_kuserok(krb_context, princ, name)) {
+		by = "krb5_kuserok";
 		retval = 1;
-		log("Authorized to %s, krb5 principal %s (krb5_kuserok)",name,
-		    (char *)gssapi_client_name.value);
 	}
-	else
-		retval = 0;
 	
+	notice("Authorized to %s, krb5 principal %s (%s)", name,
+	       (char *)gssapi_client_name.value, by);
+
 	krb5_free_principal(krb_context, princ);
 	return retval;
 }
-------------- next part --------------

Visit our website at http://www.ubswarburg.com

This message contains confidential information and is intended only 
for the individual named.  If you are not the named addressee you 
should not disseminate, distribute or copy this e-mail.  Please 
notify the sender immediately by e-mail if you have received this 
e-mail by mistake and delete this e-mail from your system.

E-mail transmission cannot be guaranteed to be secure or error-free 
as information could be intercepted, corrupted, lost, destroyed, 
arrive late or incomplete, or contain viruses.  The sender therefore 
does not accept liability for any errors or omissions in the contents 
of this message which arise as a result of e-mail transmission.  If 
verification is required please request a hard-copy version.  This 
message is provided for informational purposes and should not be 
construed as a solicitation or offer to buy or sell any securities or 
related financial instruments.


More information about the openssh-unix-dev mailing list