From 4e270b05dd9d850fb9e2e0ac43f33cb4090d3ebc Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Fri, 16 Apr 2010 15:56:21 +1000 Subject: - djm@cvs.openbsd.org 2010/04/16 01:47:26 [PROTOCOL.certkeys auth-options.c auth-options.h auth-rsa.c] [auth2-pubkey.c authfd.c key.c key.h myproposal.h ssh-add.c] [ssh-agent.c ssh-dss.c ssh-keygen.1 ssh-keygen.c ssh-rsa.c] [sshconnect.c sshconnect2.c sshd.c] revised certificate format ssh-{dss,rsa}-cert-v01@openssh.com with the following changes: move the nonce field to the beginning of the certificate where it can better protect against chosen-prefix attacks on the signature hash Rename "constraints" field to "critical options" Add a new non-critical "extensions" field Add a serial number The older format is still support for authentication and cert generation (use "ssh-keygen -t v00 -s ca_key ..." to generate a v00 certificate) ok markus@ --- auth-options.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'auth-options.c') diff --git a/auth-options.c b/auth-options.c index 69b314fbd..60d5f749b 100644 --- a/auth-options.c +++ b/auth-options.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-options.c,v 1.49 2010/03/16 15:46:52 stevesk Exp $ */ +/* $OpenBSD: auth-options.c,v 1.50 2010/04/16 01:47:26 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -27,10 +27,10 @@ #include "canohost.h" #include "buffer.h" #include "channels.h" -#include "auth-options.h" #include "servconf.h" #include "misc.h" #include "key.h" +#include "auth-options.h" #include "hostfile.h" #include "auth.h" #ifdef GSSAPI @@ -377,11 +377,11 @@ bad_option: } /* - * Set options from certificate constraints. These supersede user key options - * so this must be called after auth_parse_options(). + * Set options from critical certificate options. These supersede user key + * options so this must be called after auth_parse_options(). */ int -auth_cert_constraints(Buffer *c_orig, struct passwd *pw) +auth_cert_options(Key *k, struct passwd *pw) { u_char *name = NULL, *data_blob = NULL; u_int nlen, dlen, clen; @@ -400,12 +400,13 @@ auth_cert_constraints(Buffer *c_orig, struct passwd *pw) /* Make copy to avoid altering original */ buffer_init(&c); - buffer_append(&c, buffer_ptr(c_orig), buffer_len(c_orig)); + buffer_append(&c, + buffer_ptr(&k->cert->critical), buffer_len(&k->cert->critical)); while (buffer_len(&c) > 0) { if ((name = buffer_get_string_ret(&c, &nlen)) == NULL || (data_blob = buffer_get_string_ret(&c, &dlen)) == NULL) { - error("Certificate constraints corrupt"); + error("Certificate options corrupt"); goto out; } buffer_append(&data, data_blob, dlen); @@ -439,7 +440,7 @@ auth_cert_constraints(Buffer *c_orig, struct passwd *pw) } if (cert_forced_command != NULL) { error("Certificate has multiple " - "force-command constraints"); + "force-command options"); xfree(command); goto out; } @@ -459,7 +460,7 @@ auth_cert_constraints(Buffer *c_orig, struct passwd *pw) } if (cert_source_address_done++) { error("Certificate has multiple " - "source-address constraints"); + "source-address options"); xfree(allowed); goto out; } @@ -502,7 +503,7 @@ auth_cert_constraints(Buffer *c_orig, struct passwd *pw) name = data_blob = NULL; } - /* successfully parsed all constraints */ + /* successfully parsed all options */ ret = 0; no_port_forwarding_flag |= cert_no_port_forwarding_flag; -- cgit v1.2.3 From 30da3447d2ef3329cb0eb083cdddf84532659454 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Mon, 10 May 2010 11:58:03 +1000 Subject: - 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@ --- ChangeLog | 22 +++++++++++++ auth-options.c | 43 +++++++++++++++++++++++- auth-options.h | 3 +- auth.c | 41 ++++++++++++++++------- auth.h | 4 ++- auth2-pubkey.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- key.c | 4 +-- servconf.c | 18 +++++++--- servconf.h | 3 +- sshd.8 | 15 +++++++-- sshd_config.5 | 41 +++++++++++++++++++++-- 11 files changed, 262 insertions(+), 34 deletions(-) (limited to 'auth-options.c') diff --git a/ChangeLog b/ChangeLog index 684609459..090e2352c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -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 60d5f749b..57a67ec79 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 * Copyright (c) 1995 Tatu Ylonen , 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 20f0dbe3e..7455c9454 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 @@ -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); diff --git a/auth.c b/auth.c index 89a936068..bec191a59 100644 --- a/auth.c +++ b/auth.c @@ -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) { diff --git a/auth.h b/auth.h index a65b87dd1..77317aee6 100644 --- a/auth.h +++ b/auth.h @@ -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 83ecd6590..6b4a99725 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; diff --git a/key.c b/key.c index 34f678b38..c4d9d5771 100644 --- a/key.c +++ b/key.c @@ -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 , 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) { diff --git a/servconf.c b/servconf.c index 7d027ddb9..c556986e3 100644 --- a/servconf.c +++ b/servconf.c @@ -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 , 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)); diff --git a/servconf.h b/servconf.h index 860009f9c..45d2a2ae3 100644 --- a/servconf.h +++ b/servconf.h @@ -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 @@ -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 *); diff --git a/sshd.8 b/sshd.8 index 5f1966005..6eb49238a 100644 --- a/sshd.8 +++ b/sshd.8 @@ -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 2f5410281..a5260d358 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. -- cgit v1.2.3 From d0e4a8e2e0bc6fcee6cd8486fbcdffaf7d037aed Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Fri, 21 May 2010 14:58:32 +1000 Subject: - djm@cvs.openbsd.org 2010/05/20 23:46:02 [PROTOCOL.certkeys auth-options.c ssh-keygen.c] Move the permit-* options to the non-critical "extensions" field for v01 certificates. The logic is that if another implementation fails to implement them then the connection just loses features rather than fails outright. ok markus@ --- ChangeLog | 8 ++ PROTOCOL.certkeys | 35 ++++--- auth-options.c | 282 ++++++++++++++++++++++++++++++++++-------------------- ssh-keygen.c | 94 ++++++++++-------- 4 files changed, 267 insertions(+), 152 deletions(-) (limited to 'auth-options.c') diff --git a/ChangeLog b/ChangeLog index 2cc1369fd..86c66d1a7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -33,6 +33,14 @@ [auth2-pubkey.c] fix logspam when key options (from="..." especially) deny non-matching keys; reported by henning@ also bz#1765; ok markus@ dtucker@ + - djm@cvs.openbsd.org 2010/05/20 23:46:02 + [PROTOCOL.certkeys auth-options.c ssh-keygen.c] + Move the permit-* options to the non-critical "extensions" field for v01 + certificates. The logic is that if another implementation fails to + implement them then the connection just loses features rather than fails + outright. + + ok markus@ 20100511 - (dtucker) [Makefile.in] Bug #1770: Link libopenbsd-compat twice to solve diff --git a/PROTOCOL.certkeys b/PROTOCOL.certkeys index 0fa5748f3..81b02a078 100644 --- a/PROTOCOL.certkeys +++ b/PROTOCOL.certkeys @@ -131,7 +131,7 @@ must refuse to authorise a key that has an unrecognised option. extensions is a set of zero or more optional extensions. These extensions are not critical, and an implementation that encounters one that it does -not recognise may safely ignore it. No extensions are defined at present. +not recognise may safely ignore it. The reserved field is currently unused and is ignored in this version of the protocol. @@ -172,6 +172,28 @@ force-command string Specifies a command that is executed ssh command-line) whenever this key is used for authentication. +source-address string Comma-separated list of source addresses + from which this certificate is accepted + for authentication. Addresses are + specified in CIDR format (nn.nn.nn.nn/nn + or hhhh::hhhh/nn). + If this option is not present then + certificates may be presented from any + source address. + +Extensions +---------- + +The extensions section of the certificate specifies zero or more +non-critical certificate extensions. The encoding of extensions in this +field is identical to that of the critical options. If an implementation +does not recognise an extension, then it should ignore it. + +The supported extensions and the contents and structure of their data +fields are: + +Name Format Description +----------------------------------------------------------------------------- permit-X11-forwarding empty Flag indicating that X11 forwarding should be permitted. X11 forwarding will be refused if this option is absent. @@ -196,13 +218,4 @@ permit-user-rc empty Flag indicating that execution of of this script will not be permitted if this option is not present. -source-address string Comma-separated list of source addresses - from which this certificate is accepted - for authentication. Addresses are - specified in CIDR format (nn.nn.nn.nn/nn - or hhhh::hhhh/nn). - If this option is not present then - certificates may be presented from any - source address. - -$OpenBSD: PROTOCOL.certkeys,v 1.5 2010/05/01 02:50:50 djm Exp $ +$OpenBSD: PROTOCOL.certkeys,v 1.6 2010/05/20 23:46:02 djm Exp $ diff --git a/auth-options.c b/auth-options.c index 57a67ec79..a7040247f 100644 --- a/auth-options.c +++ b/auth-options.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-options.c,v 1.51 2010/05/07 11:30:29 djm Exp $ */ +/* $OpenBSD: auth-options.c,v 1.52 2010/05/20 23:46:02 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -417,32 +417,31 @@ bad_option: return 0; } -/* - * Set options from critical certificate options. These supersede user key - * options so this must be called after auth_parse_options(). - */ -int -auth_cert_options(Key *k, struct passwd *pw) +#define OPTIONS_CRITICAL 1 +#define OPTIONS_EXTENSIONS 2 +static int +parse_option_list(u_char *optblob, size_t optblob_len, struct passwd *pw, + u_int which, int crit, + int *cert_no_port_forwarding_flag, + int *cert_no_agent_forwarding_flag, + int *cert_no_x11_forwarding_flag, + int *cert_no_pty_flag, + int *cert_no_user_rc, + char **cert_forced_command, + int *cert_source_address_done) { + char *command, *allowed; + const char *remote_ip; u_char *name = NULL, *data_blob = NULL; u_int nlen, dlen, clen; Buffer c, data; - int ret = -1; - - int cert_no_port_forwarding_flag = 1; - int cert_no_agent_forwarding_flag = 1; - int cert_no_x11_forwarding_flag = 1; - int cert_no_pty_flag = 1; - int cert_no_user_rc = 1; - char *cert_forced_command = NULL; - int cert_source_address_done = 0; + int ret = -1, found; buffer_init(&data); /* Make copy to avoid altering original */ buffer_init(&c); - buffer_append(&c, - buffer_ptr(&k->cert->critical), buffer_len(&k->cert->critical)); + buffer_append(&c, optblob, optblob_len); while (buffer_len(&c) > 0) { if ((name = buffer_get_string_ret(&c, &nlen)) == NULL || @@ -451,90 +450,114 @@ auth_cert_options(Key *k, struct passwd *pw) goto out; } buffer_append(&data, data_blob, dlen); - debug3("found certificate constraint \"%.100s\" len %u", + debug3("found certificate option \"%.100s\" len %u", name, dlen); if (strlen(name) != nlen) { error("Certificate constraint name contains \\0"); goto out; } - if (strcmp(name, "permit-X11-forwarding") == 0) - cert_no_x11_forwarding_flag = 0; - else if (strcmp(name, "permit-agent-forwarding") == 0) - cert_no_agent_forwarding_flag = 0; - else if (strcmp(name, "permit-port-forwarding") == 0) - cert_no_port_forwarding_flag = 0; - else if (strcmp(name, "permit-pty") == 0) - cert_no_pty_flag = 0; - else if (strcmp(name, "permit-user-rc") == 0) - cert_no_user_rc = 0; - else if (strcmp(name, "force-command") == 0) { - char *command = buffer_get_string_ret(&data, &clen); - - if (command == NULL) { - error("Certificate constraint \"%s\" corrupt", - name); - goto out; - } - if (strlen(command) != clen) { - error("force-command constraint contains \\0"); - goto out; - } - if (cert_forced_command != NULL) { - error("Certificate has multiple " - "force-command options"); - xfree(command); - goto out; - } - cert_forced_command = command; - } else if (strcmp(name, "source-address") == 0) { - char *allowed = buffer_get_string_ret(&data, &clen); - const char *remote_ip = get_remote_ipaddr(); - - if (allowed == NULL) { - error("Certificate constraint \"%s\" corrupt", - name); - goto out; - } - if (strlen(allowed) != clen) { - error("source-address constraint contains \\0"); - goto out; + found = 0; + if ((which & OPTIONS_EXTENSIONS) != 0) { + if (strcmp(name, "permit-X11-forwarding") == 0) { + *cert_no_x11_forwarding_flag = 0; + found = 1; + } else if (strcmp(name, + "permit-agent-forwarding") == 0) { + *cert_no_agent_forwarding_flag = 0; + found = 1; + } else if (strcmp(name, + "permit-port-forwarding") == 0) { + *cert_no_port_forwarding_flag = 0; + found = 1; + } else if (strcmp(name, "permit-pty") == 0) { + *cert_no_pty_flag = 0; + found = 1; + } else if (strcmp(name, "permit-user-rc") == 0) { + *cert_no_user_rc = 0; + found = 1; } - if (cert_source_address_done++) { - error("Certificate has multiple " - "source-address options"); - xfree(allowed); - goto out; + } + if (!found && (which & OPTIONS_CRITICAL) != 0) { + if (strcmp(name, "force-command") == 0) { + if ((command = buffer_get_string_ret(&data, + &clen)) == NULL) { + error("Certificate constraint \"%s\" " + "corrupt", name); + goto out; + } + if (strlen(command) != clen) { + error("force-command constraint " + "contains \\0"); + goto out; + } + if (*cert_forced_command != NULL) { + error("Certificate has multiple " + "force-command options"); + xfree(command); + goto out; + } + *cert_forced_command = command; + found = 1; } - switch (addr_match_cidr_list(remote_ip, allowed)) { - case 1: - /* accepted */ - xfree(allowed); - break; - case 0: - /* no match */ - logit("Authentication tried for %.100s with " - "valid certificate but not from a " - "permitted host (ip=%.200s).", - pw->pw_name, remote_ip); - auth_debug_add("Your address '%.200s' is not " - "permitted to use this certificate for " - "login.", remote_ip); - xfree(allowed); - goto out; - case -1: - error("Certificate source-address contents " - "invalid"); - xfree(allowed); - goto out; + if (strcmp(name, "source-address") == 0) { + if ((allowed = buffer_get_string_ret(&data, + &clen)) == NULL) { + error("Certificate constraint " + "\"%s\" corrupt", name); + goto out; + } + if (strlen(allowed) != clen) { + error("source-address constraint " + "contains \\0"); + goto out; + } + if ((*cert_source_address_done)++) { + error("Certificate has multiple " + "source-address options"); + xfree(allowed); + goto out; + } + remote_ip = get_remote_ipaddr(); + switch (addr_match_cidr_list(remote_ip, + allowed)) { + case 1: + /* accepted */ + xfree(allowed); + break; + case 0: + /* no match */ + logit("Authentication tried for %.100s " + "with valid certificate but not " + "from a permitted host " + "(ip=%.200s).", pw->pw_name, + remote_ip); + auth_debug_add("Your address '%.200s' " + "is not permitted to use this " + "certificate for login.", + remote_ip); + xfree(allowed); + goto out; + case -1: + error("Certificate source-address " + "contents invalid"); + xfree(allowed); + goto out; + } + found = 1; } - } else { - error("Certificate constraint \"%s\" is not supported", - name); - goto out; } - if (buffer_len(&data) != 0) { - error("Certificate constraint \"%s\" corrupt " + if (!found) { + if (crit) { + error("Certificate critical option \"%s\" " + "is not supported", name); + goto out; + } else { + logit("Certificate extension \"%s\" " + "is not supported", name); + } + } else if (buffer_len(&data) != 0) { + error("Certificate option \"%s\" corrupt " "(extra data)", name); goto out; } @@ -543,10 +566,73 @@ auth_cert_options(Key *k, struct passwd *pw) xfree(data_blob); name = data_blob = NULL; } - /* successfully parsed all options */ ret = 0; + out: + if (ret != 0 && + cert_forced_command != NULL && + *cert_forced_command != NULL) { + xfree(*cert_forced_command); + *cert_forced_command = NULL; + } + if (name != NULL) + xfree(name); + if (data_blob != NULL) + xfree(data_blob); + buffer_free(&data); + buffer_free(&c); + return ret; +} + +/* + * Set options from critical certificate options. These supersede user key + * options so this must be called after auth_parse_options(). + */ +int +auth_cert_options(Key *k, struct passwd *pw) +{ + int cert_no_port_forwarding_flag = 1; + int cert_no_agent_forwarding_flag = 1; + int cert_no_x11_forwarding_flag = 1; + int cert_no_pty_flag = 1; + int cert_no_user_rc = 1; + char *cert_forced_command = NULL; + int cert_source_address_done = 0; + + if (key_cert_is_legacy(k)) { + /* All options are in the one field for v00 certs */ + if (parse_option_list(buffer_ptr(&k->cert->critical), + buffer_len(&k->cert->critical), pw, + OPTIONS_CRITICAL|OPTIONS_EXTENSIONS, 1, + &cert_no_port_forwarding_flag, + &cert_no_agent_forwarding_flag, + &cert_no_x11_forwarding_flag, + &cert_no_pty_flag, + &cert_no_user_rc, + &cert_forced_command, + &cert_source_address_done) == -1) + return -1; + } else { + /* Separate options and extensions for v01 certs */ + if (parse_option_list(buffer_ptr(&k->cert->critical), + buffer_len(&k->cert->critical), pw, + OPTIONS_CRITICAL, 1, NULL, NULL, NULL, NULL, NULL, + &cert_forced_command, + &cert_source_address_done) == -1) + return -1; + if (parse_option_list(buffer_ptr(&k->cert->extensions), + buffer_len(&k->cert->extensions), pw, + OPTIONS_EXTENSIONS, 1, + &cert_no_port_forwarding_flag, + &cert_no_agent_forwarding_flag, + &cert_no_x11_forwarding_flag, + &cert_no_pty_flag, + &cert_no_user_rc, + NULL, NULL) == -1) + return -1; + } + no_port_forwarding_flag |= cert_no_port_forwarding_flag; no_agent_forwarding_flag |= cert_no_agent_forwarding_flag; no_x11_forwarding_flag |= cert_no_x11_forwarding_flag; @@ -558,14 +644,6 @@ auth_cert_options(Key *k, struct passwd *pw) xfree(forced_command); forced_command = cert_forced_command; } - - out: - if (name != NULL) - xfree(name); - if (data_blob != NULL) - xfree(data_blob); - buffer_free(&data); - buffer_free(&c); - return ret; + return 0; } diff --git a/ssh-keygen.c b/ssh-keygen.c index 1eb25bd94..14eee6f87 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keygen.c,v 1.189 2010/04/23 22:48:31 djm Exp $ */ +/* $OpenBSD: ssh-keygen.c,v 1.190 2010/05/20 23:46:02 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland @@ -122,17 +122,16 @@ u_int64_t cert_valid_from = 0; u_int64_t cert_valid_to = ~0ULL; /* Certificate options */ -#define CRITOPT_X_FWD (1) -#define CRITOPT_AGENT_FWD (1<<1) -#define CRITOPT_PORT_FWD (1<<2) -#define CRITOPT_PTY (1<<3) -#define CRITOPT_USER_RC (1<<4) -#define CRITOPT_DEFAULT (CRITOPT_X_FWD|CRITOPT_AGENT_FWD| \ - CRITOPT_PORT_FWD|CRITOPT_PTY| \ - CRITOPT_USER_RC) -u_int32_t critical_flags = CRITOPT_DEFAULT; -char *critical_command = NULL; -char *critical_src_addr = NULL; +#define CERTOPT_X_FWD (1) +#define CERTOPT_AGENT_FWD (1<<1) +#define CERTOPT_PORT_FWD (1<<2) +#define CERTOPT_PTY (1<<3) +#define CERTOPT_USER_RC (1<<4) +#define CERTOPT_DEFAULT (CERTOPT_X_FWD|CERTOPT_AGENT_FWD| \ + CERTOPT_PORT_FWD|CERTOPT_PTY|CERTOPT_USER_RC) +u_int32_t certflags_flags = CERTOPT_DEFAULT; +char *certflags_command = NULL; +char *certflags_src_addr = NULL; /* Dump public key file in format used by real and the original SSH 2 */ int convert_to_ssh2 = 0; @@ -1133,24 +1132,33 @@ add_string_option(Buffer *c, const char *name, const char *value) buffer_free(&b); } +#define OPTIONS_CRITICAL 1 +#define OPTIONS_EXTENSIONS 2 static void -prepare_options_buf(Buffer *c) +prepare_options_buf(Buffer *c, int which) { buffer_clear(c); - if ((critical_flags & CRITOPT_X_FWD) != 0) + if ((which & OPTIONS_EXTENSIONS) != 0 && + (certflags_flags & CERTOPT_X_FWD) != 0) add_flag_option(c, "permit-X11-forwarding"); - if ((critical_flags & CRITOPT_AGENT_FWD) != 0) + if ((which & OPTIONS_EXTENSIONS) != 0 && + (certflags_flags & CERTOPT_AGENT_FWD) != 0) add_flag_option(c, "permit-agent-forwarding"); - if ((critical_flags & CRITOPT_PORT_FWD) != 0) + if ((which & OPTIONS_EXTENSIONS) != 0 && + (certflags_flags & CERTOPT_PORT_FWD) != 0) add_flag_option(c, "permit-port-forwarding"); - if ((critical_flags & CRITOPT_PTY) != 0) + if ((which & OPTIONS_EXTENSIONS) != 0 && + (certflags_flags & CERTOPT_PTY) != 0) add_flag_option(c, "permit-pty"); - if ((critical_flags & CRITOPT_USER_RC) != 0) + if ((which & OPTIONS_EXTENSIONS) != 0 && + (certflags_flags & CERTOPT_USER_RC) != 0) add_flag_option(c, "permit-user-rc"); - if (critical_command != NULL) - add_string_option(c, "force-command", critical_command); - if (critical_src_addr != NULL) - add_string_option(c, "source-address", critical_src_addr); + if ((which & OPTIONS_CRITICAL) != 0 && + certflags_command != NULL) + add_string_option(c, "force-command", certflags_command); + if ((which & OPTIONS_CRITICAL) != 0 && + certflags_src_addr != NULL) + add_string_option(c, "source-address", certflags_src_addr); } static void @@ -1218,7 +1226,15 @@ do_ca_sign(struct passwd *pw, int argc, char **argv) public->cert->principals = plist; public->cert->valid_after = cert_valid_from; public->cert->valid_before = cert_valid_to; - prepare_options_buf(&public->cert->critical); + if (v00) { + prepare_options_buf(&public->cert->critical, + OPTIONS_CRITICAL|OPTIONS_EXTENSIONS); + } else { + prepare_options_buf(&public->cert->critical, + OPTIONS_CRITICAL); + prepare_options_buf(&public->cert->extensions, + OPTIONS_EXTENSIONS); + } public->cert->signature_key = key_from_private(ca); if (key_certify(public, ca) != 0) @@ -1354,43 +1370,43 @@ add_cert_option(char *opt) char *val; if (strcmp(opt, "clear") == 0) - critical_flags = 0; + certflags_flags = 0; else if (strcasecmp(opt, "no-x11-forwarding") == 0) - critical_flags &= ~CRITOPT_X_FWD; + certflags_flags &= ~CERTOPT_X_FWD; else if (strcasecmp(opt, "permit-x11-forwarding") == 0) - critical_flags |= CRITOPT_X_FWD; + certflags_flags |= CERTOPT_X_FWD; else if (strcasecmp(opt, "no-agent-forwarding") == 0) - critical_flags &= ~CRITOPT_AGENT_FWD; + certflags_flags &= ~CERTOPT_AGENT_FWD; else if (strcasecmp(opt, "permit-agent-forwarding") == 0) - critical_flags |= CRITOPT_AGENT_FWD; + certflags_flags |= CERTOPT_AGENT_FWD; else if (strcasecmp(opt, "no-port-forwarding") == 0) - critical_flags &= ~CRITOPT_PORT_FWD; + certflags_flags &= ~CERTOPT_PORT_FWD; else if (strcasecmp(opt, "permit-port-forwarding") == 0) - critical_flags |= CRITOPT_PORT_FWD; + certflags_flags |= CERTOPT_PORT_FWD; else if (strcasecmp(opt, "no-pty") == 0) - critical_flags &= ~CRITOPT_PTY; + certflags_flags &= ~CERTOPT_PTY; else if (strcasecmp(opt, "permit-pty") == 0) - critical_flags |= CRITOPT_PTY; + certflags_flags |= CERTOPT_PTY; else if (strcasecmp(opt, "no-user-rc") == 0) - critical_flags &= ~CRITOPT_USER_RC; + certflags_flags &= ~CERTOPT_USER_RC; else if (strcasecmp(opt, "permit-user-rc") == 0) - critical_flags |= CRITOPT_USER_RC; + certflags_flags |= CERTOPT_USER_RC; else if (strncasecmp(opt, "force-command=", 14) == 0) { val = opt + 14; if (*val == '\0') fatal("Empty force-command option"); - if (critical_command != NULL) + if (certflags_command != NULL) fatal("force-command already specified"); - critical_command = xstrdup(val); + certflags_command = xstrdup(val); } else if (strncasecmp(opt, "source-address=", 15) == 0) { val = opt + 15; if (*val == '\0') fatal("Empty source-address option"); - if (critical_src_addr != NULL) + if (certflags_src_addr != NULL) fatal("source-address already specified"); if (addr_match_cidr_list(NULL, val) != 0) fatal("Invalid source-address list"); - critical_src_addr = xstrdup(val); + certflags_src_addr = xstrdup(val); } else fatal("Unsupported certificate option \"%s\"", opt); } @@ -1667,7 +1683,7 @@ main(int argc, char **argv) break; case 'h': cert_key_type = SSH2_CERT_TYPE_HOST; - critical_flags = 0; + certflags_flags = 0; break; case 'i': case 'X': -- cgit v1.2.3