Nicolas Williams
2002-Jan-24 16:54 UTC
PATCH: krb4/krb5/... names/patterns in auth_keys entries
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.
Nicolas Williams
2002-Jan-28 22:16 UTC
PATCH: krb4/krb5/... names/patterns in auth_keys entries
NOTE: There appears to be some confusion, that this patch depends on Simon Wilkinson's MIT compat and GSS patches. It does not. I included two versions: one for plain OpenSSH 3.0.2p1 and one for 3.0.2p1 w/ Simon's patches. Either patch allows the use of Kerberos principal names in authorized_keys entries. OpenSSH 3.0.2p1 comes with Kerberos IV and Kerberos V support for SSHv1, and my patch allows you to authorized kerberos-authenticated SSHv1 connections via authorized_keys entries. If you have Simon's patches installed then you can also use authorized_keys to authorize GSS-API/kerberos-authenticated SSHv2 connections. Comments? Nico On Thu, Jan 24, 2002 at 11:54:52AM -0500, Nicolas Williams wrote:> 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.-Content-Description: patch against OpenSSH 3.0.2p1> 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);Content-Description: patch against OpenSSH 3.0.2p1 w/ gsskeyex> 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; > }Content-Description: Legal Disclaimer> > 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.-- -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.- 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.