Index: auth.c
===================================================================
RCS file: /cvs/openssh/auth.c,v
retrieving revision 1.149
diff -u -r1.149 auth.c
--- auth.c	29 May 2011 11:40:42 -0000	1.149
+++ auth.c	20 Aug 2011 19:39:14 -0000
@@ -362,6 +362,12 @@
 	return expand_authorized_keys(options.authorized_principals_file, pw);
 }
 
+char *
+authorized_credentials_file(struct passwd *pw)
+{
+	return expand_authorized_keys(options.authorized_credentials_file, pw);
+}
+
 /* return ok if key exists in sysfile or userfile */
 HostStatus
 check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host,
@@ -532,6 +538,13 @@
 {
 	return auth_openfile(file, pw, strict_modes, 0,
 	    "authorized principals");
+}
+
+FILE *
+auth_opencredentialfile(const char *file, struct passwd *pw, int strict_modes)
+{
+	return auth_openfile(file, pw, strict_modes, 0,
+	    "authorized credentials");
 }
 
 struct passwd *
Index: auth.h
===================================================================
RCS file: /cvs/openssh/auth.h,v
retrieving revision 1.87
diff -u -r1.87 auth.h
--- auth.h	29 May 2011 11:39:38 -0000	1.87
+++ auth.h	20 Aug 2011 19:39:14 -0000
@@ -170,9 +170,11 @@
 
 char	*expand_authorized_keys(const char *, struct passwd *pw);
 char	*authorized_principals_file(struct passwd *);
+char	*authorized_credentials_file(struct passwd *);
 
 FILE	*auth_openkeyfile(const char *, struct passwd *, int);
 FILE	*auth_openprincipals(const char *, struct passwd *, int);
+FILE	*auth_opencredentialfile(const char *, struct passwd *, int);
 int	 auth_key_is_revoked(Key *);
 
 HostStatus
Index: auth2-gss.c
===================================================================
RCS file: /cvs/openssh/auth2-gss.c,v
retrieving revision 1.20
diff -u -r1.20 auth2-gss.c
--- auth2-gss.c	5 May 2011 04:04:11 -0000	1.20
+++ auth2-gss.c	20 Aug 2011 19:39:14 -0000
@@ -42,6 +42,7 @@
 #include "buffer.h"
 #include "servconf.h"
 #include "packet.h"
+#include "auth-options.h"
 #include "ssh-gss.h"
 #include "monitor_wrap.h"
 
@@ -244,7 +245,9 @@
 
 	packet_check_eom();
 
-	authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
+	authenticated = PRIVSEP(ssh_gssapi_userok(authctxt));
+	if (authenticated != 1)
+		auth_clear_options();
 
 	authctxt->postponed = 0;
 	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
@@ -278,9 +281,11 @@
 	gssbuf.value = buffer_ptr(&b);
 	gssbuf.length = buffer_len(&b);
 
-	if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))))
-		authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
-	else
+	if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) {
+		authenticated = PRIVSEP(ssh_gssapi_userok(authctxt));
+		if (authenticated != 1)
+			auth_clear_options();
+	} else
 		logit("GSSAPI MIC check failed");
 
 	buffer_free(&b);
Index: gss-serv.c
===================================================================
RCS file: /cvs/openssh/gss-serv.c,v
retrieving revision 1.25
diff -u -r1.25 gss-serv.c
--- gss-serv.c	5 Aug 2011 20:16:46 -0000	1.25
+++ gss-serv.c	20 Aug 2011 19:39:14 -0000
@@ -34,13 +34,19 @@
 #include <stdarg.h>
 #include <string.h>
 #include <unistd.h>
+#include <krb5.h>
 
 #include "openbsd-compat/sys-queue.h"
 #include "xmalloc.h"
+#include "ssh.h"
 #include "buffer.h"
+#include "log.h"
+#include "servconf.h"
 #include "key.h"
 #include "hostfile.h"
 #include "auth.h"
+#include "uidswap.h"
+#include "auth-options.h"
 #include "log.h"
 #include "channels.h"
 #include "session.h"
@@ -48,6 +54,8 @@
 
 #include "ssh-gss.h"
 
+extern ServerOptions options;
+
 static ssh_gssapi_client gssapi_client =
     { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER,
     GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL}};
@@ -327,10 +335,160 @@
 	}
 }
 
+static int
+credential_read(krb5_context context, krb5_principal *princ, char **cpp)
+{
+	char *cp;
+	char *name;
+	size_t len;
+	krb5_error_code retval;
+	cp = *cpp;
+
+	len = strcspn(cp, " \t\n#");
+	if (len == 0)
+		return 0;
+	name = strndup(cp, len);
+	if (name == NULL)
+		return 0;
+	logit("cred \"%s\"", name);
+	retval = krb5_parse_name_flags(context, name,
+			       KRB5_PRINCIPAL_PARSE_MUST_REALM, princ);
+	if (retval) {
+		debug("krb5_parse_name(\"%s\"): %.100s",
+			name, krb5_get_err_text(context, retval));
+		free(name);
+		return 0;
+	}
+	*cpp += len;
+
+	free(name);
+	return 1;
+}
+
+static int
+user_credential_allowed(Authctxt *authctxt, char *cred)
+{
+	char _line[SSH_MAX_PUBKEY_BYTES];
+	krb5_context context = NULL;
+	krb5_principal sprinc = NULL, aprinc = NULL;
+	krb5_error_code retval;
+	char *file;
+	FILE *f;
+	struct passwd *pw = authctxt->pw;
+	int found_credential = 0;
+	u_long linenum = 0;
+	char *line = NULL;
+	const char *sp_name = NULL, *sp_inst = NULL, *sp_realm = NULL;
+
+	/* Temporarily use the user's uid. */
+	temporarily_use_uid(pw);
+
+	retval = krb5_init_context(&context);
+	if (retval) {
+		logit("%s: krb5_init_context(): %.100s", __func__,
+			krb5_get_err_text(context, retval));
+		restore_uid();
+		return 0;
+	}
+
+	/* Session Principal */
+	retval = krb5_parse_name_flags(context, cred,
+			       KRB5_PRINCIPAL_PARSE_MUST_REALM, &sprinc);
+	if (retval) {
+		logit("%s: krb5_parse_name(\"%s\"): %.100s", __func__,
+			cred, krb5_get_err_text(context, retval));
+		krb5_free_context(context);
+		restore_uid();
+		return 0;
+	}
+	sp_name = krb5_principal_get_comp_string(context, sprinc, 0);
+	sp_inst = krb5_principal_get_comp_string(context, sprinc, 1);
+	sp_realm = krb5_principal_get_realm(context, sprinc);
+
+	logit("%s: %s/%s@%s", __func__, sp_name, sp_inst, sp_realm);
+
+	file = authorized_credentials_file(pw);
+	        
+	debug("trying allowed credentials file %s", file);
+	f = auth_opencredentialfile(file, pw, options.strict_modes);
+	if (!f) {
+		krb5_free_principal(context, sprinc);
+		krb5_free_context(context);
+		restore_uid();
+		return 0;
+	}
+
+	found_credential = 0;
+	while (read_keyfile_line(f, file, _line, sizeof(_line), &linenum) != -1) {
+		char *cp, *cred_options = NULL;
+		size_t len;
+		if (line)
+			free(line);
+		line = percent_expand(_line,
+			"c", cred,
+			"h", pw->pw_dir,
+			"u", pw->pw_name,
+			"n", sp_name ? sp_name : "NONAME",
+			"i", sp_inst ? sp_inst : "NOINST",
+			"r", sp_realm ? sp_realm : "NOREALM",
+			(char *)NULL);
+
+		debug("original line: %s", _line);
+		debug("expanded line: %s", line);
+
+		auth_clear_options();
+
+		/* Skip leading whitespace, empty and comment lines. */
+		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
+			;
+		if (!*cp || *cp == '\n' || *cp == '#')
+			continue;
+
+		len = strcspn(cp, " \t\n#");
+		if (cp[len] != '\0') {
+			cred_options = cp;
+			cp+= len;
+                        /* Skip remaining whitespace. */
+                        for (; *cp == ' ' || *cp == '\t'; cp++)
+                                ;
+		}
+
+		if (credential_read(context, &aprinc, &cp) != 1) {
+			debug2("%s: advance: '%s'", __func__,  cp);
+			continue;
+		}
+		debug("%d: options \"%.*s\"", linenum, len, cred_options);
+
+                if (auth_parse_options(pw, cred_options, file, linenum) != 1)
+                        continue;
+
+		if (krb5_principal_match(context, sprinc, aprinc)) {
+                        found_credential = 1;
+                        debug("matching credential found: file %s, line %lu",
+                            file, linenum);
+                        break;
+                }
+	}
+	if (sprinc)
+		krb5_free_principal(context, sprinc);
+	if (aprinc)
+		krb5_free_principal(context, aprinc);
+	if (context)
+		krb5_free_context(context);
+	restore_uid();
+	fclose(f);
+	if (line)
+		free(line);
+	if (!found_credential)
+		debug2("credential not found");
+	return found_credential;
+}
+
 /* Privileged */
 int
-ssh_gssapi_userok(char *user)
+ssh_gssapi_userok(Authctxt *authctxt)
 {
+	char *user = authctxt->user;
 	OM_uint32 lmin;
 
 	if (gssapi_client.exportedname.length == 0 ||
@@ -339,7 +497,9 @@
 		return 0;
 	}
 	if (gssapi_client.mech && gssapi_client.mech->userok)
-		if ((*gssapi_client.mech->userok)(&gssapi_client, user))
+		if (user_credential_allowed(authctxt,
+			(char *)gssapi_client.displayname.value) ||
+		    (*gssapi_client.mech->userok)(&gssapi_client, user))
 			return 1;
 		else {
 			/* Destroy delegated credentials if userok fails */
Index: monitor.c
===================================================================
RCS file: /cvs/openssh/monitor.c,v
retrieving revision 1.150
diff -u -r1.150 monitor.c
--- monitor.c	5 Aug 2011 20:15:18 -0000	1.150
+++ monitor.c	20 Aug 2011 19:39:14 -0000
@@ -2089,7 +2089,7 @@
 {
 	int authenticated;
 
-	authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user);
+	authenticated = authctxt->valid && ssh_gssapi_userok(authctxt);
 
 	buffer_clear(m);
 	buffer_put_int(m, authenticated);
Index: monitor_wrap.c
===================================================================
RCS file: /cvs/openssh/monitor_wrap.c,v
retrieving revision 1.89
diff -u -r1.89 monitor_wrap.c
--- monitor_wrap.c	20 Jun 2011 04:42:23 -0000	1.89
+++ monitor_wrap.c	20 Aug 2011 19:39:15 -0000
@@ -1270,7 +1270,7 @@
 }
 
 int
-mm_ssh_gssapi_userok(char *user)
+mm_ssh_gssapi_userok(Authctxt *authctxt)
 {
 	Buffer m;
 	int authenticated = 0;
Index: monitor_wrap.h
===================================================================
RCS file: /cvs/openssh/monitor_wrap.h,v
retrieving revision 1.30
diff -u -r1.30 monitor_wrap.h
--- monitor_wrap.h	20 Jun 2011 04:42:23 -0000	1.30
+++ monitor_wrap.h	20 Aug 2011 19:39:15 -0000
@@ -58,7 +58,7 @@
 OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
 OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *,
    gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *);
-int mm_ssh_gssapi_userok(char *user);
+int mm_ssh_gssapi_userok(struct Authctxt *);
 OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
 #endif
 
Index: pathnames.h
===================================================================
RCS file: /cvs/openssh/pathnames.h,v
retrieving revision 1.28
diff -u -r1.28 pathnames.h
--- pathnames.h	29 May 2011 11:39:38 -0000	1.28
+++ pathnames.h	20 Aug 2011 19:39:15 -0000
@@ -100,6 +100,12 @@
 #define _PATH_SSH_USER_PERMITTED_KEYS2	".ssh/authorized_keys2"
 
 /*
+ * As above but containing a one per line list of credentials with
+ * options.
+ */
+#define _PATH_SSH_USER_PERMITTED_CREDENTIALS	".ssh/authorized_credentials"
+
+/*
  * Per-user and system-wide ssh "rc" files.  These files are executed with
  * /bin/sh before starting the shell or command if they exist.  They will be
  * passed "proto cookie" as arguments if X11 forwarding with spoofing is in
Index: servconf.c
===================================================================
RCS file: /cvs/openssh/servconf.c,v
retrieving revision 1.219
diff -u -r1.219 servconf.c
--- servconf.c	22 Jun 2011 22:30:03 -0000	1.219
+++ servconf.c	20 Aug 2011 19:39:15 -0000
@@ -136,6 +136,7 @@
 	options->revoked_keys_file = NULL;
 	options->trusted_user_ca_keys = NULL;
 	options->authorized_principals_file = NULL;
+	options->authorized_credentials_file = NULL;
 	options->ip_qos_interactive = -1;
 	options->ip_qos_bulk = -1;
 }
@@ -273,6 +274,9 @@
 		options->permit_tun = SSH_TUNMODE_NO;
 	if (options->zero_knowledge_password_authentication == -1)
 		options->zero_knowledge_password_authentication = 0;
+	if (options->authorized_credentials_file == NULL)
+		options->authorized_credentials_file = 
+			xstrdup(_PATH_SSH_USER_PERMITTED_CREDENTIALS);
 	if (options->ip_qos_interactive == -1)
 		options->ip_qos_interactive = IPTOS_LOWDELAY;
 	if (options->ip_qos_bulk == -1)
@@ -323,6 +327,7 @@
 	sUsePrivilegeSeparation, sAllowAgentForwarding,
 	sZeroKnowledgePasswordAuthentication, sHostCertificate,
 	sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
+	sAuthorizedCredentialsFile,
 	sKexAlgorithms, sIPQoS,
 	sDeprecated, sUnsupported
 } ServerOpCodes;
@@ -446,6 +451,7 @@
 	{ "revokedkeys", sRevokedKeys, SSHCFG_ALL },
 	{ "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL },
 	{ "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL },
+	{ "authorizedcredentialsfile", sAuthorizedCredentialsFile, SSHCFG_ALL },
 	{ "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL },
 	{ "ipqos", sIPQoS, SSHCFG_ALL },
 	{ NULL, sBadOption, 0 }
@@ -1254,7 +1260,11 @@
 		return 0;
 
 	case sAuthorizedPrincipalsFile:
-		charptr = &options->authorized_principals_file;
+	case sAuthorizedCredentialsFile:
+		charptr = (opcode == sAuthorizedPrincipalsFile) ?
+			&options->authorized_principals_file :
+			&options->authorized_credentials_file;
+
 		arg = strdelim(&cp);
 		if (!arg || *arg == '\0')
 			fatal("%s line %d: missing file name.",
@@ -1758,6 +1768,8 @@
 	dump_cfg_string(sRevokedKeys, o->revoked_keys_file);
 	dump_cfg_string(sAuthorizedPrincipalsFile,
 	    o->authorized_principals_file);
+	dump_cfg_string(sAuthorizedCredentialsFile,
+	    o->authorized_credentials_file);
 
 	/* string arguments requiring a lookup */
 	dump_cfg_string(sLogLevel, log_level_name(o->log_level));
Index: servconf.h
===================================================================
RCS file: /cvs/openssh/servconf.h,v
retrieving revision 1.91
diff -u -r1.91 servconf.h
--- servconf.h	22 Jun 2011 22:30:03 -0000	1.91
+++ servconf.h	20 Aug 2011 19:39:15 -0000
@@ -166,6 +166,7 @@
 	char   *revoked_keys_file;
 	char   *trusted_user_ca_keys;
 	char   *authorized_principals_file;
+	char   *authorized_credentials_file;
 }       ServerOptions;
 
 /*
Index: ssh-gss.h
===================================================================
RCS file: /cvs/openssh/ssh-gss.h,v
retrieving revision 1.12
diff -u -r1.12 ssh-gss.h
--- ssh-gss.h	12 Jun 2007 13:40:39 -0000	1.12
+++ ssh-gss.h	20 Aug 2011 19:39:15 -0000
@@ -120,7 +120,8 @@
 
 /* In the server */
 OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
-int ssh_gssapi_userok(char *name);
+struct Authctxt;
+int ssh_gssapi_userok(struct Authctxt *);
 OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
 void ssh_gssapi_do_child(char ***, u_int *);
 void ssh_gssapi_cleanup_creds(void);
