summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2021-12-19 22:11:39 +0000
committerDamien Miller <djm@mindrot.org>2021-12-20 09:27:06 +1100
commit39f00dcf44915f20684160f0a88d3ef8a3278351 (patch)
treedb88a6de0209e5e9c4058ba7386e0e6b3ad7baa8
parentce943912df812c573a33d00bf9e5435b7fcca3f7 (diff)
upstream: ssh-agent side of destination constraints
Gives ssh-agent the ability to parse restrict-destination-v00@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
-rw-r--r--ssh-agent.c505
1 files changed, 463 insertions, 42 deletions
diff --git a/ssh-agent.c b/ssh-agent.c
index 568bbf347..346dae3dc 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@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@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 {
@@ -213,15 +218,253 @@ idtab_init(void)
}
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)
{
sshkey_free(id->key);
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@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) {