diff options
author | djm@openbsd.org <djm@openbsd.org> | 2015-09-24 06:15:11 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2015-10-06 12:21:54 +1100 |
commit | 4e44a79a07d4b88b6a4e5e8c1bed5f58c841b1b8 (patch) | |
tree | 7ef647dabf413a83da2f0c26917a8e0b5e1d2145 | |
parent | e3cbb06ade83c72b640a53728d362bbefa0008e2 (diff) | |
download | openssh-git-4e44a79a07d4b88b6a4e5e8c1bed5f58c841b1b8.tar.gz |
upstream commit
add ssh_config CertificateFile option to explicitly list
a certificate; patch from Meghana Bhat on bz#2436; ok markus@
Upstream-ID: 58648ec53c510b41c1f46d8fe293aadc87229ab8
-rw-r--r-- | readconf.c | 47 | ||||
-rw-r--r-- | readconf.h | 8 | ||||
-rw-r--r-- | ssh.1 | 8 | ||||
-rw-r--r-- | ssh.c | 65 | ||||
-rw-r--r-- | ssh.h | 8 | ||||
-rw-r--r-- | ssh_config.5 | 54 | ||||
-rw-r--r-- | sshconnect2.c | 61 |
7 files changed, 226 insertions, 25 deletions
@@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.c,v 1.240 2015/08/21 23:53:08 djm Exp $ */ +/* $OpenBSD: readconf.c,v 1.241 2015/09/24 06:15:11 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -135,6 +135,7 @@ typedef enum { oPasswordAuthentication, oRSAAuthentication, oChallengeResponseAuthentication, oXAuthLocation, oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward, + oCertificateFile, oUser, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand, oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts, oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, @@ -202,6 +203,7 @@ static struct { { "identityfile", oIdentityFile }, { "identityfile2", oIdentityFile }, /* obsolete */ { "identitiesonly", oIdentitiesOnly }, + { "certificatefile", oCertificateFile }, { "hostname", oHostName }, { "hostkeyalias", oHostKeyAlias }, { "proxycommand", oProxyCommand }, @@ -366,6 +368,30 @@ clear_forwardings(Options *options) } void +add_certificate_file(Options *options, const char *path, int userprovided) +{ + int i; + + if (options->num_certificate_files >= SSH_MAX_CERTIFICATE_FILES) + fatal("Too many certificate files specified (max %d)", + SSH_MAX_CERTIFICATE_FILES); + + /* Avoid registering duplicates */ + for (i = 0; i < options->num_certificate_files; i++) { + if (options->certificate_file_userprovided[i] == userprovided && + strcmp(options->certificate_files[i], path) == 0) { + debug2("%s: ignoring duplicate key %s", __func__, path); + return; + } + } + + options->certificate_file_userprovided[options->num_certificate_files] = + userprovided; + options->certificate_files[options->num_certificate_files++] = + xstrdup(path); +} + +void add_identity_file(Options *options, const char *dir, const char *filename, int userprovided) { @@ -981,6 +1007,24 @@ parse_time: } break; + case oCertificateFile: + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing argument.", + filename, linenum); + if (*activep) { + intptr = &options->num_certificate_files; + if (*intptr >= SSH_MAX_CERTIFICATE_FILES) { + fatal("%.200s line %d: Too many certificate " + "files specified (max %d).", + filename, linenum, + SSH_MAX_CERTIFICATE_FILES); + } + add_certificate_file(options, arg, + flags & SSHCONF_USERCONF); + } + break; + case oXAuthLocation: charptr=&options->xauth_location; goto parse_string; @@ -1625,6 +1669,7 @@ initialize_options(Options * options) options->hostkeyalgorithms = NULL; options->protocol = SSH_PROTO_UNKNOWN; options->num_identity_files = 0; + options->num_certificate_files = 0; options->hostname = NULL; options->host_key_alias = NULL; options->proxy_command = NULL; @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.h,v 1.110 2015/07/10 06:21:53 markus Exp $ */ +/* $OpenBSD: readconf.h,v 1.111 2015/09/24 06:15:11 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> @@ -95,6 +95,11 @@ typedef struct { int identity_file_userprovided[SSH_MAX_IDENTITY_FILES]; struct sshkey *identity_keys[SSH_MAX_IDENTITY_FILES]; + int num_certificate_files; /* Number of extra certificates for ssh. */ + char *certificate_files[SSH_MAX_CERTIFICATE_FILES]; + int certificate_file_userprovided[SSH_MAX_CERTIFICATE_FILES]; + struct sshkey *certificates[SSH_MAX_CERTIFICATE_FILES]; + /* Local TCP/IP forward requests. */ int num_local_forwards; struct Forward *local_forwards; @@ -194,5 +199,6 @@ void dump_client_config(Options *o, const char *host); void add_local_forward(Options *, const struct Forward *); void add_remote_forward(Options *, const struct Forward *); void add_identity_file(Options *, const char *, const char *, int); +void add_certificate_file(Options *, const char *, int); #endif /* READCONF_H */ @@ -33,8 +33,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: ssh.1,v 1.362 2015/09/11 03:42:32 djm Exp $ -.Dd $Mdocdate: September 11 2015 $ +.\" $OpenBSD: ssh.1,v 1.363 2015/09/24 06:15:11 djm Exp $ +.Dd $Mdocdate: September 24 2015 $ .Dt SSH 1 .Os .Sh NAME @@ -304,6 +304,9 @@ It is possible to have multiple .Fl i options (and multiple identities specified in configuration files). +If no certificates have been explicitly specified by +.Cm CertificateFile +directive, .Nm will also try to load certificate information from the filename obtained by appending @@ -468,6 +471,7 @@ For full details of the options listed below, and their possible values, see .It CanonicalizeHostname .It CanonicalizeMaxDots .It CanonicalizePermittedCNAMEs +.It CertificateFile .It ChallengeResponseAuthentication .It CheckHostIP .It Cipher @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.425 2015/09/11 06:55:46 jmc Exp $ */ +/* $OpenBSD: ssh.c,v 1.426 2015/09/24 06:15:11 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -1354,6 +1354,10 @@ main(int ac, char **av) options.identity_keys[i] = NULL; } } + for (i = 0; i < options.num_certificate_files; i++) { + free(options.certificate_files[i]); + options.certificate_files[i] = NULL; + } exit_status = compat20 ? ssh_session2() : ssh_session(); packet_close(); @@ -1940,25 +1944,30 @@ ssh_session2(void) options.escape_char : SSH_ESCAPECHAR_NONE, id); } +/* Loads all IdentityFile and CertificateFile keys */ static void load_public_identity_files(void) { char *filename, *cp, thishost[NI_MAXHOST]; char *pwdir = NULL, *pwname = NULL; - int i = 0; Key *public; struct passwd *pw; - u_int n_ids; + int i; + u_int n_ids, n_certs; char *identity_files[SSH_MAX_IDENTITY_FILES]; Key *identity_keys[SSH_MAX_IDENTITY_FILES]; + char *certificate_files[SSH_MAX_CERTIFICATE_FILES]; + struct sshkey *certificates[SSH_MAX_CERTIFICATE_FILES]; #ifdef ENABLE_PKCS11 Key **keys; int nkeys; #endif /* PKCS11 */ - n_ids = 0; + n_ids = n_certs = 0; memset(identity_files, 0, sizeof(identity_files)); memset(identity_keys, 0, sizeof(identity_keys)); + memset(certificate_files, 0, sizeof(certificate_files)); + memset(certificates, 0, sizeof(certificates)); #ifdef ENABLE_PKCS11 if (options.pkcs11_provider != NULL && @@ -1990,6 +1999,7 @@ load_public_identity_files(void) if (n_ids >= SSH_MAX_IDENTITY_FILES || strcasecmp(options.identity_files[i], "none") == 0) { free(options.identity_files[i]); + options.identity_files[i] = NULL; continue; } cp = tilde_expand_filename(options.identity_files[i], @@ -2008,7 +2018,12 @@ load_public_identity_files(void) if (++n_ids >= SSH_MAX_IDENTITY_FILES) continue; - /* Try to add the certificate variant too */ + /* + * If no certificates have been explicitly listed then try + * to add the default certificate variant too. + */ + if (options.num_certificate_files != 0) + continue; xasprintf(&cp, "%s-cert", filename); public = key_load_public(cp, NULL); debug("identity file %s type %d", cp, @@ -2025,14 +2040,50 @@ load_public_identity_files(void) continue; } identity_keys[n_ids] = public; - /* point to the original path, most likely the private key */ - identity_files[n_ids] = xstrdup(filename); + identity_files[n_ids] = cp; n_ids++; } + + if (options.num_certificate_files > SSH_MAX_CERTIFICATE_FILES) + fatal("%s: too many certificates", __func__); + for (i = 0; i < options.num_certificate_files; i++) { + cp = tilde_expand_filename(options.certificate_files[i], + original_real_uid); + filename = percent_expand(cp, "d", pwdir, + "u", pwname, "l", thishost, "h", host, + "r", options.user, (char *)NULL); + free(cp); + + public = key_load_public(filename, NULL); + debug("certificate file %s type %d", filename, + public ? public->type : -1); + free(options.certificate_files[i]); + options.certificate_files[i] = NULL; + if (public == NULL) { + free(filename); + continue; + } + if (!key_is_cert(public)) { + debug("%s: key %s type %s is not a certificate", + __func__, filename, key_type(public)); + key_free(public); + free(filename); + continue; + } + certificate_files[n_certs] = filename; + certificates[n_certs] = public; + ++n_certs; + } + options.num_identity_files = n_ids; memcpy(options.identity_files, identity_files, sizeof(identity_files)); memcpy(options.identity_keys, identity_keys, sizeof(identity_keys)); + options.num_certificate_files = n_certs; + memcpy(options.certificate_files, + certificate_files, sizeof(certificate_files)); + memcpy(options.certificates, certificates, sizeof(certificates)); + explicit_bzero(pwname, strlen(pwname)); free(pwname); explicit_bzero(pwdir, strlen(pwdir)); @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.h,v 1.81 2015/08/04 05:23:06 djm Exp $ */ +/* $OpenBSD: ssh.h,v 1.82 2015/09/24 06:15:11 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> @@ -19,6 +19,12 @@ #define SSH_DEFAULT_PORT 22 /* + * Maximum number of certificate files that can be specified + * in configuration files or on the command line. + */ +#define SSH_MAX_CERTIFICATE_FILES 100 + +/* * Maximum number of RSA authentication identity files that can be specified * in configuration files or on the command line. */ diff --git a/ssh_config.5 b/ssh_config.5 index 54c42ab8..39cf932d 100644 --- a/ssh_config.5 +++ b/ssh_config.5 @@ -33,8 +33,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: ssh_config.5,v 1.220 2015/09/22 08:33:23 sobrado Exp $ -.Dd $Mdocdate: September 22 2015 $ +.\" $OpenBSD: ssh_config.5,v 1.221 2015/09/24 06:15:11 djm Exp $ +.Dd $Mdocdate: September 24 2015 $ .Dt SSH_CONFIG 5 .Os .Sh NAME @@ -325,6 +325,41 @@ to be canonicalized to names in the or .Dq *.c.example.com domains. +.It Cm CertificateFile +Specifies a file from which the user's certificate is read. +A corresponding private key must be provided separately in order +to use this certificate either +from an +.Cm IdentityFile +directive or +.Fl i +flag to +.Xr ssh 1 , +via +.Xr ssh-agent 1 , +or via a +.Cm PKCS11Provider . +.Pp +The file name may use the tilde +syntax to refer to a user's home directory or one of the following +escape characters: +.Ql %d +(local user's home directory), +.Ql %u +(local user name), +.Ql %l +(local host name), +.Ql %h +(remote host name) or +.Ql %r +(remote user name). +.Pp +It is possible to have multiple certificate files specified in +configuration files; these certificates will be tried in sequence. +Multiple +.Cm CertificateFile +directives will add to the list of certificates used for +authentication. .It Cm ChallengeResponseAuthentication Specifies whether to use challenge-response authentication. The argument to this keyword must be @@ -869,9 +904,13 @@ specifications). .It Cm IdentitiesOnly Specifies that .Xr ssh 1 -should only use the authentication identity files configured in the +should only use the authentication identity and certificate files explicitly +configured in the .Nm -files, +files +or passed on the +.Xr ssh 1 +command-line, even if .Xr ssh-agent 1 or a @@ -901,6 +940,8 @@ Additionally, any identities represented by the authentication agent will be used for authentication unless .Cm IdentitiesOnly is set. +If no certificates have been explicitly specified by +.Cm CertificateFile , .Xr ssh 1 will try to load certificate information from the filename obtained by appending @@ -934,6 +975,11 @@ differs from that of other configuration directives). may be used in conjunction with .Cm IdentitiesOnly to select which identities in an agent are offered during authentication. +.Cm IdentityFile +may also be used in conjunction with +.Cm CertificateFile +in order to provide any certificate also needed for authentication with +the identity. .It Cm IgnoreUnknown Specifies a pattern-list of unknown options to be ignored if they are encountered in configuration parsing. diff --git a/sshconnect2.c b/sshconnect2.c index 77510318..e8218839 100644 --- a/sshconnect2.c +++ b/sshconnect2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect2.c,v 1.226 2015/07/30 00:01:34 djm Exp $ */ +/* $OpenBSD: sshconnect2.c,v 1.227 2015/09/24 06:15:11 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2008 Damien Miller. All rights reserved. @@ -1001,18 +1001,17 @@ static int sign_and_send_pubkey(Authctxt *authctxt, Identity *id) { Buffer b; + Identity *private_id; u_char *blob, *signature; - u_int bloblen; size_t slen; - u_int skip = 0; - int ret = -1; - int have_sig = 1; + u_int bloblen, skip = 0; + int matched, ret = -1, have_sig = 1; char *fp; if ((fp = sshkey_fingerprint(id->key, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) return 0; - debug3("sign_and_send_pubkey: %s %s", key_type(id->key), fp); + debug3("%s: %s %s", __func__, key_type(id->key), fp); free(fp); if (key_to_blob(id->key, &blob, &bloblen) == 0) { @@ -1044,6 +1043,36 @@ sign_and_send_pubkey(Authctxt *authctxt, Identity *id) } buffer_put_string(&b, blob, bloblen); + /* + * If the key is an certificate, try to find a matching private key + * and use it to complete the signature. + * If no such private key exists, return failure and continue with + * other methods of authentication. + */ + if (key_is_cert(id->key)) { + matched = 0; + TAILQ_FOREACH(private_id, &authctxt->keys, next) { + if (sshkey_equal_public(id->key, private_id->key) && + id->key->type != private_id->key->type) { + id = private_id; + matched = 1; + break; + } + } + if (matched) { + debug2("%s: using private key \"%s\"%s for " + "certificate", __func__, id->filename, + id->agent_fd != -1 ? " from agent" : ""); + } else { + /* XXX maybe verbose/error? */ + debug("%s: no private key for certificate " + "\"%s\"", __func__, id->filename); + free(blob); + buffer_free(&b); + return 0; + } + } + /* generate signature */ ret = identity_sign(id, &signature, &slen, buffer_ptr(&b), buffer_len(&b), datafellows); @@ -1180,9 +1209,11 @@ load_identity_file(char *filename, int userprovided) /* * try keys in the following order: - * 1. agent keys that are found in the config file - * 2. other agent keys - * 3. keys that are only listed in the config file + * 1. certificates listed in the config file + * 2. other input certificates + * 3. agent keys that are found in the config file + * 4. other agent keys + * 5. keys that are only listed in the config file */ static void pubkey_prepare(Authctxt *authctxt) @@ -1236,6 +1267,18 @@ pubkey_prepare(Authctxt *authctxt) free(id); } } + /* list of certificates specified by user */ + for (i = 0; i < options.num_certificate_files; i++) { + key = options.certificates[i]; + if (!key_is_cert(key) || key->cert == NULL || + key->cert->type != SSH2_CERT_TYPE_USER) + continue; + id = xcalloc(1, sizeof(*id)); + id->key = key; + id->filename = xstrdup(options.certificate_files[i]); + id->userprovided = options.certificate_file_userprovided[i]; + TAILQ_INSERT_TAIL(preferred, id, next); + } /* list of keys supported by the agent */ if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) { if (r != SSH_ERR_AGENT_NOT_PRESENT) |