version 1.1.1.21, 2021/04/19 14:38:29 |
version 1.1.1.22, 2022/02/23 19:04:26 |
|
|
/* $OpenBSD: ssh-add.c,v 1.160 2021/04/03 06:18:41 djm Exp $ */ |
/* $OpenBSD: ssh-add.c,v 1.165 2022/02/04 02:49:17 dtucker Exp $ */ |
/* |
/* |
* Author: Tatu Ylonen <ylo@cs.hut.fi> |
* Author: Tatu Ylonen <ylo@cs.hut.fi> |
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
|
|
#include "digest.h" |
#include "digest.h" |
#include "ssh-sk.h" |
#include "ssh-sk.h" |
#include "sk-api.h" |
#include "sk-api.h" |
|
#include "hostfile.h" |
|
|
/* argv0 */ |
/* argv0 */ |
extern char *__progname; |
extern char *__progname; |
Line 72 extern char *__progname; |
|
Line 73 extern char *__progname; |
|
/* Default files to add */ |
/* Default files to add */ |
static char *default_files[] = { |
static char *default_files[] = { |
_PATH_SSH_CLIENT_ID_RSA, |
_PATH_SSH_CLIENT_ID_RSA, |
_PATH_SSH_CLIENT_ID_DSA, |
|
_PATH_SSH_CLIENT_ID_ECDSA, |
_PATH_SSH_CLIENT_ID_ECDSA, |
_PATH_SSH_CLIENT_ID_ECDSA_SK, |
_PATH_SSH_CLIENT_ID_ECDSA_SK, |
_PATH_SSH_CLIENT_ID_ED25519, |
_PATH_SSH_CLIENT_ID_ED25519, |
_PATH_SSH_CLIENT_ID_ED25519_SK, |
_PATH_SSH_CLIENT_ID_ED25519_SK, |
_PATH_SSH_CLIENT_ID_XMSS, |
_PATH_SSH_CLIENT_ID_XMSS, |
|
_PATH_SSH_CLIENT_ID_DSA, |
NULL |
NULL |
}; |
}; |
|
|
Line 224 delete_all(int agent_fd, int qflag) |
|
Line 225 delete_all(int agent_fd, int qflag) |
|
|
|
static int |
static int |
add_file(int agent_fd, const char *filename, int key_only, int qflag, |
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; |
struct sshkey *private, *cert; |
char *comment = NULL; |
char *comment = NULL; |
Line 347 add_file(int agent_fd, const char *filen |
|
Line 349 add_file(int agent_fd, const char *filen |
|
"without provider\n", filename); |
"without provider\n", filename); |
goto out; |
goto out; |
} |
} |
if ((private->sk_flags & SSH_SK_USER_VERIFICATION_REQD) != 0) { |
|
fprintf(stderr, "FIDO verify-required key %s is not " |
|
"currently supported by ssh-agent\n", filename); |
|
goto out; |
|
} |
|
} else { |
} else { |
/* Don't send provider constraint for other keys */ |
/* Don't send provider constraint for other keys */ |
skprovider = NULL; |
skprovider = NULL; |
} |
} |
|
|
if ((r = ssh_add_identity_constrained(agent_fd, private, comment, |
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; |
ret = 0; |
if (!qflag) { |
if (!qflag) { |
fprintf(stderr, "Identity added: %s (%s)\n", |
fprintf(stderr, "Identity added: %s (%s)\n", |
Line 410 add_file(int agent_fd, const char *filen |
|
Line 408 add_file(int agent_fd, const char *filen |
|
sshkey_free(cert); |
sshkey_free(cert); |
|
|
if ((r = ssh_add_identity_constrained(agent_fd, private, comment, |
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, |
error_r(r, "Certificate %s (%s) add failed", certpath, |
private->cert->key_id); |
private->cert->key_id); |
goto out; |
goto out; |
Line 438 add_file(int agent_fd, const char *filen |
|
Line 437 add_file(int agent_fd, const char *filen |
|
} |
} |
|
|
static int |
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; |
char *pin = NULL; |
int r, ret = -1; |
int r, ret = -1; |
Line 450 update_card(int agent_fd, int add, const |
|
Line 450 update_card(int agent_fd, int add, const |
|
} |
} |
|
|
if ((r = ssh_update_card(agent_fd, add, id, pin == NULL ? "" : pin, |
if ((r = ssh_update_card(agent_fd, add, id, pin == NULL ? "" : pin, |
lifetime, confirm)) == 0) { |
lifetime, confirm, dest_constraints, ndest_constraints)) == 0) { |
ret = 0; |
ret = 0; |
if (!qflag) { |
if (!qflag) { |
fprintf(stderr, "Card %s: %s\n", |
fprintf(stderr, "Card %s: %s\n", |
Line 571 lock_agent(int agent_fd, int lock) |
|
Line 571 lock_agent(int agent_fd, int lock) |
|
} |
} |
|
|
static int |
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 sshkey **keys; |
struct sshsk_resident_key **srks; |
size_t nkeys, i; |
size_t nsrks, i; |
|
struct sshkey *key; |
int r, ok = 0; |
int r, ok = 0; |
char *fp; |
char *fp; |
|
|
pass = read_passphrase("Enter PIN for authenticator: ", RP_ALLOW_STDIN); |
pass = read_passphrase("Enter PIN for authenticator: ", RP_ALLOW_STDIN); |
if ((r = sshsk_load_resident(skprovider, NULL, pass, |
if ((r = sshsk_load_resident(skprovider, NULL, pass, 0, |
&keys, &nkeys)) != 0) { |
&srks, &nsrks)) != 0) { |
error_r(r, "Unable to load resident keys"); |
error_r(r, "Unable to load resident keys"); |
return r; |
return r; |
} |
} |
for (i = 0; i < nkeys; i++) { |
for (i = 0; i < nsrks; i++) { |
if ((fp = sshkey_fingerprint(keys[i], |
key = srks[i]->key; |
|
if ((fp = sshkey_fingerprint(key, |
fingerprint_hash, SSH_FP_DEFAULT)) == NULL) |
fingerprint_hash, SSH_FP_DEFAULT)) == NULL) |
fatal_f("sshkey_fingerprint failed"); |
fatal_f("sshkey_fingerprint failed"); |
if ((r = ssh_add_identity_constrained(agent_fd, keys[i], "", |
if ((r = ssh_add_identity_constrained(agent_fd, key, "", |
lifetime, confirm, maxsign, skprovider)) != 0) { |
lifetime, confirm, maxsign, skprovider, |
|
dest_constraints, ndest_constraints)) != 0) { |
error("Unable to add key %s %s", |
error("Unable to add key %s %s", |
sshkey_type(keys[i]), fp); |
sshkey_type(key), fp); |
free(fp); |
free(fp); |
ok = r; |
ok = r; |
continue; |
continue; |
Line 600 load_resident_keys(int agent_fd, const c |
|
Line 604 load_resident_keys(int agent_fd, const c |
|
ok = 1; |
ok = 1; |
if (!qflag) { |
if (!qflag) { |
fprintf(stderr, "Resident identity added: %s %s\n", |
fprintf(stderr, "Resident identity added: %s %s\n", |
sshkey_type(keys[i]), fp); |
sshkey_type(key), fp); |
if (lifetime != 0) { |
if (lifetime != 0) { |
fprintf(stderr, |
fprintf(stderr, |
"Lifetime set to %d seconds\n", lifetime); |
"Lifetime set to %d seconds\n", lifetime); |
Line 611 load_resident_keys(int agent_fd, const c |
|
Line 615 load_resident_keys(int agent_fd, const c |
|
} |
} |
} |
} |
free(fp); |
free(fp); |
sshkey_free(keys[i]); |
|
} |
} |
free(keys); |
sshsk_free_resident_keys(srks, nsrks); |
if (nkeys == 0) |
if (nsrks == 0) |
return SSH_ERR_KEY_NOT_FOUND; |
return SSH_ERR_KEY_NOT_FOUND; |
return ok == 1 ? 0 : ok; |
return ok == 1 ? 0 : ok; |
} |
} |
|
|
static int |
static int |
do_file(int agent_fd, int deleting, int key_only, char *file, int qflag, |
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 (deleting) { |
if (delete_file(agent_fd, file, key_only, qflag) == -1) |
if (delete_file(agent_fd, file, key_only, qflag) == -1) |
return -1; |
return -1; |
} else { |
} 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 -1; |
} |
} |
return 0; |
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 |
static void |
usage(void) |
usage(void) |
{ |
{ |
fprintf(stderr, |
fprintf(stderr, |
"usage: ssh-add [-cDdKkLlqvXx] [-E fingerprint_hash] [-S provider] [-t life]\n" |
"usage: ssh-add [-cDdKkLlqvXx] [-E fingerprint_hash] [-H hostkey_file]\n" |
|
" [-h destination_constraint] [-S provider] [-t life]\n" |
#ifdef WITH_XMSS |
#ifdef WITH_XMSS |
" [-M maxsign] [-m minleft]\n" |
" [-M maxsign] [-m minleft]\n" |
#endif |
#endif |
Line 655 main(int argc, char **argv) |
|
Line 779 main(int argc, char **argv) |
|
extern int optind; |
extern int optind; |
int agent_fd; |
int agent_fd; |
char *pkcs11provider = NULL, *skprovider = NULL; |
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 r, i, ch, deleting = 0, ret = 0, key_only = 0, do_download = 0; |
int xflag = 0, lflag = 0, Dflag = 0, qflag = 0, Tflag = 0; |
int xflag = 0, lflag = 0, Dflag = 0, qflag = 0, Tflag = 0; |
SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; |
SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; |
LogLevel log_level = SYSLOG_LEVEL_INFO; |
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 */ |
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ |
sanitise_stdfd(); |
sanitise_stdfd(); |
Line 685 main(int argc, char **argv) |
|
Line 812 main(int argc, char **argv) |
|
|
|
skprovider = getenv("SSH_SK_PROVIDER"); |
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) { |
switch (ch) { |
case 'v': |
case 'v': |
if (log_level == SYSLOG_LEVEL_INFO) |
if (log_level == SYSLOG_LEVEL_INFO) |
Line 698 main(int argc, char **argv) |
|
Line 825 main(int argc, char **argv) |
|
if (fingerprint_hash == -1) |
if (fingerprint_hash == -1) |
fatal("Invalid hash algorithm \"%s\"", optarg); |
fatal("Invalid hash algorithm \"%s\"", optarg); |
break; |
break; |
|
case 'H': |
|
stringlist_append(&hostkey_files, optarg); |
|
break; |
|
case 'h': |
|
stringlist_append(&dest_constraint_strings, optarg); |
|
break; |
case 'k': |
case 'k': |
key_only = 1; |
key_only = 1; |
break; |
break; |
Line 791 main(int argc, char **argv) |
|
Line 924 main(int argc, char **argv) |
|
|
|
if (skprovider == NULL) |
if (skprovider == NULL) |
skprovider = "internal"; |
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; |
argc -= optind; |
argv += optind; |
argv += optind; |
Line 804 main(int argc, char **argv) |
|
Line 950 main(int argc, char **argv) |
|
} |
} |
if (pkcs11provider != NULL) { |
if (pkcs11provider != NULL) { |
if (update_card(agent_fd, !deleting, pkcs11provider, |
if (update_card(agent_fd, !deleting, pkcs11provider, |
qflag) == -1) |
qflag, dest_constraints, ndest_constraints) == -1) |
ret = 1; |
ret = 1; |
goto done; |
goto done; |
} |
} |
if (do_download) { |
if (do_download) { |
if (skprovider == NULL) |
if (skprovider == NULL) |
fatal("Cannot download keys without provider"); |
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; |
ret = 1; |
goto done; |
goto done; |
} |
} |
Line 834 main(int argc, char **argv) |
|
Line 981 main(int argc, char **argv) |
|
if (stat(buf, &st) == -1) |
if (stat(buf, &st) == -1) |
continue; |
continue; |
if (do_file(agent_fd, deleting, key_only, buf, |
if (do_file(agent_fd, deleting, key_only, buf, |
qflag, skprovider) == -1) |
qflag, skprovider, |
|
dest_constraints, ndest_constraints) == -1) |
ret = 1; |
ret = 1; |
else |
else |
count++; |
count++; |
Line 844 main(int argc, char **argv) |
|
Line 992 main(int argc, char **argv) |
|
} else { |
} else { |
for (i = 0; i < argc; i++) { |
for (i = 0; i < argc; i++) { |
if (do_file(agent_fd, deleting, key_only, |
if (do_file(agent_fd, deleting, key_only, |
argv[i], qflag, skprovider) == -1) |
argv[i], qflag, skprovider, |
|
dest_constraints, ndest_constraints) == -1) |
ret = 1; |
ret = 1; |
} |
} |
} |
} |