diff options
Diffstat (limited to 'usr.bin')
-rw-r--r-- | usr.bin/ssh/authfd.c | 87 | ||||
-rw-r--r-- | usr.bin/ssh/authfd.h | 26 | ||||
-rw-r--r-- | usr.bin/ssh/ssh-add.c | 186 | ||||
-rw-r--r-- | usr.bin/ssh/ssh-add/Makefile | 4 | ||||
-rw-r--r-- | usr.bin/ssh/sshconnect.c | 4 |
5 files changed, 276 insertions, 31 deletions
diff --git a/usr.bin/ssh/authfd.c b/usr.bin/ssh/authfd.c index 18fa1f19c2b..c358581b939 100644 --- a/usr.bin/ssh/authfd.c +++ b/usr.bin/ssh/authfd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: authfd.c,v 1.128 2021/12/19 22:08:48 djm Exp $ */ +/* $OpenBSD: authfd.c,v 1.129 2021/12/19 22:10:24 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -453,12 +453,63 @@ ssh_agent_sign(int sock, const struct sshkey *key, /* Encode key for a message to the agent. */ +static int +encode_dest_constraint_hop(struct sshbuf *m, + const struct dest_constraint_hop *dch) +{ + struct sshbuf *b; + u_int i; + int r; + + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_put_cstring(b, dch->user)) != 0 || + (r = sshbuf_put_cstring(b, dch->hostname)) != 0 || + (r = sshbuf_put_string(b, NULL, 0)) != 0) /* reserved */ + goto out; + for (i = 0; i < dch->nkeys; i++) { + if ((r = sshkey_puts(dch->keys[i], b)) != 0 || + (r = sshbuf_put_u8(b, dch->key_is_ca[i] != 0)) != 0) + goto out; + } + if ((r = sshbuf_put_stringb(m, b)) != 0) + goto out; + /* success */ + r = 0; + out: + sshbuf_free(b); + return r; +} + +static int +encode_dest_constraint(struct sshbuf *m, const struct dest_constraint *dc) +{ + struct sshbuf *b; + int r; + + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = encode_dest_constraint_hop(b, &dc->from) != 0) || + (r = encode_dest_constraint_hop(b, &dc->to) != 0) || + (r = sshbuf_put_string(b, NULL, 0)) != 0) /* reserved */ + goto out; + if ((r = sshbuf_put_stringb(m, b)) != 0) + goto out; + /* success */ + r = 0; + out: + sshbuf_free(b); + return r; +} static int encode_constraints(struct sshbuf *m, u_int life, u_int confirm, u_int maxsign, - const char *provider) + const char *provider, struct dest_constraint **dest_constraints, + size_t ndest_constraints) { int r; + struct sshbuf *b = NULL; + size_t i; if (life != 0) { if ((r = sshbuf_put_u8(m, SSH_AGENT_CONSTRAIN_LIFETIME)) != 0 || @@ -482,8 +533,26 @@ encode_constraints(struct sshbuf *m, u_int life, u_int confirm, u_int maxsign, (r = sshbuf_put_cstring(m, provider)) != 0) goto out; } + if (dest_constraints != NULL && ndest_constraints > 0) { + if ((b = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + for (i = 0; i < ndest_constraints; i++) { + if ((r = encode_dest_constraint(b, + dest_constraints[i])) != 0) + goto out; + } + if ((r = sshbuf_put_u8(m, + SSH_AGENT_CONSTRAIN_EXTENSION)) != 0 || + (r = sshbuf_put_cstring(m, + "restrict-destination-v00@openssh.com")) != 0 || + (r = sshbuf_put_stringb(m, b)) != 0) + goto out; + } r = 0; out: + sshbuf_free(b); return r; } @@ -494,10 +563,12 @@ encode_constraints(struct sshbuf *m, u_int life, u_int confirm, u_int maxsign, int ssh_add_identity_constrained(int sock, struct sshkey *key, const char *comment, u_int life, u_int confirm, u_int maxsign, - const char *provider) + const char *provider, struct dest_constraint **dest_constraints, + size_t ndest_constraints) { struct sshbuf *msg; - int r, constrained = (life || confirm || maxsign || provider); + int r, constrained = (life || confirm || maxsign || + provider || dest_constraints); u_char type; if ((msg = sshbuf_new()) == NULL) @@ -535,7 +606,7 @@ ssh_add_identity_constrained(int sock, struct sshkey *key, } if (constrained && (r = encode_constraints(msg, life, confirm, maxsign, - provider)) != 0) + provider, dest_constraints, ndest_constraints)) != 0) goto out; if ((r = ssh_request_reply_decode(sock, msg)) != 0) goto out; @@ -589,7 +660,8 @@ ssh_remove_identity(int sock, const struct sshkey *key) */ int ssh_update_card(int sock, int add, const char *reader_id, const char *pin, - u_int life, u_int confirm) + u_int life, u_int confirm, + struct dest_constraint **dest_constraints, size_t ndest_constraints) { struct sshbuf *msg; int r, constrained = (life || confirm); @@ -609,7 +681,8 @@ ssh_update_card(int sock, int add, const char *reader_id, const char *pin, (r = sshbuf_put_cstring(msg, pin)) != 0) goto out; if (constrained && - (r = encode_constraints(msg, life, confirm, 0, NULL)) != 0) + (r = encode_constraints(msg, life, confirm, 0, NULL, + dest_constraints, ndest_constraints)) != 0) goto out; if ((r = ssh_request_reply_decode(sock, msg)) != 0) goto out; diff --git a/usr.bin/ssh/authfd.h b/usr.bin/ssh/authfd.h index fe1ed8a1abc..7a1c0ddff98 100644 --- a/usr.bin/ssh/authfd.h +++ b/usr.bin/ssh/authfd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: authfd.h,v 1.50 2021/12/19 22:08:48 djm Exp $ */ +/* $OpenBSD: authfd.h,v 1.51 2021/12/19 22:10:24 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> @@ -17,6 +17,7 @@ #define AUTHFD_H struct sshbuf; +struct sshkey; /* List of identities returned by ssh_fetch_identitylist() */ struct ssh_identitylist { @@ -25,6 +26,20 @@ struct ssh_identitylist { char **comments; }; +/* Key destination restrictions */ +struct dest_constraint_hop { + char *user; /* wildcards allowed */ + char *hostname; /* used to matching cert principals and for display */ + int is_ca; + u_int nkeys; /* number of entries in *both* 'keys' and 'key_is_ca' */ + struct sshkey **keys; + int *key_is_ca; +}; +struct dest_constraint { + struct dest_constraint_hop from; + struct dest_constraint_hop to; +}; + int ssh_get_authentication_socket(int *fdp); int ssh_get_authentication_socket_path(const char *authsocket, int *fdp); void ssh_close_authentication_socket(int sock); @@ -33,12 +48,15 @@ int ssh_lock_agent(int sock, int lock, const char *password); int ssh_fetch_identitylist(int sock, struct ssh_identitylist **idlp); void ssh_free_identitylist(struct ssh_identitylist *idl); int ssh_add_identity_constrained(int sock, struct sshkey *key, - const char *comment, u_int life, u_int confirm, u_int maxsign, - const char *provider); + const char *comment, u_int life, u_int confirm, u_int maxsign, + const char *provider, struct dest_constraint **dest_constraints, + size_t ndest_constraints); int ssh_agent_has_key(int sock, const struct sshkey *key); int ssh_remove_identity(int sock, const struct sshkey *key); int ssh_update_card(int sock, int add, const char *reader_id, - const char *pin, u_int life, u_int confirm); + const char *pin, u_int life, u_int confirm, + struct dest_constraint **dest_constraints, + size_t ndest_constraints); int ssh_remove_all_identities(int sock, int version); int ssh_agent_sign(int sock, const struct sshkey *key, diff --git a/usr.bin/ssh/ssh-add.c b/usr.bin/ssh/ssh-add.c index 116b5a87cdc..970d1163795 100644 --- a/usr.bin/ssh/ssh-add.c +++ b/usr.bin/ssh/ssh-add.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-add.c,v 1.161 2021/10/28 02:54:18 djm Exp $ */ +/* $OpenBSD: ssh-add.c,v 1.162 2021/12/19 22:10:24 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -65,6 +65,7 @@ #include "digest.h" #include "ssh-sk.h" #include "sk-api.h" +#include "hostfile.h" /* argv0 */ extern char *__progname; @@ -224,7 +225,8 @@ delete_all(int agent_fd, int qflag) static int add_file(int agent_fd, const char *filename, int key_only, int qflag, - const char *skprovider) + const char *skprovider, struct dest_constraint **dest_constraints, + size_t ndest_constraints) { struct sshkey *private, *cert; char *comment = NULL; @@ -358,7 +360,8 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag, } if ((r = ssh_add_identity_constrained(agent_fd, private, comment, - lifetime, confirm, maxsign, skprovider)) == 0) { + lifetime, confirm, maxsign, skprovider, + dest_constraints, ndest_constraints)) == 0) { ret = 0; if (!qflag) { fprintf(stderr, "Identity added: %s (%s)\n", @@ -410,7 +413,8 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag, sshkey_free(cert); if ((r = ssh_add_identity_constrained(agent_fd, private, comment, - lifetime, confirm, maxsign, skprovider)) != 0) { + lifetime, confirm, maxsign, skprovider, + dest_constraints, ndest_constraints)) != 0) { error_r(r, "Certificate %s (%s) add failed", certpath, private->cert->key_id); goto out; @@ -438,7 +442,8 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag, } static int -update_card(int agent_fd, int add, const char *id, int qflag) +update_card(int agent_fd, int add, const char *id, int qflag, + struct dest_constraint **dest_constraints, size_t ndest_constraints) { char *pin = NULL; int r, ret = -1; @@ -450,7 +455,7 @@ update_card(int agent_fd, int add, const char *id, int qflag) } if ((r = ssh_update_card(agent_fd, add, id, pin == NULL ? "" : pin, - lifetime, confirm)) == 0) { + lifetime, confirm, dest_constraints, ndest_constraints)) == 0) { ret = 0; if (!qflag) { fprintf(stderr, "Card %s: %s\n", @@ -571,7 +576,8 @@ lock_agent(int agent_fd, int lock) } static int -load_resident_keys(int agent_fd, const char *skprovider, int qflag) +load_resident_keys(int agent_fd, const char *skprovider, int qflag, + struct dest_constraint **dest_constraints, size_t ndest_constraints) { struct sshsk_resident_key **srks; size_t nsrks, i; @@ -591,8 +597,10 @@ load_resident_keys(int agent_fd, const char *skprovider, int qflag) fingerprint_hash, SSH_FP_DEFAULT)) == NULL) fatal_f("sshkey_fingerprint failed"); if ((r = ssh_add_identity_constrained(agent_fd, key, "", - lifetime, confirm, maxsign, skprovider)) != 0) { - error("Unable to add key %s %s", sshkey_type(key), fp); + lifetime, confirm, maxsign, skprovider, + dest_constraints, ndest_constraints)) != 0) { + error("Unable to add key %s %s", + sshkey_type(key), fp); free(fp); ok = r; continue; @@ -621,23 +629,144 @@ load_resident_keys(int agent_fd, const char *skprovider, int qflag) static int do_file(int agent_fd, int deleting, int key_only, char *file, int qflag, - const char *skprovider) + const char *skprovider, struct dest_constraint **dest_constraints, + size_t ndest_constraints) { if (deleting) { if (delete_file(agent_fd, file, key_only, qflag) == -1) return -1; } else { - if (add_file(agent_fd, file, key_only, qflag, skprovider) == -1) + if (add_file(agent_fd, file, key_only, qflag, skprovider, + dest_constraints, ndest_constraints) == -1) return -1; } return 0; } +/* Append string 's' to a NULL-terminated array of strings */ +static void +stringlist_append(char ***listp, const char *s) +{ + size_t i = 0; + + if (*listp == NULL) + *listp = xcalloc(2, sizeof(**listp)); + else { + for (i = 0; (*listp)[i] != NULL; i++) + ; /* count */ + *listp = xrecallocarray(*listp, i + 1, i + 2, sizeof(**listp)); + } + (*listp)[i] = xstrdup(s); +} + +static void +parse_dest_constraint_hop(const char *s, struct dest_constraint_hop *dch, + char **hostkey_files) +{ + char *user = NULL, *host, *os, *path; + size_t i; + struct hostkeys *hostkeys; + const struct hostkey_entry *hke; + int r, want_ca; + + memset(dch, '\0', sizeof(*dch)); + os = xstrdup(s); + if ((host = strchr(os, '@')) == NULL) + host = os; + else { + *host++ = '\0'; + user = os; + } + cleanhostname(host); + /* Trivial case: username@ (all hosts) */ + if (*host == '\0') { + if (user == NULL) { + fatal("Invalid key destination constraint \"%s\": " + "does not specify user or host", s); + } + dch->user = xstrdup(user); + /* other fields left blank */ + free(os); + return; + } + if (hostkey_files == NULL) + fatal_f("no hostkey files"); + /* Otherwise we need to look up the keys for this hostname */ + hostkeys = init_hostkeys(); + for (i = 0; hostkey_files[i]; i++) { + path = tilde_expand_filename(hostkey_files[i], getuid()); + debug2_f("looking up host keys for \"%s\" in %s", host, path); + load_hostkeys(hostkeys, host, path, 0); + free(path); + } + dch->user = user == NULL ? NULL : xstrdup(user); + dch->hostname = xstrdup(host); + for (i = 0; i < hostkeys->num_entries; i++) { + hke = hostkeys->entries + i; + want_ca = hke->marker == MRK_CA; + if (hke->marker != MRK_NONE && !want_ca) + continue; + debug3_f("%s%s%s: adding %s %skey from %s:%lu as key %u", + user == NULL ? "": user, user == NULL ? "" : "@", + host, sshkey_type(hke->key), want_ca ? "CA " : "", + hke->file, hke->line, dch->nkeys); + 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_from_private(hke->key, + &(dch->keys[dch->nkeys]))) != 0) + fatal_fr(r, "sshkey_from_private"); + dch->key_is_ca[dch->nkeys] = want_ca; + dch->nkeys++; + } + if (dch->nkeys == 0) + fatal("No host keys found for destination \"%s\"", host); + free_hostkeys(hostkeys); + free(os); + return; +} + +static void +parse_dest_constraint(const char *s, struct dest_constraint ***dcp, + size_t *ndcp, char **hostkey_files) +{ + struct dest_constraint *dc; + char *os, *cp; + + dc = xcalloc(1, sizeof(*dc)); + os = xstrdup(s); + if ((cp = strchr(os, '>')) == NULL) { + /* initial hop; no 'from' hop specified */ + parse_dest_constraint_hop(os, &dc->to, hostkey_files); + } else { + /* two hops specified */ + *(cp++) = '\0'; + parse_dest_constraint_hop(os, &dc->from, hostkey_files); + parse_dest_constraint_hop(cp, &dc->to, hostkey_files); + if (dc->from.user != NULL) { + fatal("Invalid key constraint %s: cannot specify " + "user on 'from' host", os); + } + } + /* XXX eliminate or error on duplicates */ + debug2_f("constraint %zu: %s%s%s (%u keys) > %s%s%s (%u keys)", *ndcp, + dc->from.user ? dc->from.user : "", dc->from.user ? "@" : "", + 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); + *dcp = xrecallocarray(*dcp, *ndcp, *ndcp + 1, sizeof(**dcp)); + (*dcp)[(*ndcp)++] = dc; + free(os); +} + + static void usage(void) { fprintf(stderr, "usage: ssh-add [-cDdKkLlqvXx] [-E fingerprint_hash] [-S provider] [-t life]\n" +" [-H hostkey_file] [-h destination]\n" #ifdef WITH_XMSS " [-M maxsign] [-m minleft]\n" #endif @@ -655,10 +784,13 @@ main(int argc, char **argv) extern int optind; int agent_fd; char *pkcs11provider = NULL, *skprovider = NULL; + char **dest_constraint_strings = NULL, **hostkey_files = NULL; int r, i, ch, deleting = 0, ret = 0, key_only = 0, do_download = 0; int xflag = 0, lflag = 0, Dflag = 0, qflag = 0, Tflag = 0; SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; LogLevel log_level = SYSLOG_LEVEL_INFO; + struct dest_constraint **dest_constraints = NULL; + size_t ndest_constraints = 0; /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); @@ -685,7 +817,7 @@ main(int argc, char **argv) skprovider = getenv("SSH_SK_PROVIDER"); - while ((ch = getopt(argc, argv, "vkKlLcdDTxXE:e:M:m:qs:S:t:")) != -1) { + while ((ch = getopt(argc, argv, "vkKlLcdDTxXE:e:h:H:M:m:qs:S:t:")) != -1) { switch (ch) { case 'v': if (log_level == SYSLOG_LEVEL_INFO) @@ -698,6 +830,12 @@ main(int argc, char **argv) if (fingerprint_hash == -1) fatal("Invalid hash algorithm \"%s\"", optarg); break; + case 'H': + stringlist_append(&hostkey_files, optarg); + break; + case 'h': + stringlist_append(&dest_constraint_strings, optarg); + break; case 'k': key_only = 1; break; @@ -791,6 +929,19 @@ main(int argc, char **argv) if (skprovider == NULL) skprovider = "internal"; + if (hostkey_files == NULL) { + /* use defaults from readconf.c */ + stringlist_append(&hostkey_files, _PATH_SSH_USER_HOSTFILE); + stringlist_append(&hostkey_files, _PATH_SSH_USER_HOSTFILE2); + stringlist_append(&hostkey_files, _PATH_SSH_SYSTEM_HOSTFILE); + stringlist_append(&hostkey_files, _PATH_SSH_SYSTEM_HOSTFILE2); + } + if (dest_constraint_strings != NULL) { + for (i = 0; dest_constraint_strings[i] != NULL; i++) { + parse_dest_constraint(dest_constraint_strings[i], + &dest_constraints, &ndest_constraints, hostkey_files); + } + } argc -= optind; argv += optind; @@ -804,14 +955,15 @@ main(int argc, char **argv) } if (pkcs11provider != NULL) { if (update_card(agent_fd, !deleting, pkcs11provider, - qflag) == -1) + qflag, dest_constraints, ndest_constraints) == -1) ret = 1; goto done; } if (do_download) { if (skprovider == NULL) fatal("Cannot download keys without provider"); - if (load_resident_keys(agent_fd, skprovider, qflag) != 0) + if (load_resident_keys(agent_fd, skprovider, qflag, + dest_constraints, ndest_constraints) != 0) ret = 1; goto done; } @@ -834,7 +986,8 @@ main(int argc, char **argv) if (stat(buf, &st) == -1) continue; if (do_file(agent_fd, deleting, key_only, buf, - qflag, skprovider) == -1) + qflag, skprovider, + dest_constraints, ndest_constraints) == -1) ret = 1; else count++; @@ -844,7 +997,8 @@ main(int argc, char **argv) } else { for (i = 0; i < argc; i++) { if (do_file(agent_fd, deleting, key_only, - argv[i], qflag, skprovider) == -1) + argv[i], qflag, skprovider, + dest_constraints, ndest_constraints) == -1) ret = 1; } } diff --git a/usr.bin/ssh/ssh-add/Makefile b/usr.bin/ssh/ssh-add/Makefile index 6e9a2b45869..69b50f69902 100644 --- a/usr.bin/ssh/ssh-add/Makefile +++ b/usr.bin/ssh/ssh-add/Makefile @@ -1,9 +1,9 @@ -# $OpenBSD: Makefile,v 1.31 2020/04/03 02:26:56 djm Exp $ +# $OpenBSD: Makefile,v 1.32 2021/12/19 22:11:06 djm Exp $ .PATH: ${.CURDIR}/.. SRCS= ssh-add.c -SRCS+= authfd.c cleanup.c fatal.c readpass.c utf8.c +SRCS+= authfd.c cleanup.c fatal.c readpass.c utf8.c hostfile.c hmac.c SRCS+= ${SRCS_BASE} ${SRCS_KEY} ${SRCS_KEYP} ${SRCS_KRL} ${SRCS_UTL} SRCS+= ${SRCS_SK_CLIENT} diff --git a/usr.bin/ssh/sshconnect.c b/usr.bin/ssh/sshconnect.c index 465c4d37339..52daead44d6 100644 --- a/usr.bin/ssh/sshconnect.c +++ b/usr.bin/ssh/sshconnect.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect.c,v 1.355 2021/07/02 05:11:21 dtucker Exp $ */ +/* $OpenBSD: sshconnect.c,v 1.356 2021/12/19 22:10:24 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -1662,7 +1662,7 @@ maybe_add_key_to_agent(const char *authfile, struct sshkey *private, if ((r = ssh_add_identity_constrained(auth_sock, private, comment == NULL ? authfile : comment, options.add_keys_to_agent_lifespan, - (options.add_keys_to_agent == 3), 0, skprovider)) == 0) + (options.add_keys_to_agent == 3), 0, skprovider, NULL, 0)) == 0) debug("identity added to agent: %s", authfile); else debug("could not add identity to agent: %s (%d)", authfile, r); |