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 21 Aug 2011 13:47:04 -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 21 Aug 2011 13:47:04 -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 21 Aug 2011 13:47:04 -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 21 Aug 2011 13:47:04 -0000 @@ -34,13 +34,19 @@ #include #include #include +#include #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,176 @@ } } +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("%s(): cred \"%s\"", __func__, 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; + int quoted = 0; + int len = 0; + + 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; + + cred_options = cp; + for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { + if (*cp == '\\' && cp[1] == '"') + cp++; /* Skip both */ + else if (*cp == '"') + quoted = !quoted; + } + + if (*cp == '\n' || *cp == '\0') { /* No options, rewind */ + cp = cred_options; + cred_options = NULL; + } else { + len = cp - cred_options; + /* Skip remaining whitespace. */ + for (; *cp == ' ' || *cp == '\t'; cp++) + ; + } + debug("%d: cred: \"%s\"", linenum, cp); + if (cred_options) + debug("%d: options: \"%.*s\"", + linenum, len, cred_options); + + + 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 +513,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 21 Aug 2011 13:47:05 -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 21 Aug 2011 13:47:05 -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 21 Aug 2011 13:47:05 -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 21 Aug 2011 13:47:05 -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 21 Aug 2011 13:47:05 -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 21 Aug 2011 13:47:05 -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 21 Aug 2011 13:47:05 -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);