diff options
-rw-r--r-- | ChangeLog | 22 | ||||
-rw-r--r-- | auth-options.c | 43 | ||||
-rw-r--r-- | auth-options.h | 3 | ||||
-rw-r--r-- | auth.c | 41 | ||||
-rw-r--r-- | auth.h | 4 | ||||
-rw-r--r-- | auth2-pubkey.c | 102 | ||||
-rw-r--r-- | key.c | 4 | ||||
-rw-r--r-- | servconf.c | 18 | ||||
-rw-r--r-- | servconf.h | 3 | ||||
-rw-r--r-- | sshd.8 | 15 | ||||
-rw-r--r-- | sshd_config.5 | 41 |
11 files changed, 262 insertions, 34 deletions
@@ -31,6 +31,28 @@ [sftp.c] restore mput and mget which got lost in the tab-completion changes. found by Kenneth Whitaker, ok djm@ + - djm@cvs.openbsd.org 2010/05/07 11:30:30 + [auth-options.c auth-options.h auth.c auth.h auth2-pubkey.c] + [key.c servconf.c servconf.h sshd.8 sshd_config.5] + add some optional indirection to matching of principal names listed + in certificates. Currently, a certificate must include the a user's name + to be accepted for authentication. This change adds the ability to + specify a list of certificate principal names that are acceptable. + + When authenticating using a CA trusted through ~/.ssh/authorized_keys, + this adds a new principals="name1[,name2,...]" key option. + + For CAs listed through sshd_config's TrustedCAKeys option, a new config + option "AuthorizedPrincipalsFile" specifies a per-user file containing + the list of acceptable names. + + If either option is absent, the current behaviour of requiring the + username to appear in principals continues to apply. + + These options are useful for role accounts, disjoint account namespaces + and "user@realm"-style naming policies in certificates. + + feedback and ok markus@ 20100423 - (dtucker) [configure.ac] Bug #1756: Check for the existence of a lib64 dir diff --git a/auth-options.c b/auth-options.c index 60d5f749..57a67ec7 100644 --- a/auth-options.c +++ b/auth-options.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-options.c,v 1.50 2010/04/16 01:47:26 djm Exp $ */ +/* $OpenBSD: auth-options.c,v 1.51 2010/05/07 11:30:29 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -55,6 +55,9 @@ struct envstring *custom_environment = NULL; /* "tunnel=" option. */ int forced_tun_device = -1; +/* "principals=" option. */ +char *authorized_principals = NULL; + extern ServerOptions options; void @@ -76,6 +79,10 @@ auth_clear_options(void) xfree(forced_command); forced_command = NULL; } + if (authorized_principals) { + xfree(authorized_principals); + authorized_principals = NULL; + } forced_tun_device = -1; channel_clear_permitted_opens(); } @@ -141,6 +148,8 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum) cp = "command=\""; if (strncasecmp(opts, cp, strlen(cp)) == 0) { opts += strlen(cp); + if (forced_command != NULL) + xfree(forced_command); forced_command = xmalloc(strlen(opts) + 1); i = 0; while (*opts) { @@ -167,6 +176,38 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum) opts++; goto next_option; } + cp = "principals=\""; + if (strncasecmp(opts, cp, strlen(cp)) == 0) { + opts += strlen(cp); + if (authorized_principals != NULL) + xfree(authorized_principals); + authorized_principals = xmalloc(strlen(opts) + 1); + i = 0; + while (*opts) { + if (*opts == '"') + break; + if (*opts == '\\' && opts[1] == '"') { + opts += 2; + authorized_principals[i++] = '"'; + continue; + } + authorized_principals[i++] = *opts++; + } + if (!*opts) { + debug("%.100s, line %lu: missing end quote", + file, linenum); + auth_debug_add("%.100s, line %lu: missing end quote", + file, linenum); + xfree(authorized_principals); + authorized_principals = NULL; + goto bad_option; + } + authorized_principals[i] = '\0'; + auth_debug_add("principals: %.900s", + authorized_principals); + opts++; + goto next_option; + } cp = "environment=\""; if (options.permit_user_env && strncasecmp(opts, cp, strlen(cp)) == 0) { diff --git a/auth-options.h b/auth-options.h index 20f0dbe3..7455c945 100644 --- a/auth-options.h +++ b/auth-options.h @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-options.h,v 1.19 2010/04/16 01:47:26 djm Exp $ */ +/* $OpenBSD: auth-options.h,v 1.20 2010/05/07 11:30:29 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> @@ -31,6 +31,7 @@ extern char *forced_command; extern struct envstring *custom_environment; extern int forced_tun_device; extern int key_is_cert_authority; +extern char *authorized_principals; int auth_parse_options(struct passwd *, char *, char *, u_long); void auth_clear_options(void); @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.c,v 1.86 2010/03/05 02:58:11 djm Exp $ */ +/* $OpenBSD: auth.c,v 1.87 2010/05/07 11:30:29 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -366,6 +366,14 @@ authorized_keys_file2(struct passwd *pw) return expand_authorized_keys(options.authorized_keys_file2, pw); } +char * +authorized_principals_file(struct passwd *pw) +{ + if (options.authorized_principals_file == NULL) + return NULL; + return expand_authorized_keys(options.authorized_principals_file, pw); +} + /* return ok if key exists in sysfile or userfile */ HostStatus check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host, @@ -477,21 +485,18 @@ secure_filename(FILE *f, const char *file, struct passwd *pw, return 0; } -FILE * -auth_openkeyfile(const char *file, struct passwd *pw, int strict_modes) +static FILE * +auth_openfile(const char *file, struct passwd *pw, int strict_modes, + int log_missing, char *file_type) { char line[1024]; struct stat st; int fd; FILE *f; - /* - * Open the file containing the authorized keys - * Fail quietly if file does not exist - */ if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) { - if (errno != ENOENT) - debug("Could not open keyfile '%s': %s", file, + if (log_missing || errno != ENOENT) + debug("Could not open %s '%s': %s", file_type, file, strerror(errno)); return NULL; } @@ -501,8 +506,8 @@ auth_openkeyfile(const char *file, struct passwd *pw, int strict_modes) return NULL; } if (!S_ISREG(st.st_mode)) { - logit("User %s authorized keys %s is not a regular file", - pw->pw_name, file); + logit("User %s %s %s is not a regular file", + pw->pw_name, file_type, file); close(fd); return NULL; } @@ -521,6 +526,20 @@ auth_openkeyfile(const char *file, struct passwd *pw, int strict_modes) return f; } + +FILE * +auth_openkeyfile(const char *file, struct passwd *pw, int strict_modes) +{ + return auth_openfile(file, pw, strict_modes, 1, "authorized keys"); +} + +FILE * +auth_openprincipals(const char *file, struct passwd *pw, int strict_modes) +{ + return auth_openfile(file, pw, strict_modes, 0, + "authorized principals"); +} + struct passwd * getpwnamallow(const char *user) { @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.h,v 1.65 2010/03/04 10:36:03 djm Exp $ */ +/* $OpenBSD: auth.h,v 1.66 2010/05/07 11:30:29 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -169,8 +169,10 @@ void abandon_challenge_response(Authctxt *); char *authorized_keys_file(struct passwd *); char *authorized_keys_file2(struct passwd *); +char *authorized_principals_file(struct passwd *); FILE *auth_openkeyfile(const char *, struct passwd *, int); +FILE *auth_openprincipals(const char *, struct passwd *, int); int auth_key_is_revoked(Key *); HostStatus diff --git a/auth2-pubkey.c b/auth2-pubkey.c index 83ecd659..6b4a9972 100644 --- a/auth2-pubkey.c +++ b/auth2-pubkey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-pubkey.c,v 1.23 2010/04/16 01:47:26 djm Exp $ */ +/* $OpenBSD: auth2-pubkey.c,v 1.24 2010/05/07 11:30:29 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -57,6 +57,7 @@ #include "monitor_wrap.h" #include "misc.h" #include "authfile.h" +#include "match.h" /* import */ extern ServerOptions options; @@ -176,6 +177,63 @@ done: return authenticated; } +static int +match_principals_option(const char *principal_list, struct KeyCert *cert) +{ + char *result; + u_int i; + + /* XXX percent_expand() sequences for authorized_principals? */ + + for (i = 0; i < cert->nprincipals; i++) { + if ((result = match_list(cert->principals[i], + principal_list, NULL)) != NULL) { + debug3("matched principal from key options \"%.100s\"", + result); + xfree(result); + return 1; + } + } + return 0; +} + +static int +match_principals_file(const char *file, struct passwd *pw, struct KeyCert *cert) +{ + FILE *f; + char line[SSH_MAX_PUBKEY_BYTES], *cp; + u_long linenum = 0; + u_int i; + + temporarily_use_uid(pw); + debug("trying authorized principals file %s", file); + if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) { + restore_uid(); + return 0; + } + while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { + /* Skip leading whitespace, empty and comment lines. */ + for (cp = line; *cp == ' ' || *cp == '\t'; cp++) + ; + if (!*cp || *cp == '\n' || *cp == '#') + continue; + line[strcspn(line, "\n")] = '\0'; + + for (i = 0; i < cert->nprincipals; i++) { + if (strcmp(cp, cert->principals[i]) == 0) { + debug3("matched principal from file \"%.100s\"", + cert->principals[i]); + fclose(f); + restore_uid(); + return 1; + } + } + } + fclose(f); + restore_uid(); + return 0; +} + /* return 1 if user allows given key */ static int user_key_allowed2(struct passwd *pw, Key *key, char *file) @@ -244,13 +302,26 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file) SSH_FP_HEX); debug("matching CA found: file %s, line %lu, %s %s", file, linenum, key_type(found), fp); - if (key_cert_check_authority(key, 0, 0, pw->pw_name, - &reason) != 0) { + /* + * If the user has specified a list of principals as + * a key option, then prefer that list to matching + * their username in the certificate principals list. + */ + if (authorized_principals != NULL && + !match_principals_option(authorized_principals, + key->cert)) { + reason = "Certificate does not contain an " + "authorized principal"; + fail_reason: xfree(fp); error("%s", reason); auth_debug_add("%s", reason); continue; } + if (key_cert_check_authority(key, 0, 0, + authorized_principals == NULL ? pw->pw_name : NULL, + &reason) != 0) + goto fail_reason; if (auth_cert_options(key, pw) != 0) { xfree(fp); continue; @@ -284,7 +355,7 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file) static int user_cert_trusted_ca(struct passwd *pw, Key *key) { - char *ca_fp; + char *ca_fp, *principals_file = NULL; const char *reason; int ret = 0; @@ -301,11 +372,24 @@ user_cert_trusted_ca(struct passwd *pw, Key *key) options.trusted_user_ca_keys); goto out; } - if (key_cert_check_authority(key, 0, 1, pw->pw_name, &reason) != 0) { - error("%s", reason); - auth_debug_add("%s", reason); - goto out; + /* + * If AuthorizedPrincipals is in use, then compare the certificate + * principals against the names in that file rather than matching + * against the username. + */ + if ((principals_file = authorized_principals_file(pw)) != NULL) { + if (!match_principals_file(principals_file, pw, key->cert)) { + reason = "Certificate does not contain an " + "authorized principal"; + fail_reason: + error("%s", reason); + auth_debug_add("%s", reason); + goto out; + } } + if (key_cert_check_authority(key, 0, 1, + principals_file == NULL ? pw->pw_name : NULL, &reason) != 0) + goto fail_reason; if (auth_cert_options(key, pw) != 0) goto out; @@ -315,6 +399,8 @@ user_cert_trusted_ca(struct passwd *pw, Key *key) ret = 1; out: + if (principals_file != NULL) + xfree(principals_file); if (ca_fp != NULL) xfree(ca_fp); return ret; @@ -1,4 +1,4 @@ -/* $OpenBSD: key.c,v 1.87 2010/04/16 01:47:26 djm Exp $ */ +/* $OpenBSD: key.c,v 1.88 2010/05/07 11:30:29 djm Exp $ */ /* * read_bignum(): * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -1623,7 +1623,7 @@ key_cert_check_authority(const Key *k, int want_host, int require_principal, *reason = "Certificate lacks principal list"; return -1; } - } else { + } else if (name != NULL) { principal_matches = 0; for (i = 0; i < k->cert->nprincipals; i++) { if (strcmp(name, k->cert->principals[i]) == 0) { @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.c,v 1.207 2010/03/25 23:38:28 djm Exp $ */ +/* $OpenBSD: servconf.c,v 1.208 2010/05/07 11:30:29 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * All rights reserved @@ -131,6 +131,7 @@ initialize_server_options(ServerOptions *options) options->zero_knowledge_password_authentication = -1; options->revoked_keys_file = NULL; options->trusted_user_ca_keys = NULL; + options->authorized_principals_file = NULL; } void @@ -310,7 +311,7 @@ typedef enum { sMatch, sPermitOpen, sForceCommand, sChrootDirectory, sUsePrivilegeSeparation, sAllowAgentForwarding, sZeroKnowledgePasswordAuthentication, sHostCertificate, - sRevokedKeys, sTrustedUserCAKeys, + sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile, sDeprecated, sUnsupported } ServerOpCodes; @@ -432,6 +433,7 @@ static struct { { "hostcertificate", sHostCertificate, SSHCFG_GLOBAL }, { "revokedkeys", sRevokedKeys, SSHCFG_ALL }, { "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL }, + { "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_GLOBAL }, { NULL, sBadOption, 0 } }; @@ -1218,10 +1220,14 @@ process_server_config_line(ServerOptions *options, char *line, * AuthorizedKeysFile /etc/ssh_keys/%u */ case sAuthorizedKeysFile: + charptr = &options->authorized_keys_file; + goto parse_tilde_filename; case sAuthorizedKeysFile2: - charptr = (opcode == sAuthorizedKeysFile) ? - &options->authorized_keys_file : - &options->authorized_keys_file2; + charptr = &options->authorized_keys_file2; + goto parse_tilde_filename; + case sAuthorizedPrincipalsFile: + charptr = &options->authorized_principals_file; + parse_tilde_filename: arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing file name.", @@ -1682,6 +1688,8 @@ dump_config(ServerOptions *o) dump_cfg_string(sChrootDirectory, o->chroot_directory); dump_cfg_string(sTrustedUserCAKeys, o->trusted_user_ca_keys); dump_cfg_string(sRevokedKeys, o->revoked_keys_file); + dump_cfg_string(sAuthorizedPrincipalsFile, + o->authorized_principals_file); /* string arguments requiring a lookup */ dump_cfg_string(sLogLevel, log_level_name(o->log_level)); @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.h,v 1.92 2010/03/04 10:36:03 djm Exp $ */ +/* $OpenBSD: servconf.h,v 1.93 2010/05/07 11:30:30 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> @@ -156,6 +156,7 @@ typedef struct { char *chroot_directory; char *revoked_keys_file; char *trusted_user_ca_keys; + char *authorized_principals_file; } ServerOptions; void initialize_server_options(ServerOptions *); @@ -34,8 +34,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd.8,v 1.255 2010/03/05 06:50:35 jmc Exp $ -.Dd $Mdocdate: March 5 2010 $ +.\" $OpenBSD: sshd.8,v 1.256 2010/05/07 11:30:30 djm Exp $ +.Dd $Mdocdate: May 7 2010 $ .Dt SSHD 8 .Os .Sh NAME @@ -602,6 +602,17 @@ Multiple options may be applied separated by commas. No pattern matching is performed on the specified hostnames, they must be literal domains or addresses. +.It Cm principals="principals" +On a +.Cm cert-authority +line, specifies allowed principals for certificate authentication as a +comma-separated list. +At least one name from the list must appear in the certificate's +list of principals for the certificate to be accepted. +This option is ignored for keys that are not marked as trusted certificate +signers using the +.Cm cert-authority +option. .It Cm tunnel="n" Force a .Xr tun 4 diff --git a/sshd_config.5 b/sshd_config.5 index 2f541028..a5260d35 100644 --- a/sshd_config.5 +++ b/sshd_config.5 @@ -34,8 +34,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd_config.5,v 1.120 2010/03/04 23:17:25 djm Exp $ -.Dd $Mdocdate: March 4 2010 $ +.\" $OpenBSD: sshd_config.5,v 1.121 2010/05/07 11:30:30 djm Exp $ +.Dd $Mdocdate: May 7 2010 $ .Dt SSHD_CONFIG 5 .Os .Sh NAME @@ -167,6 +167,43 @@ is taken to be an absolute path or one relative to the user's home directory. The default is .Dq .ssh/authorized_keys . +.It Cm AuthorizedPrincipalsFile +Specifies a file that lists principal names that are accepted for +certificate authentication. +When using certificates signed by a key listed in +.Cm TrustedUserCAKeys , +this file lists names, one of which must appear in the certificate for it +to be accepted for authentication. +Names are listed one per line; empty lines and comments starting with +.Ql # +are ignored. +.Pp +.Cm AuthorizedPrincipalsFile +may contain tokens of the form %T which are substituted during connection +setup. +The following tokens are defined: %% is replaced by a literal '%', +%h is replaced by the home directory of the user being authenticated, and +%u is replaced by the username of that user. +After expansion, +.Cm AuthorizedPrincipalsFile +is taken to be an absolute path or one relative to the user's home +directory. +.Pp +The default is not to use a principals file - in this case, the username +of the user must appear in a certificate's principals list for it to be +accepted. +Note that +.Cm AuthorizedPrincipalsFile +is only used when authentication proceeds using a CA listed in +.Cm TrustedUserCAKeys +and is not consulted for certification authorities trusted via +.Pa ~/.ssh/authorized_keys , +though the +.Cm principals= +key option offers a similar facility (see +.Xr sshd 8 +for details). +.Pp .It Cm Banner The contents of the specified file are sent to the remote user before authentication is allowed. |