[openssh-commits] [openssh] 02/10: upstream commit

git+noreply at mindrot.org git+noreply at mindrot.org
Tue Oct 6 12:29:12 AEDT 2015


This is an automated email from the git hooks/post-receive script.

djm pushed a commit to branch master
in repository openssh.

commit 4e44a79a07d4b88b6a4e5e8c1bed5f58c841b1b8
Author: djm at openbsd.org <djm at openbsd.org>
Date:   Thu Sep 24 06:15:11 2015 +0000

    upstream commit
    
    add ssh_config CertificateFile option to explicitly list
     a certificate; patch from Meghana Bhat on bz#2436; ok markus@
    
    Upstream-ID: 58648ec53c510b41c1f46d8fe293aadc87229ab8
---
 readconf.c    | 47 +++++++++++++++++++++++++++++++++++++++++-
 readconf.h    |  8 +++++++-
 ssh.1         |  8 ++++++--
 ssh.c         | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++-------
 ssh.h         |  8 +++++++-
 ssh_config.5  | 54 +++++++++++++++++++++++++++++++++++++++++++++----
 sshconnect2.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++---------
 7 files changed, 226 insertions(+), 25 deletions(-)

diff --git a/readconf.c b/readconf.c
index 354e292..09888b1 100644
--- a/readconf.c
+++ b/readconf.c
@@ -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 at cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo at 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;
diff --git a/readconf.h b/readconf.h
index bb2d552..6d6927f 100644
--- a/readconf.h
+++ b/readconf.h
@@ -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 at 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 */
diff --git a/ssh.1 b/ssh.1
index 495b787..b08fa79 100644
--- a/ssh.1
+++ b/ssh.1
@@ -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
diff --git a/ssh.c b/ssh.c
index 91911d3..a6e4de3 100644
--- a/ssh.c
+++ b/ssh.c
@@ -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 at cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo at 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));
diff --git a/ssh.h b/ssh.h
index 39c7e18..80eaeb3 100644
--- a/ssh.h
+++ b/ssh.h
@@ -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 at 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 54c42ab..39cf932 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 7751031..e821883 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)

-- 
To stop receiving notification emails like this one, please contact
djm at mindrot.org.


More information about the openssh-commits mailing list