summaryrefslogtreecommitdiffstats
path: root/ssh-agent.c
diff options
context:
space:
mode:
Diffstat (limited to 'ssh-agent.c')
-rw-r--r--ssh-agent.c124
1 files changed, 98 insertions, 26 deletions
diff --git a/ssh-agent.c b/ssh-agent.c
index 1d4c321e..12ec66cf 100644
--- a/ssh-agent.c
+++ b/ssh-agent.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-agent.c,v 1.301 2023/12/18 14:46:12 djm Exp $ */
+/* $OpenBSD: ssh-agent.c,v 1.302 2023/12/18 14:46:56 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -105,6 +105,8 @@
#define AGENT_MAX_SID_LEN 128
/* Maximum number of destination constraints to accept on a key */
#define AGENT_MAX_DEST_CONSTRAINTS 1024
+/* Maximum number of associated certificate constraints to accept on a key */
+#define AGENT_MAX_EXT_CERTS 1024
/* XXX store hostkey_sid in a refcounted tree */
@@ -1158,11 +1160,14 @@ parse_dest_constraint(struct sshbuf *m, struct dest_constraint *dc)
static int
parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp,
- struct dest_constraint **dcsp, size_t *ndcsp)
+ struct dest_constraint **dcsp, size_t *ndcsp, int *cert_onlyp,
+ struct sshkey ***certs, size_t *ncerts)
{
char *ext_name = NULL;
int r;
struct sshbuf *b = NULL;
+ u_char v;
+ struct sshkey *k;
if ((r = sshbuf_get_cstring(m, &ext_name, NULL)) != 0) {
error_fr(r, "parse constraint extension");
@@ -1205,6 +1210,36 @@ parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp,
*dcsp + (*ndcsp)++)) != 0)
goto out; /* error already logged */
}
+ } else if (strcmp(ext_name,
+ "associated-certs-v00@openssh.com") == 0) {
+ if (certs == NULL || ncerts == NULL || cert_onlyp == NULL) {
+ error_f("%s not valid here", ext_name);
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ if (*certs != NULL) {
+ error_f("%s already set", ext_name);
+ goto out;
+ }
+ if ((r = sshbuf_get_u8(m, &v)) != 0 ||
+ (r = sshbuf_froms(m, &b)) != 0) {
+ error_fr(r, "parse %s", ext_name);
+ goto out;
+ }
+ *cert_onlyp = v != 0;
+ while (sshbuf_len(b) != 0) {
+ if (*ncerts >= AGENT_MAX_EXT_CERTS) {
+ error_f("too many %s constraints", ext_name);
+ goto out;
+ }
+ *certs = xrecallocarray(*certs, *ncerts, *ncerts + 1,
+ sizeof(**certs));
+ if ((r = sshkey_froms(b, &k)) != 0) {
+ error_fr(r, "parse key");
+ goto out;
+ }
+ (*certs)[(*ncerts)++] = k;
+ }
} else {
error_f("unsupported constraint \"%s\"", ext_name);
r = SSH_ERR_FEATURE_UNSUPPORTED;
@@ -1221,7 +1256,8 @@ parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp,
static int
parse_key_constraints(struct sshbuf *m, struct sshkey *k, time_t *deathp,
u_int *secondsp, int *confirmp, char **sk_providerp,
- struct dest_constraint **dcsp, size_t *ndcsp)
+ struct dest_constraint **dcsp, size_t *ndcsp,
+ int *cert_onlyp, size_t *ncerts, struct sshkey ***certs)
{
u_char ctype;
int r;
@@ -1276,7 +1312,8 @@ 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, dcsp, ndcsp)) != 0)
+ sk_providerp, dcsp, ndcsp,
+ cert_onlyp, certs, ncerts)) != 0)
goto out; /* error already logged */
break;
default:
@@ -1313,7 +1350,8 @@ process_add_identity(SocketEntry *e)
goto out;
}
if (parse_key_constraints(e->request, k, &death, &seconds, &confirm,
- &sk_provider, &dest_constraints, &ndest_constraints) != 0) {
+ &sk_provider, &dest_constraints, &ndest_constraints,
+ NULL, NULL, NULL) != 0) {
error_f("failed to parse constraints");
sshbuf_reset(e->request);
goto out;
@@ -1473,6 +1511,32 @@ no_identities(SocketEntry *e)
sshbuf_free(msg);
}
+/* Add an identity to idlist; takes ownership of 'key' and 'comment' */
+static void
+add_p11_identity(struct sshkey *key, char *comment, const char *provider,
+ time_t death, int confirm, struct dest_constraint *dest_constraints,
+ size_t ndest_constraints)
+{
+ Identity *id;
+
+ if (lookup_identity(key) != NULL) {
+ sshkey_free(key);
+ free(comment);
+ return;
+ }
+ id = xcalloc(1, sizeof(Identity));
+ id->key = key;
+ id->comment = comment;
+ id->provider = xstrdup(provider);
+ id->death = death;
+ id->confirm = confirm;
+ id->dest_constraints = dup_dest_constraints(dest_constraints,
+ ndest_constraints);
+ id->ndest_constraints = ndest_constraints;
+ TAILQ_INSERT_TAIL(&idtab->idlist, id, next);
+ idtab->nentries++;
+}
+
#ifdef ENABLE_PKCS11
static void
process_add_smartcard_key(SocketEntry *e)
@@ -1483,9 +1547,10 @@ process_add_smartcard_key(SocketEntry *e)
u_int seconds = 0;
time_t death = 0;
struct sshkey **keys = NULL, *k;
- Identity *id;
struct dest_constraint *dest_constraints = NULL;
- size_t ndest_constraints = 0;
+ size_t j, ndest_constraints = 0, ncerts = 0;
+ struct sshkey **certs = NULL;
+ int cert_only = 0;
debug2_f("entering");
if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 ||
@@ -1494,7 +1559,8 @@ process_add_smartcard_key(SocketEntry *e)
goto send;
}
if (parse_key_constraints(e->request, NULL, &death, &seconds, &confirm,
- NULL, &dest_constraints, &ndest_constraints) != 0) {
+ NULL, &dest_constraints, &ndest_constraints, &cert_only,
+ &ncerts, &certs) != 0) {
error_f("failed to parse constraints");
goto send;
}
@@ -1520,25 +1586,28 @@ process_add_smartcard_key(SocketEntry *e)
count = pkcs11_add_provider(canonical_provider, pin, &keys, &comments);
for (i = 0; i < count; i++) {
- k = keys[i];
- if (lookup_identity(k) == NULL) {
- id = xcalloc(1, sizeof(Identity));
- id->key = k;
- keys[i] = NULL; /* transferred */
- id->provider = xstrdup(canonical_provider);
- if (*comments[i] != '\0') {
- id->comment = comments[i];
- comments[i] = NULL; /* transferred */
- } else {
- id->comment = xstrdup(canonical_provider);
- }
- id->death = death;
- id->confirm = confirm;
- id->dest_constraints = dup_dest_constraints(
+ if (comments[i] == NULL || comments[i][0] == '\0') {
+ free(comments[i]);
+ comments[i] = xstrdup(canonical_provider);
+ }
+ for (j = 0; j < ncerts; j++) {
+ if (!sshkey_is_cert(certs[j]))
+ continue;
+ if (!sshkey_equal_public(keys[i], certs[j]))
+ continue;
+ if (pkcs11_make_cert(keys[i], certs[j], &k) != 0)
+ continue;
+ add_p11_identity(k, xstrdup(comments[i]),
+ canonical_provider, death, confirm,
+ dest_constraints, ndest_constraints);
+ success = 1;
+ }
+ if (!cert_only && lookup_identity(keys[i]) == NULL) {
+ add_p11_identity(keys[i], comments[i],
+ canonical_provider, death, confirm,
dest_constraints, ndest_constraints);
- id->ndest_constraints = ndest_constraints;
- TAILQ_INSERT_TAIL(&idtab->idlist, id, next);
- idtab->nentries++;
+ keys[i] = NULL; /* transferred */
+ comments[i] = NULL; /* transferred */
success = 1;
}
/* XXX update constraints for existing keys */
@@ -1551,6 +1620,9 @@ send:
free(keys);
free(comments);
free_dest_constraints(dest_constraints, ndest_constraints);
+ for (j = 0; j < ncerts; j++)
+ sshkey_free(certs[j]);
+ free(certs);
send_status(e, success);
}