[openssh-commits] [openssh] 07/17: upstream: ssh-agent side of destination constraints

git+noreply at mindrot.org git+noreply at mindrot.org
Mon Dec 20 09:28:32 AEDT 2021


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

djm pushed a commit to branch master
in repository openssh.

commit 39f00dcf44915f20684160f0a88d3ef8a3278351
Author: djm at openbsd.org <djm at openbsd.org>
Date:   Sun Dec 19 22:11:39 2021 +0000

    upstream: ssh-agent side of destination constraints
    
    Gives ssh-agent the ability to parse restrict-destination-v00 at openssh.com
    constraints and to apply them to keys.
    
    Check constraints against the hostkeys recorded for a SocketEntry when
    attempting a signature, adding, listing or deleting keys. Note that
    the "delete all keys" request will remove constrained keys regardless of
    location.
    
    feedback Jann Horn & markus@
    ok markus@
    
    OpenBSD-Commit-ID: 84a7fb81106c2d609a6ac17469436df16d196319
---
 ssh-agent.c | 505 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 463 insertions(+), 42 deletions(-)

diff --git a/ssh-agent.c b/ssh-agent.c
index 568bbf34..346dae3d 100644
--- a/ssh-agent.c
+++ b/ssh-agent.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-agent.c,v 1.280 2021/12/19 22:09:23 djm Exp $ */
+/* $OpenBSD: ssh-agent.c,v 1.281 2021/12/19 22:11:39 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo at cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
@@ -92,6 +92,7 @@
 #include "pathnames.h"
 #include "ssh-pkcs11.h"
 #include "sk-api.h"
+#include "myproposal.h"
 
 #ifndef DEFAULT_ALLOWED_PROVIDERS
 # define DEFAULT_ALLOWED_PROVIDERS "/usr/lib*/*,/usr/local/lib*/*"
@@ -105,6 +106,8 @@
 #define AGENT_MAX_SESSION_IDS		16
 /* Maximum size of session ID */
 #define AGENT_MAX_SID_LEN		128
+/* Maximum number of destination constraints to accept on a key */
+#define AGENT_MAX_DEST_CONSTRAINTS	1024
 
 /* XXX store hostkey_sid in a refcounted tree */
 
@@ -141,6 +144,8 @@ typedef struct identity {
 	time_t death;
 	u_int confirm;
 	char *sk_provider;
+	struct dest_constraint *dest_constraints;
+	size_t ndest_constraints;
 } Identity;
 
 struct idtable {
@@ -212,6 +217,33 @@ idtab_init(void)
 	idtab->nentries = 0;
 }
 
+static void
+free_dest_constraint_hop(struct dest_constraint_hop *dch)
+{
+	u_int i;
+
+	if (dch == NULL)
+		return;
+	free(dch->user);
+	free(dch->hostname);
+	for (i = 0; i < dch->nkeys; i++)
+		sshkey_free(dch->keys[i]);
+	free(dch->keys);
+	free(dch->key_is_ca);
+}
+
+static void
+free_dest_constraints(struct dest_constraint *dcs, size_t ndcs)
+{
+	size_t i;
+
+	for (i = 0; i < ndcs; i++) {
+		free_dest_constraint_hop(&dcs[i].from);
+		free_dest_constraint_hop(&dcs[i].to);
+	}
+	free(dcs);
+}
+
 static void
 free_identity(Identity *id)
 {
@@ -219,9 +251,220 @@ free_identity(Identity *id)
 	free(id->provider);
 	free(id->comment);
 	free(id->sk_provider);
+	free_dest_constraints(id->dest_constraints, id->ndest_constraints);
 	free(id);
 }
 
+/*
+ * Match 'key' against the key/CA list in a destination constraint hop
+ * Returns 0 on success or -1 otherwise.
+ */
+static int
+match_key_hop(const char *tag, const struct sshkey *key,
+    const struct dest_constraint_hop *dch)
+{
+	const char *reason = NULL;
+	u_int i;
+	char *fp;
+
+	if (key == NULL)
+		return -1;
+	/* XXX logspam */
+	if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT,
+	    SSH_FP_DEFAULT)) == NULL)
+		fatal_f("fingerprint failed");
+	debug3_f("%s: entering hostname %s, requested key %s %s, %u keys avail",
+	    tag, dch->hostname, sshkey_type(key), fp, dch->nkeys);
+	free(fp);
+	for (i = 0; i < dch->nkeys; i++) {
+		if (dch->keys[i] == NULL)
+			return -1;
+		/* XXX logspam */
+		if ((fp = sshkey_fingerprint(dch->keys[i], SSH_FP_HASH_DEFAULT,
+		    SSH_FP_DEFAULT)) == NULL)
+			fatal_f("fingerprint failed");
+		debug3_f("%s: key %u: %s%s %s", tag, i,
+		    dch->key_is_ca[i] ? "CA " : "",
+		    sshkey_type(dch->keys[i]), fp);
+		free(fp);
+		if (!sshkey_is_cert(key)) {
+			/* plain key */
+			if (dch->key_is_ca[i] ||
+			    !sshkey_equal(key, dch->keys[i]))
+				continue;
+			return 0;
+		}
+		/* certificate */
+		if (!dch->key_is_ca[i])
+			continue;
+		if (key->cert == NULL || key->cert->signature_key == NULL)
+			return -1; /* shouldn't happen */
+		if (!sshkey_equal(key->cert->signature_key, dch->keys[i]))
+			continue;
+		if (sshkey_cert_check_host(key, dch->hostname, 1,
+		    SSH_ALLOWED_CA_SIGALGS, &reason) != 0) {
+			debug_f("cert %s / hostname %s rejected: %s",
+			    key->cert->key_id, dch->hostname, reason);
+			continue;
+		}
+		return 0;
+	}
+	return -1;
+}
+
+/* Check destination constraints on an identity against the hostkey/user */
+static int
+permitted_by_dest_constraints(const struct sshkey *fromkey,
+    const struct sshkey *tokey, Identity *id, const char *user,
+    const char **hostnamep)
+{
+	size_t i;
+	struct dest_constraint *d;
+
+	if (hostnamep != NULL)
+		*hostnamep = NULL;
+	for (i = 0; i < id->ndest_constraints; i++) {
+		d = id->dest_constraints + i;
+		/* XXX remove logspam */
+		debug2_f("constraint %zu %s%s%s (%u keys) > %s%s%s (%u keys)",
+		    i, d->from.user ? d->from.user : "",
+		    d->from.user ? "@" : "",
+		    d->from.hostname ? d->from.hostname : "(ORIGIN)",
+		    d->from.nkeys,
+		    d->to.user ? d->to.user : "", d->to.user ? "@" : "",
+		    d->to.hostname ? d->to.hostname : "(ANY)", d->to.nkeys);
+
+		/* Match 'from' key */
+		if (fromkey == NULL) {
+			/* We are matching the first hop */
+			if (d->from.hostname != NULL || d->from.nkeys != 0)
+				continue;
+		} else if (match_key_hop("from", fromkey, &d->from) != 0)
+			continue;
+
+		/* Match 'to' key */
+		if (tokey != NULL && match_key_hop("to", tokey, &d->to) != 0)
+			continue;
+
+		/* Match user if specified */
+		if (d->to.user != NULL && user != NULL &&
+		    !match_pattern(user, d->to.user))
+			continue;
+
+		/* successfully matched this constraint */
+		if (hostnamep != NULL)
+			*hostnamep = d->to.hostname;
+		debug2_f("allowed for hostname %s",
+		    d->to.hostname == NULL ? "*" : d->to.hostname);
+		return 0;
+	}
+	/* no match */
+	debug2_f("%s identity \"%s\" not permitted for this destination",
+	    sshkey_type(id->key), id->comment);
+	return -1;
+}
+
+/*
+ * Check whether hostkeys on a SocketEntry and the optionally specified user
+ * are permitted by the destination constraints on the Identity.
+ * Returns 0 on success or -1 otherwise.
+ */
+static int
+identity_permitted(Identity *id, SocketEntry *e, char *user,
+    const char **forward_hostnamep, const char **last_hostnamep)
+{
+	size_t i;
+	const char **hp;
+	struct hostkey_sid *hks;
+	const struct sshkey *fromkey = NULL;
+	const char *test_user;
+	char *fp1, *fp2;
+
+	/* XXX remove logspam */
+	debug3_f("entering: key %s comment \"%s\", %zu socket bindings, "
+	    "%zu constraints", sshkey_type(id->key), id->comment,
+	    e->nsession_ids, id->ndest_constraints);
+	if (id->ndest_constraints == 0)
+		return 0; /* unconstrained */
+	if (e->nsession_ids == 0)
+		return 0; /* local use */
+	/*
+	 * Walk through the hops recorded by session_id and try to find a
+	 * constraint that satisfies each.
+	 */
+	for (i = 0; i < e->nsession_ids; i++) {
+		hks = e->session_ids + i;
+		if (hks->key == NULL)
+			fatal_f("internal error: no bound key");
+		/* XXX remove logspam */
+		fp1 = fp2 = NULL;
+		if (fromkey != NULL &&
+		    (fp1 = sshkey_fingerprint(fromkey, SSH_FP_HASH_DEFAULT,
+		    SSH_FP_DEFAULT)) == NULL)
+			fatal_f("fingerprint failed");
+		if ((fp2 = sshkey_fingerprint(hks->key, SSH_FP_HASH_DEFAULT,
+		    SSH_FP_DEFAULT)) == NULL)
+			fatal_f("fingerprint failed");
+		debug3_f("socketentry fd=%d, entry %zu %s, "
+		    "from hostkey %s %s to user %s hostkey %s %s",
+		    e->fd, i, hks->forwarded ? "FORWARD" : "AUTH",
+		    fromkey ? sshkey_type(fromkey) : "(ORIGIN)",
+		    fromkey ? fp1 : "", user ? user : "(ANY)",
+		    sshkey_type(hks->key), fp2);
+		free(fp1);
+		free(fp2);
+		/*
+		 * Record the hostnames for the initial forwarding and
+		 * the final destination.
+		 */
+		hp = NULL;
+		if (i == e->nsession_ids - 1)
+			hp = last_hostnamep;
+		else if (i == 0)
+			hp = forward_hostnamep;
+		/* Special handling for final recorded binding */
+		test_user = NULL;
+		if (i == e->nsession_ids - 1) {
+			/* Can only check user at final hop */
+			test_user = user;
+			/*
+			 * user is only presented for signature requests.
+			 * If this is the case, make sure last binding is not
+			 * for a forwarding.
+			 */
+			if (hks->forwarded && user != NULL) {
+				error_f("tried to sign on forwarding hop");
+				return -1;
+			}
+		} else if (!hks->forwarded) {
+			error_f("tried to forward though signing bind");
+			return -1;
+		}
+		if (permitted_by_dest_constraints(fromkey, hks->key, id,
+		    test_user, hp) != 0)
+			return -1;
+		fromkey = hks->key;
+	}
+	/*
+	 * Another special case: if the last bound session ID was for a
+	 * forwarding, and this function is not being called to check a sign
+	 * request (i.e. no 'user' supplied), then only permit the key if
+	 * there is a permission that would allow it to be used at another
+	 * destination. This hides keys that are allowed to be used to
+	 * authenicate *to* a host but not permitted for *use* beyond it.
+	 */
+	hks = &e->session_ids[e->nsession_ids - 1];
+	if (hks->forwarded && user == NULL &&
+	    permitted_by_dest_constraints(hks->key, NULL, id,
+	    NULL, NULL) != 0) {
+		debug3_f("key permitted at host but not after");
+		return -1;
+	}
+
+	/* success */
+	return 0;
+}
+
 /* return matching private key for given public key */
 static Identity *
 lookup_identity(struct sshkey *key)
@@ -269,27 +512,36 @@ static void
 process_request_identities(SocketEntry *e)
 {
 	Identity *id;
-	struct sshbuf *msg;
+	struct sshbuf *msg, *keys;
 	int r;
+	u_int nentries = 0;
 
 	debug2_f("entering");
 
-	if ((msg = sshbuf_new()) == NULL)
+	if ((msg = sshbuf_new()) == NULL || (keys = sshbuf_new()) == NULL)
 		fatal_f("sshbuf_new failed");
-	if ((r = sshbuf_put_u8(msg, SSH2_AGENT_IDENTITIES_ANSWER)) != 0 ||
-	    (r = sshbuf_put_u32(msg, idtab->nentries)) != 0)
-		fatal_fr(r, "compose");
 	TAILQ_FOREACH(id, &idtab->idlist, next) {
-		if ((r = sshkey_puts_opts(id->key, msg,
+		/* identity not visible, don't include in response */
+		if (identity_permitted(id, e, NULL, NULL, NULL) != 0)
+			continue;
+		if ((r = sshkey_puts_opts(id->key, keys,
 		    SSHKEY_SERIALIZE_INFO)) != 0 ||
-		    (r = sshbuf_put_cstring(msg, id->comment)) != 0) {
+		    (r = sshbuf_put_cstring(keys, id->comment)) != 0) {
 			error_fr(r, "compose key/comment");
 			continue;
 		}
+		nentries++;
 	}
+	debug2_f("replying with %u allowed of %u available keys",
+	    nentries, idtab->nentries);
+	if ((r = sshbuf_put_u8(msg, SSH2_AGENT_IDENTITIES_ANSWER)) != 0 ||
+	    (r = sshbuf_put_u32(msg, nentries)) != 0 ||
+	    (r = sshbuf_putb(msg, keys)) != 0)
+		fatal_fr(r, "compose");
 	if ((r = sshbuf_put_stringb(e->output, msg)) != 0)
 		fatal_fr(r, "enqueue");
 	sshbuf_free(msg);
+	sshbuf_free(keys);
 }
 
 
@@ -460,10 +712,11 @@ static void
 process_sign_request2(SocketEntry *e)
 {
 	u_char *signature = NULL;
-	size_t i, slen = 0;
+	size_t slen = 0;
 	u_int compat = 0, flags;
 	int r, ok = -1;
 	char *fp = NULL, *user = NULL, *sig_dest = NULL;
+	const char *fwd_host = NULL, *dest_host = NULL;
 	struct sshbuf *msg = NULL, *data = NULL, *sid = NULL;
 	struct sshkey *key = NULL;
 	struct identity *id;
@@ -484,31 +737,41 @@ process_sign_request2(SocketEntry *e)
 		verbose_f("%s key not found", sshkey_type(key));
 		goto send;
 	}
-	/*
-	 * If session IDs were recorded for this socket, then use them to
-	 * annotate the confirmation messages with the host keys.
-	 */
-	if (e->nsession_ids > 0 &&
-	    parse_userauth_request(data, key, &user, &sid) == 0) {
+	if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT,
+	    SSH_FP_DEFAULT)) == NULL)
+		fatal_f("fingerprint failed");
+
+	if (id->ndest_constraints != 0) {
+		if (e->nsession_ids == 0) {
+			logit_f("refusing use of destination-constrained key "
+			    "to sign on unbound connection");
+			goto send;
+		}
+		if (parse_userauth_request(data, key, &user, &sid) != 0) {
+			logit_f("refusing use of destination-constrained key "
+			   "to sign an unidentified signature");
+			goto send;
+		}
+		/* XXX logspam */
+		debug_f("user=%s", user);
+		if (identity_permitted(id, e, user, &fwd_host, &dest_host) != 0)
+			goto send;
+		/* XXX display fwd_host/dest_host in askpass UI */
 		/*
-		 * session ID from userauth request should match the final
-		 * ID in the list recorded in the socket, unless the ssh
-		 * client at that point lacks the binding extension (or if
-		 * an attacker is trying to steal use of the agent).
+		 * Ensure that the session ID is the most recent one
+		 * registered on the socket - it should have been bound by
+		 * ssh immediately before userauth.
 		 */
-		i = e->nsession_ids - 1;
-		if (buf_equal(sid, e->session_ids[i].sid) == 0) {
-			if ((fp = sshkey_fingerprint(e->session_ids[i].key,
-			    SSH_FP_HASH_DEFAULT, SSH_FP_DEFAULT)) == NULL)
-				fatal_f("fingerprint failed");
-			debug3_f("destination %s %s (slot %zu)",
-			    sshkey_type(e->session_ids[i].key), fp, i);
-			xasprintf(&sig_dest, "public key request for "
-			    "target user \"%s\" to %s %s", user,
-			    sshkey_type(e->session_ids[i].key), fp);
-			free(fp);
-			fp = NULL;
+		if (buf_equal(sid,
+		    e->session_ids[e->nsession_ids - 1].sid) != 0) {
+			error_f("unexpected session ID (%zu listed) on "
+			    "signature request for target user %s with "
+			    "key %s %s", e->nsession_ids, user,
+			    sshkey_type(id->key), fp);
+			goto send;
 		}
+		xasprintf(&sig_dest, "public key authentication request for "
+		    "user \"%s\" to listed host", user);
 	}
 	if (id->confirm && confirm_key(id, sig_dest) != 0) {
 		verbose_f("user refused key");
@@ -521,9 +784,6 @@ process_sign_request2(SocketEntry *e)
 			goto send;
 		}
 		if ((id->key->sk_flags & SSH_SK_USER_PRESENCE_REQD)) {
-			if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT,
-			    SSH_FP_DEFAULT)) == NULL)
-				fatal_f("fingerprint failed");
 			notifier = notify_start(0,
 			    "Confirm user presence for key %s %s%s%s",
 			    sshkey_type(id->key), fp,
@@ -580,6 +840,9 @@ process_remove_identity(SocketEntry *e)
 		debug_f("key not found");
 		goto done;
 	}
+	/* identity not visible, cannot be removed */
+	if (identity_permitted(id, e, NULL, NULL, NULL) != 0)
+		goto done; /* error already logged */
 	/* We have this key, free it. */
 	if (idtab->nentries < 1)
 		fatal_f("internal error: nentries %d", idtab->nentries);
@@ -639,10 +902,119 @@ reaper(void)
 }
 
 static int
-parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp)
+parse_dest_constraint_hop(struct sshbuf *b, struct dest_constraint_hop *dch)
+{
+	u_char key_is_ca;
+	size_t elen = 0;
+	int r;
+	struct sshkey *k = NULL;
+	char *fp;
+
+	memset(dch, '\0', sizeof(*dch));
+	if ((r = sshbuf_get_cstring(b, &dch->user, NULL)) != 0 ||
+	    (r = sshbuf_get_cstring(b, &dch->hostname, NULL)) != 0 ||
+	    (r = sshbuf_get_string_direct(b, NULL, &elen)) != 0) {
+		error_fr(r, "parse");
+		goto out;
+	}
+	if (elen != 0) {
+		error_f("unsupported extensions (len %zu)", elen);
+		r = SSH_ERR_FEATURE_UNSUPPORTED;
+		goto out;
+	}
+	if (*dch->hostname == '\0') {
+		free(dch->hostname);
+		dch->hostname = NULL;
+	}
+	if (*dch->user == '\0') {
+		free(dch->user);
+		dch->user = NULL;
+	}
+	while (sshbuf_len(b) != 0) {
+		dch->keys = xrecallocarray(dch->keys, dch->nkeys,
+		    dch->nkeys + 1, sizeof(*dch->keys));
+		dch->key_is_ca = xrecallocarray(dch->key_is_ca, dch->nkeys,
+		    dch->nkeys + 1, sizeof(*dch->key_is_ca));
+		if ((r = sshkey_froms(b, &k)) != 0 ||
+		    (r = sshbuf_get_u8(b, &key_is_ca)) != 0)
+			goto out;
+		if ((fp = sshkey_fingerprint(k, SSH_FP_HASH_DEFAULT,
+		    SSH_FP_DEFAULT)) == NULL)
+			fatal_f("fingerprint failed");
+		debug3_f("%s%s%s: adding %skey %s %s",
+		    dch->user == NULL ? "" : dch->user,
+		    dch->user == NULL ? "" : "@",
+		    dch->hostname, key_is_ca ? "CA " : "", sshkey_type(k), fp);
+		free(fp);
+		dch->keys[dch->nkeys] = k;
+		dch->key_is_ca[dch->nkeys] = key_is_ca != 0;
+		dch->nkeys++;
+		k = NULL; /* transferred */
+	}
+	/* success */
+	r = 0;
+ out:
+	sshkey_free(k);
+	return r;
+}
+
+static int
+parse_dest_constraint(struct sshbuf *m, struct dest_constraint *dc)
+{
+	struct sshbuf *b = NULL, *frombuf = NULL, *tobuf = NULL;
+	int r;
+	size_t elen = 0;
+
+	debug3_f("entering");
+
+	memset(dc, '\0', sizeof(*dc));
+	if ((r = sshbuf_froms(m, &b)) != 0 ||
+	    (r = sshbuf_froms(b, &frombuf)) != 0 ||
+	    (r = sshbuf_froms(b, &tobuf)) != 0 ||
+	    (r = sshbuf_get_string_direct(b, NULL, &elen)) != 0) {
+		error_fr(r, "parse");
+		goto out;
+	}
+	if ((r = parse_dest_constraint_hop(frombuf, &dc->from) != 0) ||
+	    (r = parse_dest_constraint_hop(tobuf, &dc->to) != 0))
+		goto out; /* already logged */
+	if (elen != 0) {
+		error_f("unsupported extensions (len %zu)", elen);
+		r = SSH_ERR_FEATURE_UNSUPPORTED;
+		goto out;
+	}
+	debug2_f("parsed %s (%u keys) > %s%s%s (%u keys)",
+	    dc->from.hostname ? dc->from.hostname : "(ORIGIN)", dc->from.nkeys,
+	    dc->to.user ? dc->to.user : "", dc->to.user ? "@" : "",
+	    dc->to.hostname ? dc->to.hostname : "(ANY)", dc->to.nkeys);
+	/* check consistency */
+	if ((dc->from.hostname == NULL) != (dc->from.nkeys == 0) ||
+	    dc->from.user != NULL) {
+		error_f("inconsistent \"from\" specification");
+		r = SSH_ERR_INVALID_FORMAT;
+		goto out;
+	}
+	if (dc->to.hostname == NULL || dc->to.nkeys == 0) {
+		error_f("incomplete \"to\" specification");
+		r = SSH_ERR_INVALID_FORMAT;
+		goto out;
+	}
+	/* success */
+	r = 0;
+ out:
+	sshbuf_free(b);
+	sshbuf_free(frombuf);
+	sshbuf_free(tobuf);
+	return r;
+}
+
+static int
+parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp,
+    struct dest_constraint **dcsp, size_t *ndcsp)
 {
 	char *ext_name = NULL;
 	int r;
+	struct sshbuf *b = NULL;
 
 	if ((r = sshbuf_get_cstring(m, &ext_name, NULL)) != 0) {
 		error_fr(r, "parse constraint extension");
@@ -664,6 +1036,27 @@ parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp)
 			error_fr(r, "parse %s", ext_name);
 			goto out;
 		}
+	} else if (strcmp(ext_name,
+	    "restrict-destination-v00 at openssh.com") == 0) {
+		if (*dcsp != NULL) {
+			error_f("%s already set", ext_name);
+			goto out;
+		}
+		if ((r = sshbuf_froms(m, &b)) != 0) {
+			error_fr(r, "parse %s outer", ext_name);
+			goto out;
+		}
+		while (sshbuf_len(b) != 0) {
+			if (*ndcsp >= AGENT_MAX_DEST_CONSTRAINTS) {
+				error_f("too many %s constraints", ext_name);
+				goto out;
+			}
+			*dcsp = xrecallocarray(*dcsp, *ndcsp, *ndcsp + 1,
+			    sizeof(**dcsp));
+			if ((r = parse_dest_constraint(b,
+			    *dcsp + (*ndcsp)++)) != 0)
+				goto out; /* error already logged */
+		}
 	} else {
 		error_f("unsupported constraint \"%s\"", ext_name);
 		r = SSH_ERR_FEATURE_UNSUPPORTED;
@@ -673,12 +1066,14 @@ parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp)
 	r = 0;
  out:
 	free(ext_name);
+	sshbuf_free(b);
 	return r;
 }
 
 static int
 parse_key_constraints(struct sshbuf *m, struct sshkey *k, time_t *deathp,
-    u_int *secondsp, int *confirmp, char **sk_providerp)
+    u_int *secondsp, int *confirmp, char **sk_providerp,
+    struct dest_constraint **dcsp, size_t *ndcsp)
 {
 	u_char ctype;
 	int r;
@@ -733,7 +1128,7 @@ parse_key_constraints(struct sshbuf *m, struct sshkey *k, time_t *deathp,
 			break;
 		case SSH_AGENT_CONSTRAIN_EXTENSION:
 			if ((r = parse_key_constraint_extension(m,
-			    sk_providerp)) != 0)
+			    sk_providerp, dcsp, ndcsp)) != 0)
 				goto out; /* error already logged */
 			break;
 		default:
@@ -757,6 +1152,8 @@ process_add_identity(SocketEntry *e)
 	char canonical_provider[PATH_MAX];
 	time_t death = 0;
 	u_int seconds = 0;
+	struct dest_constraint *dest_constraints = NULL;
+	size_t ndest_constraints = 0;
 	struct sshkey *k = NULL;
 	int r = SSH_ERR_INTERNAL_ERROR;
 
@@ -768,7 +1165,7 @@ process_add_identity(SocketEntry *e)
 		goto out;
 	}
 	if (parse_key_constraints(e->request, k, &death, &seconds, &confirm,
-	    &sk_provider) != 0) {
+	    &sk_provider, &dest_constraints, &ndest_constraints) != 0) {
 		error_f("failed to parse constraints");
 		sshbuf_reset(e->request);
 		goto out;
@@ -811,10 +1208,15 @@ process_add_identity(SocketEntry *e)
 		/* Increment the number of identities. */
 		idtab->nentries++;
 	} else {
+		/* identity not visible, do not update */
+		if (identity_permitted(id, e, NULL, NULL, NULL) != 0)
+			goto out; /* error already logged */
 		/* key state might have been updated */
 		sshkey_free(id->key);
 		free(id->comment);
 		free(id->sk_provider);
+		free_dest_constraints(id->dest_constraints,
+		    id->ndest_constraints);
 	}
 	/* success */
 	id->key = k;
@@ -822,23 +1224,29 @@ process_add_identity(SocketEntry *e)
 	id->death = death;
 	id->confirm = confirm;
 	id->sk_provider = sk_provider;
+	id->dest_constraints = dest_constraints;
+	id->ndest_constraints = ndest_constraints;
 
 	if ((fp = sshkey_fingerprint(k, SSH_FP_HASH_DEFAULT,
 	    SSH_FP_DEFAULT)) == NULL)
 		fatal_f("sshkey_fingerprint failed");
 	debug_f("add %s %s \"%.100s\" (life: %u) (confirm: %u) "
-	    "(provider: %s)", sshkey_ssh_name(k), fp, comment, seconds,
-	    confirm, sk_provider == NULL ? "none" : sk_provider);
+	    "(provider: %s) (destination constraints: %zu)",
+	    sshkey_ssh_name(k), fp, comment, seconds, confirm,
+	    sk_provider == NULL ? "none" : sk_provider, ndest_constraints);
 	free(fp);
 	/* transferred */
 	k = NULL;
 	comment = NULL;
 	sk_provider = NULL;
+	dest_constraints = NULL;
+	ndest_constraints = 0;
 	success = 1;
  out:
 	free(sk_provider);
 	free(comment);
 	sshkey_free(k);
+	free_dest_constraints(dest_constraints, ndest_constraints);
 	send_status(e, success);
 }
 
@@ -921,6 +1329,8 @@ process_add_smartcard_key(SocketEntry *e)
 	time_t death = 0;
 	struct sshkey **keys = NULL, *k;
 	Identity *id;
+	struct dest_constraint *dest_constraints = NULL;
+	size_t ndest_constraints = 0;
 
 	debug2_f("entering");
 	if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 ||
@@ -929,7 +1339,7 @@ process_add_smartcard_key(SocketEntry *e)
 		goto send;
 	}
 	if (parse_key_constraints(e->request, NULL, &death, &seconds, &confirm,
-	    NULL) != 0) {
+	    NULL, &dest_constraints, &ndest_constraints) != 0) {
 		error_f("failed to parse constraints");
 		goto send;
 	}
@@ -963,6 +1373,10 @@ process_add_smartcard_key(SocketEntry *e)
 			}
 			id->death = death;
 			id->confirm = confirm;
+			id->dest_constraints = dest_constraints;
+			id->ndest_constraints = ndest_constraints;
+			dest_constraints = NULL; /* transferred */
+			ndest_constraints = 0;
 			TAILQ_INSERT_TAIL(&idtab->idlist, id, next);
 			idtab->nentries++;
 			success = 1;
@@ -976,6 +1390,7 @@ send:
 	free(provider);
 	free(keys);
 	free(comments);
+	free_dest_constraints(dest_constraints, ndest_constraints);
 	send_status(e, success);
 }
 
@@ -1029,8 +1444,8 @@ process_ext_session_bind(SocketEntry *e)
 	struct sshkey *key = NULL;
 	struct sshbuf *sid = NULL, *sig = NULL;
 	char *fp = NULL;
-	u_char fwd;
 	size_t i;
+	u_char fwd = 0;
 
 	debug2_f("entering");
 	if ((r = sshkey_froms(e->request, &key)) != 0 ||
@@ -1051,6 +1466,12 @@ process_ext_session_bind(SocketEntry *e)
 	}
 	/* check whether sid/key already recorded */
 	for (i = 0; i < e->nsession_ids; i++) {
+		if (!e->session_ids[i].forwarded) {
+			error_f("attempt to bind session ID to socket "
+			    "previously bound for authentication attempt");
+			r = -1;
+			goto out;
+		}
 		sid_match = buf_equal(sid, e->session_ids[i].sid) == 0;
 		key_match = sshkey_equal(key, e->session_ids[i].key);
 		if (sid_match && key_match) {

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


More information about the openssh-commits mailing list