version 1.291, 2016/09/12 03:25:20 |
version 1.316, 2018/06/01 04:21:29 |
|
|
#include <string.h> |
#include <string.h> |
#include <unistd.h> |
#include <unistd.h> |
#include <limits.h> |
#include <limits.h> |
|
#include <locale.h> |
|
|
#include "xmalloc.h" |
#include "xmalloc.h" |
#include "sshkey.h" |
#include "sshkey.h" |
#include "rsa.h" |
|
#include "authfile.h" |
#include "authfile.h" |
#include "uuencode.h" |
#include "uuencode.h" |
#include "sshbuf.h" |
#include "sshbuf.h" |
|
|
#include "atomicio.h" |
#include "atomicio.h" |
#include "krl.h" |
#include "krl.h" |
#include "digest.h" |
#include "digest.h" |
|
#include "utf8.h" |
|
#include "authfd.h" |
|
|
#ifdef ENABLE_PKCS11 |
#ifdef ENABLE_PKCS11 |
#include "ssh-pkcs11.h" |
#include "ssh-pkcs11.h" |
|
|
/* Path to CA key when certifying keys. */ |
/* Path to CA key when certifying keys. */ |
char *ca_key_path = NULL; |
char *ca_key_path = NULL; |
|
|
|
/* Prefer to use agent keys for CA signing */ |
|
int prefer_agent = 0; |
|
|
/* Certificate serial number */ |
/* Certificate serial number */ |
unsigned long long cert_serial = 0; |
unsigned long long cert_serial = 0; |
|
|
|
|
char *certflags_command = NULL; |
char *certflags_command = NULL; |
char *certflags_src_addr = NULL; |
char *certflags_src_addr = NULL; |
|
|
|
/* Arbitrary extensions specified by user */ |
|
struct cert_userext { |
|
char *key; |
|
char *val; |
|
int crit; |
|
}; |
|
struct cert_userext *cert_userext; |
|
size_t ncert_userext; |
|
|
/* Conversion to/from various formats */ |
/* Conversion to/from various formats */ |
int convert_to = 0; |
int convert_to = 0; |
int convert_from = 0; |
int convert_from = 0; |
|
|
OPENSSL_DSA_MAX_MODULUS_BITS : OPENSSL_RSA_MAX_MODULUS_BITS; |
OPENSSL_DSA_MAX_MODULUS_BITS : OPENSSL_RSA_MAX_MODULUS_BITS; |
if (*bitsp > maxbits) |
if (*bitsp > maxbits) |
fatal("key bits exceeds maximum %d", maxbits); |
fatal("key bits exceeds maximum %d", maxbits); |
if (type == KEY_DSA && *bitsp != 1024) |
switch (type) { |
fatal("DSA keys must be 1024 bits"); |
case KEY_DSA: |
else if (type != KEY_ECDSA && type != KEY_ED25519 && *bitsp < 1024) |
if (*bitsp != 1024) |
fatal("Key must at least be 1024 bits"); |
fatal("Invalid DSA key length: must be 1024 bits"); |
else if (type == KEY_ECDSA && sshkey_ecdsa_bits_to_nid(*bitsp) == -1) |
break; |
fatal("Invalid ECDSA key length - valid lengths are " |
case KEY_RSA: |
"256, 384 or 521 bits"); |
if (*bitsp < SSH_RSA_MINIMUM_MODULUS_SIZE) |
|
fatal("Invalid RSA key length: minimum is %d bits", |
|
SSH_RSA_MINIMUM_MODULUS_SIZE); |
|
break; |
|
case KEY_ECDSA: |
|
if (sshkey_ecdsa_bits_to_nid(*bitsp) == -1) |
|
fatal("Invalid ECDSA key length: valid lengths are " |
|
"256, 384 or 521 bits"); |
|
} |
#endif |
#endif |
} |
} |
|
|
|
|
name = _PATH_SSH_CLIENT_ID_RSA; |
name = _PATH_SSH_CLIENT_ID_RSA; |
else { |
else { |
switch (sshkey_type_from_name(key_type_name)) { |
switch (sshkey_type_from_name(key_type_name)) { |
case KEY_RSA1: |
|
name = _PATH_SSH_CLIENT_IDENTITY; |
|
break; |
|
case KEY_DSA_CERT: |
case KEY_DSA_CERT: |
case KEY_DSA: |
case KEY_DSA: |
name = _PATH_SSH_CLIENT_ID_DSA; |
name = _PATH_SSH_CLIENT_ID_DSA; |
|
|
case KEY_ED25519_CERT: |
case KEY_ED25519_CERT: |
name = _PATH_SSH_CLIENT_ID_ED25519; |
name = _PATH_SSH_CLIENT_ID_ED25519; |
break; |
break; |
|
case KEY_XMSS: |
|
case KEY_XMSS_CERT: |
|
name = _PATH_SSH_CLIENT_ID_XMSS; |
|
break; |
default: |
default: |
fatal("bad key type"); |
fatal("bad key type"); |
} |
} |
|
|
char comment[61]; |
char comment[61]; |
int r; |
int r; |
|
|
if (k->type == KEY_RSA1) |
|
fatal("version 1 keys are not supported"); |
|
if ((r = sshkey_to_blob(k, &blob, &len)) != 0) |
if ((r = sshkey_to_blob(k, &blob, &len)) != 0) |
fatal("key_to_blob failed: %s", ssh_err(r)); |
fatal("key_to_blob failed: %s", ssh_err(r)); |
/* Comment + surrounds must fit into 72 chars (RFC 4716 sec 3.3) */ |
/* Comment + surrounds must fit into 72 chars (RFC 4716 sec 3.3) */ |
|
|
do_convert_to_pkcs8(struct sshkey *k) |
do_convert_to_pkcs8(struct sshkey *k) |
{ |
{ |
switch (sshkey_type_plain(k->type)) { |
switch (sshkey_type_plain(k->type)) { |
case KEY_RSA1: |
|
case KEY_RSA: |
case KEY_RSA: |
if (!PEM_write_RSA_PUBKEY(stdout, k->rsa)) |
if (!PEM_write_RSA_PUBKEY(stdout, k->rsa)) |
fatal("PEM_write_RSA_PUBKEY failed"); |
fatal("PEM_write_RSA_PUBKEY failed"); |
|
|
do_convert_to_pem(struct sshkey *k) |
do_convert_to_pem(struct sshkey *k) |
{ |
{ |
switch (sshkey_type_plain(k->type)) { |
switch (sshkey_type_plain(k->type)) { |
case KEY_RSA1: |
|
case KEY_RSA: |
case KEY_RSA: |
if (!PEM_write_RSAPublicKey(stdout, k->rsa)) |
if (!PEM_write_RSAPublicKey(stdout, k->rsa)) |
fatal("PEM_write_RSAPublicKey failed"); |
fatal("PEM_write_RSAPublicKey failed"); |
break; |
break; |
#if notyet /* OpenSSH 0.9.8 lacks this function */ |
|
case KEY_DSA: |
|
if (!PEM_write_DSAPublicKey(stdout, k->dsa)) |
|
fatal("PEM_write_DSAPublicKey failed"); |
|
break; |
|
#endif |
|
/* XXX ECDSA? */ |
|
default: |
default: |
fatal("%s: unsupported key type %s", __func__, sshkey_type(k)); |
fatal("%s: unsupported key type %s", __func__, sshkey_type(k)); |
} |
} |
|
|
return NULL; |
return NULL; |
} |
} |
if ((key = sshkey_new_private(ktype)) == NULL) |
if ((key = sshkey_new_private(ktype)) == NULL) |
fatal("key_new_private failed"); |
fatal("sshkey_new_private failed"); |
free(type); |
free(type); |
|
|
switch (key->type) { |
switch (key->type) { |
|
|
buffer_get_bignum_bits(b, key->rsa->iqmp); |
buffer_get_bignum_bits(b, key->rsa->iqmp); |
buffer_get_bignum_bits(b, key->rsa->q); |
buffer_get_bignum_bits(b, key->rsa->q); |
buffer_get_bignum_bits(b, key->rsa->p); |
buffer_get_bignum_bits(b, key->rsa->p); |
if ((r = rsa_generate_additional_parameters(key->rsa)) != 0) |
if ((r = ssh_rsa_generate_additional_parameters(key)) != 0) |
fatal("generate RSA parameters failed: %s", ssh_err(r)); |
fatal("generate RSA parameters failed: %s", ssh_err(r)); |
break; |
break; |
} |
} |
|
|
|
|
/* try the key */ |
/* try the key */ |
if (sshkey_sign(key, &sig, &slen, data, sizeof(data), NULL, 0) != 0 || |
if (sshkey_sign(key, &sig, &slen, data, sizeof(data), NULL, 0) != 0 || |
sshkey_verify(key, sig, slen, data, sizeof(data), 0) != 0) { |
sshkey_verify(key, sig, slen, data, sizeof(data), NULL, 0) != 0) { |
sshkey_free(key); |
sshkey_free(key); |
free(sig); |
free(sig); |
return NULL; |
return NULL; |
|
|
{ |
{ |
FILE *fp; |
FILE *fp; |
RSA *rsa; |
RSA *rsa; |
#ifdef notyet |
|
DSA *dsa; |
|
#endif |
|
|
|
if ((fp = fopen(identity_file, "r")) == NULL) |
if ((fp = fopen(identity_file, "r")) == NULL) |
fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); |
fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); |
|
|
fclose(fp); |
fclose(fp); |
return; |
return; |
} |
} |
#if notyet /* OpenSSH 0.9.8 lacks this function */ |
|
rewind(fp); |
|
if ((dsa = PEM_read_DSAPublicKey(fp, NULL, NULL, NULL)) != NULL) { |
|
if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) |
|
fatal("sshkey_new failed"); |
|
(*k)->type = KEY_DSA; |
|
(*k)->dsa = dsa; |
|
fclose(fp); |
|
return; |
|
} |
|
/* XXX ECDSA */ |
|
#endif |
|
fatal("%s: unrecognised raw private key format", __func__); |
fatal("%s: unrecognised raw private key format", __func__); |
} |
} |
|
|
|
|
fatal("%s: %s", identity_file, strerror(errno)); |
fatal("%s: %s", identity_file, strerror(errno)); |
prv = load_identity(identity_file); |
prv = load_identity(identity_file); |
if ((r = sshkey_write(prv, stdout)) != 0) |
if ((r = sshkey_write(prv, stdout)) != 0) |
error("key_write failed: %s", ssh_err(r)); |
error("sshkey_write failed: %s", ssh_err(r)); |
sshkey_free(prv); |
sshkey_free(prv); |
fprintf(stdout, "\n"); |
fprintf(stdout, "\n"); |
exit(0); |
exit(0); |
|
|
struct sshkey *ret; |
struct sshkey *ret; |
int r; |
int r; |
|
|
if ((ret = sshkey_new(KEY_RSA1)) == NULL) |
|
fatal("sshkey_new failed"); |
|
/* Try RSA1 */ |
|
if ((r = sshkey_read(ret, cpp)) == 0) |
|
return ret; |
|
/* Try modern */ |
|
sshkey_free(ret); |
|
if ((ret = sshkey_new(KEY_UNSPEC)) == NULL) |
if ((ret = sshkey_new(KEY_UNSPEC)) == NULL) |
fatal("sshkey_new failed"); |
fatal("sshkey_new failed"); |
if ((r = sshkey_read(ret, cpp)) == 0) |
if ((r = sshkey_read(ret, cpp)) == 0) |
|
|
ra = sshkey_fingerprint(public, fingerprint_hash, SSH_FP_RANDOMART); |
ra = sshkey_fingerprint(public, fingerprint_hash, SSH_FP_RANDOMART); |
if (fp == NULL || ra == NULL) |
if (fp == NULL || ra == NULL) |
fatal("%s: sshkey_fingerprint failed", __func__); |
fatal("%s: sshkey_fingerprint failed", __func__); |
printf("%u %s %s (%s)\n", sshkey_size(public), fp, |
mprintf("%u %s %s (%s)\n", sshkey_size(public), fp, |
comment ? comment : "no comment", sshkey_type(public)); |
comment ? comment : "no comment", sshkey_type(public)); |
if (log_level >= SYSLOG_LEVEL_VERBOSE) |
if (log_level >= SYSLOG_LEVEL_VERBOSE) |
printf("%s\n", ra); |
printf("%s\n", ra); |
|
|
char *path; |
char *path; |
} key_types[] = { |
} key_types[] = { |
#ifdef WITH_OPENSSL |
#ifdef WITH_OPENSSL |
#ifdef WITH_SSH1 |
|
{ "rsa1", "RSA1", _PATH_HOST_KEY_FILE }, |
|
#endif /* WITH_SSH1 */ |
|
{ "rsa", "RSA" ,_PATH_HOST_RSA_KEY_FILE }, |
{ "rsa", "RSA" ,_PATH_HOST_RSA_KEY_FILE }, |
{ "dsa", "DSA", _PATH_HOST_DSA_KEY_FILE }, |
{ "dsa", "DSA", _PATH_HOST_DSA_KEY_FILE }, |
{ "ecdsa", "ECDSA",_PATH_HOST_ECDSA_KEY_FILE }, |
{ "ecdsa", "ECDSA",_PATH_HOST_ECDSA_KEY_FILE }, |
#endif /* WITH_OPENSSL */ |
#endif /* WITH_OPENSSL */ |
{ "ed25519", "ED25519",_PATH_HOST_ED25519_KEY_FILE }, |
{ "ed25519", "ED25519",_PATH_HOST_ED25519_KEY_FILE }, |
|
#ifdef WITH_XMSS |
|
{ "xmss", "XMSS",_PATH_HOST_XMSS_KEY_FILE }, |
|
#endif /* WITH_XMSS */ |
{ NULL, NULL, NULL } |
{ NULL, NULL, NULL } |
}; |
}; |
|
|
int first = 0; |
int first = 0; |
struct stat st; |
struct stat st; |
struct sshkey *private, *public; |
struct sshkey *private, *public; |
char comment[1024]; |
char comment[1024], *prv_tmp, *pub_tmp, *prv_file, *pub_file; |
int i, type, fd, r; |
int i, type, fd, r; |
FILE *f; |
FILE *f; |
|
|
for (i = 0; key_types[i].key_type; i++) { |
for (i = 0; key_types[i].key_type; i++) { |
if (stat(key_types[i].path, &st) == 0) |
public = private = NULL; |
continue; |
prv_tmp = pub_tmp = prv_file = pub_file = NULL; |
if (errno != ENOENT) { |
|
|
xasprintf(&prv_file, "%s%s", |
|
identity_file, key_types[i].path); |
|
|
|
/* Check whether private key exists and is not zero-length */ |
|
if (stat(prv_file, &st) == 0) { |
|
if (st.st_size != 0) |
|
goto next; |
|
} else if (errno != ENOENT) { |
error("Could not stat %s: %s", key_types[i].path, |
error("Could not stat %s: %s", key_types[i].path, |
strerror(errno)); |
strerror(errno)); |
first = 0; |
goto failnext; |
continue; |
|
} |
} |
|
|
|
/* |
|
* Private key doesn't exist or is invalid; proceed with |
|
* key generation. |
|
*/ |
|
xasprintf(&prv_tmp, "%s%s.XXXXXXXXXX", |
|
identity_file, key_types[i].path); |
|
xasprintf(&pub_tmp, "%s%s.pub.XXXXXXXXXX", |
|
identity_file, key_types[i].path); |
|
xasprintf(&pub_file, "%s%s.pub", |
|
identity_file, key_types[i].path); |
|
|
if (first == 0) { |
if (first == 0) { |
first = 1; |
first = 1; |
printf("%s: generating new host keys: ", __progname); |
printf("%s: generating new host keys: ", __progname); |
|
|
printf("%s ", key_types[i].key_type_display); |
printf("%s ", key_types[i].key_type_display); |
fflush(stdout); |
fflush(stdout); |
type = sshkey_type_from_name(key_types[i].key_type); |
type = sshkey_type_from_name(key_types[i].key_type); |
strlcpy(identity_file, key_types[i].path, sizeof(identity_file)); |
if ((fd = mkstemp(prv_tmp)) == -1) { |
|
error("Could not save your public key in %s: %s", |
|
prv_tmp, strerror(errno)); |
|
goto failnext; |
|
} |
|
close(fd); /* just using mkstemp() to generate/reserve a name */ |
bits = 0; |
bits = 0; |
type_bits_valid(type, NULL, &bits); |
type_bits_valid(type, NULL, &bits); |
if ((r = sshkey_generate(type, bits, &private)) != 0) { |
if ((r = sshkey_generate(type, bits, &private)) != 0) { |
error("key_generate failed: %s", ssh_err(r)); |
error("sshkey_generate failed: %s", ssh_err(r)); |
first = 0; |
goto failnext; |
continue; |
|
} |
} |
if ((r = sshkey_from_private(private, &public)) != 0) |
if ((r = sshkey_from_private(private, &public)) != 0) |
fatal("sshkey_from_private failed: %s", ssh_err(r)); |
fatal("sshkey_from_private failed: %s", ssh_err(r)); |
snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, |
snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, |
hostname); |
hostname); |
if ((r = sshkey_save_private(private, identity_file, "", |
if ((r = sshkey_save_private(private, prv_tmp, "", |
comment, use_new_format, new_format_cipher, rounds)) != 0) { |
comment, use_new_format, new_format_cipher, rounds)) != 0) { |
error("Saving key \"%s\" failed: %s", |
error("Saving key \"%s\" failed: %s", |
identity_file, ssh_err(r)); |
prv_tmp, ssh_err(r)); |
sshkey_free(private); |
goto failnext; |
sshkey_free(public); |
|
first = 0; |
|
continue; |
|
} |
} |
sshkey_free(private); |
if ((fd = mkstemp(pub_tmp)) == -1) { |
strlcat(identity_file, ".pub", sizeof(identity_file)); |
error("Could not save your public key in %s: %s", |
fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); |
pub_tmp, strerror(errno)); |
if (fd == -1) { |
goto failnext; |
error("Could not save your public key in %s", |
|
identity_file); |
|
sshkey_free(public); |
|
first = 0; |
|
continue; |
|
} |
} |
|
(void)fchmod(fd, 0644); |
f = fdopen(fd, "w"); |
f = fdopen(fd, "w"); |
if (f == NULL) { |
if (f == NULL) { |
error("fdopen %s failed", identity_file); |
error("fdopen %s failed: %s", pub_tmp, strerror(errno)); |
close(fd); |
close(fd); |
sshkey_free(public); |
goto failnext; |
first = 0; |
|
continue; |
|
} |
} |
if ((r = sshkey_write(public, f)) != 0) { |
if ((r = sshkey_write(public, f)) != 0) { |
error("write key failed: %s", ssh_err(r)); |
error("write key failed: %s", ssh_err(r)); |
fclose(f); |
fclose(f); |
sshkey_free(public); |
goto failnext; |
first = 0; |
|
continue; |
|
} |
} |
fprintf(f, " %s\n", comment); |
fprintf(f, " %s\n", comment); |
fclose(f); |
if (ferror(f) != 0) { |
sshkey_free(public); |
error("write key failed: %s", strerror(errno)); |
|
fclose(f); |
|
goto failnext; |
|
} |
|
if (fclose(f) != 0) { |
|
error("key close failed: %s", strerror(errno)); |
|
goto failnext; |
|
} |
|
|
|
/* Rename temporary files to their permanent locations. */ |
|
if (rename(pub_tmp, pub_file) != 0) { |
|
error("Unable to move %s into position: %s", |
|
pub_file, strerror(errno)); |
|
goto failnext; |
|
} |
|
if (rename(prv_tmp, prv_file) != 0) { |
|
error("Unable to move %s into position: %s", |
|
key_types[i].path, strerror(errno)); |
|
failnext: |
|
first = 0; |
|
goto next; |
|
} |
|
next: |
|
sshkey_free(private); |
|
sshkey_free(public); |
|
free(prv_tmp); |
|
free(pub_tmp); |
|
free(prv_file); |
|
free(pub_file); |
} |
} |
if (first != 0) |
if (first != 0) |
printf("\n"); |
printf("\n"); |
|
|
struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx; |
struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx; |
char *hashed, *cp, *hosts, *ohosts; |
char *hashed, *cp, *hosts, *ohosts; |
int has_wild = l->hosts && strcspn(l->hosts, "*?!") != strlen(l->hosts); |
int has_wild = l->hosts && strcspn(l->hosts, "*?!") != strlen(l->hosts); |
|
int was_hashed = l->hosts && l->hosts[0] == HASH_DELIM; |
|
|
switch (l->status) { |
switch (l->status) { |
case HKF_STATUS_OK: |
case HKF_STATUS_OK: |
|
|
* Don't hash hosts already already hashed, with wildcard |
* Don't hash hosts already already hashed, with wildcard |
* characters or a CA/revocation marker. |
* characters or a CA/revocation marker. |
*/ |
*/ |
if ((l->match & HKF_MATCH_HOST_HASHED) != 0 || |
if (was_hashed || has_wild || l->marker != MRK_NONE) { |
has_wild || l->marker != MRK_NONE) { |
|
fprintf(ctx->out, "%s\n", l->line); |
fprintf(ctx->out, "%s\n", l->line); |
if (has_wild && !find_host) { |
if (has_wild && !find_host) { |
logit("%s:%ld: ignoring host name " |
logit("%s:%lu: ignoring host name " |
"with wildcard: %.64s", l->path, |
"with wildcard: %.64s", l->path, |
l->linenum, l->hosts); |
l->linenum, l->hosts); |
} |
} |
|
|
*/ |
*/ |
ohosts = hosts = xstrdup(l->hosts); |
ohosts = hosts = xstrdup(l->hosts); |
while ((cp = strsep(&hosts, ",")) != NULL && *cp != '\0') { |
while ((cp = strsep(&hosts, ",")) != NULL && *cp != '\0') { |
|
lowercase(cp); |
if ((hashed = host_hash(cp, NULL, 0)) == NULL) |
if ((hashed = host_hash(cp, NULL, 0)) == NULL) |
fatal("hash_host failed"); |
fatal("hash_host failed"); |
fprintf(ctx->out, "%s %s\n", hashed, l->rawkey); |
fprintf(ctx->out, "%s %s\n", hashed, l->rawkey); |
|
|
case HKF_STATUS_INVALID: |
case HKF_STATUS_INVALID: |
/* Retain invalid lines, but mark file as invalid. */ |
/* Retain invalid lines, but mark file as invalid. */ |
ctx->invalid = 1; |
ctx->invalid = 1; |
logit("%s:%ld: invalid line", l->path, l->linenum); |
logit("%s:%lu: invalid line", l->path, l->linenum); |
/* FALLTHROUGH */ |
/* FALLTHROUGH */ |
default: |
default: |
fprintf(ctx->out, "%s\n", l->line); |
fprintf(ctx->out, "%s\n", l->line); |
|
|
*/ |
*/ |
ctx->found_key = 1; |
ctx->found_key = 1; |
if (!quiet) |
if (!quiet) |
printf("# Host %s found: line %ld\n", |
printf("# Host %s found: line %lu\n", |
ctx->host, l->linenum); |
ctx->host, l->linenum); |
} |
} |
return 0; |
return 0; |
} else if (find_host) { |
} else if (find_host) { |
ctx->found_key = 1; |
ctx->found_key = 1; |
if (!quiet) { |
if (!quiet) { |
printf("# Host %s found: line %ld %s\n", |
printf("# Host %s found: line %lu %s\n", |
ctx->host, |
ctx->host, |
l->linenum, l->marker == MRK_CA ? "CA" : |
l->linenum, l->marker == MRK_CA ? "CA" : |
(l->marker == MRK_REVOKE ? "REVOKED" : "")); |
(l->marker == MRK_REVOKE ? "REVOKED" : "")); |
|
|
known_hosts_hash(l, ctx); |
known_hosts_hash(l, ctx); |
else if (print_fingerprint) { |
else if (print_fingerprint) { |
fp = sshkey_fingerprint(l->key, fptype, rep); |
fp = sshkey_fingerprint(l->key, fptype, rep); |
printf("%s %s %s %s\n", ctx->host, |
mprintf("%s %s %s %s\n", ctx->host, |
sshkey_type(l->key), fp, l->comment); |
sshkey_type(l->key), fp, l->comment); |
free(fp); |
free(fp); |
} else |
} else |
|
|
/* Retain non-matching hosts when deleting */ |
/* Retain non-matching hosts when deleting */ |
if (l->status == HKF_STATUS_INVALID) { |
if (l->status == HKF_STATUS_INVALID) { |
ctx->invalid = 1; |
ctx->invalid = 1; |
logit("%s:%ld: invalid line", l->path, l->linenum); |
logit("%s:%lu: invalid line", l->path, l->linenum); |
} |
} |
fprintf(ctx->out, "%s\n", l->line); |
fprintf(ctx->out, "%s\n", l->line); |
} |
} |
|
|
} |
} |
inplace = 1; |
inplace = 1; |
} |
} |
|
|
/* XXX support identity_file == "-" for stdin */ |
/* XXX support identity_file == "-" for stdin */ |
foreach_options = find_host ? HKF_WANT_MATCH : 0; |
foreach_options = find_host ? HKF_WANT_MATCH : 0; |
foreach_options |= print_fingerprint ? HKF_WANT_PARSE_KEY : 0; |
foreach_options |= print_fingerprint ? HKF_WANT_PARSE_KEY : 0; |
if ((r = hostkeys_foreach(identity_file, |
if ((r = hostkeys_foreach(identity_file, (find_host || !hash_hosts) ? |
hash_hosts ? known_hosts_hash : known_hosts_find_delete, &ctx, |
known_hosts_find_delete : known_hosts_hash, &ctx, name, NULL, |
name, NULL, foreach_options)) != 0) { |
foreach_options)) != 0) { |
if (inplace) |
if (inplace) |
unlink(tmp); |
unlink(tmp); |
fatal("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r)); |
fatal("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r)); |
|
|
fatal("Failed to load key %s: %s", identity_file, ssh_err(r)); |
fatal("Failed to load key %s: %s", identity_file, ssh_err(r)); |
} |
} |
if (comment) |
if (comment) |
printf("Key has comment '%s'\n", comment); |
mprintf("Key has comment '%s'\n", comment); |
|
|
/* Ask the new passphrase (twice). */ |
/* Ask the new passphrase (twice). */ |
if (identity_new_passphrase) { |
if (identity_new_passphrase) { |
|
|
} |
} |
} |
} |
|
|
if (private->type != KEY_RSA1 && private->type != KEY_ED25519 && |
if (private->type != KEY_ED25519 && private->type != KEY_XMSS && |
!use_new_format) { |
!use_new_format) { |
error("Comments are only supported for RSA1 or keys stored in " |
error("Comments are only supported for keys stored in " |
"the new format (-o)."); |
"the new format (-o)."); |
explicit_bzero(passphrase, strlen(passphrase)); |
explicit_bzero(passphrase, strlen(passphrase)); |
sshkey_free(private); |
sshkey_free(private); |
exit(1); |
exit(1); |
} |
} |
printf("Key now has comment '%s'\n", comment); |
if (comment) |
|
printf("Key now has comment '%s'\n", comment); |
|
else |
|
printf("Key now has no comment\n"); |
|
|
if (identity_comment) { |
if (identity_comment) { |
strlcpy(new_comment, identity_comment, sizeof(new_comment)); |
strlcpy(new_comment, identity_comment, sizeof(new_comment)); |
|
|
explicit_bzero(passphrase, strlen(passphrase)); |
explicit_bzero(passphrase, strlen(passphrase)); |
free(passphrase); |
free(passphrase); |
if ((r = sshkey_from_private(private, &public)) != 0) |
if ((r = sshkey_from_private(private, &public)) != 0) |
fatal("key_from_private failed: %s", ssh_err(r)); |
fatal("sshkey_from_private failed: %s", ssh_err(r)); |
sshkey_free(private); |
sshkey_free(private); |
|
|
strlcat(identity_file, ".pub", sizeof(identity_file)); |
strlcat(identity_file, ".pub", sizeof(identity_file)); |
|
|
static void |
static void |
prepare_options_buf(struct sshbuf *c, int which) |
prepare_options_buf(struct sshbuf *c, int which) |
{ |
{ |
|
size_t i; |
|
|
sshbuf_reset(c); |
sshbuf_reset(c); |
if ((which & OPTIONS_CRITICAL) != 0 && |
if ((which & OPTIONS_CRITICAL) != 0 && |
certflags_command != NULL) |
certflags_command != NULL) |
|
|
if ((which & OPTIONS_CRITICAL) != 0 && |
if ((which & OPTIONS_CRITICAL) != 0 && |
certflags_src_addr != NULL) |
certflags_src_addr != NULL) |
add_string_option(c, "source-address", certflags_src_addr); |
add_string_option(c, "source-address", certflags_src_addr); |
|
for (i = 0; i < ncert_userext; i++) { |
|
if ((cert_userext[i].crit && (which & OPTIONS_EXTENSIONS)) || |
|
(!cert_userext[i].crit && (which & OPTIONS_CRITICAL))) |
|
continue; |
|
if (cert_userext[i].val == NULL) |
|
add_flag_option(c, cert_userext[i].key); |
|
else { |
|
add_string_option(c, cert_userext[i].key, |
|
cert_userext[i].val); |
|
} |
|
} |
} |
} |
|
|
static struct sshkey * |
static struct sshkey * |
|
|
#endif /* ENABLE_PKCS11 */ |
#endif /* ENABLE_PKCS11 */ |
} |
} |
|
|
|
/* Signer for sshkey_certify_custom that uses the agent */ |
|
static int |
|
agent_signer(const struct sshkey *key, u_char **sigp, size_t *lenp, |
|
const u_char *data, size_t datalen, |
|
const char *alg, u_int compat, void *ctx) |
|
{ |
|
int *agent_fdp = (int *)ctx; |
|
|
|
return ssh_agent_sign(*agent_fdp, key, sigp, lenp, |
|
data, datalen, alg, compat); |
|
} |
|
|
static void |
static void |
do_ca_sign(struct passwd *pw, int argc, char **argv) |
do_ca_sign(struct passwd *pw, int argc, char **argv) |
{ |
{ |
int r, i, fd; |
int r, i, fd, found, agent_fd = -1; |
u_int n; |
u_int n; |
struct sshkey *ca, *public; |
struct sshkey *ca, *public; |
char valid[64], *otmp, *tmp, *cp, *out, *comment, **plist = NULL; |
char valid[64], *otmp, *tmp, *cp, *out, *comment, **plist = NULL; |
FILE *f; |
FILE *f; |
|
struct ssh_identitylist *agent_ids; |
|
size_t j; |
|
|
#ifdef ENABLE_PKCS11 |
#ifdef ENABLE_PKCS11 |
pkcs11_init(1); |
pkcs11_init(1); |
#endif |
#endif |
tmp = tilde_expand_filename(ca_key_path, pw->pw_uid); |
tmp = tilde_expand_filename(ca_key_path, pw->pw_uid); |
if (pkcs11provider != NULL) { |
if (pkcs11provider != NULL) { |
|
/* If a PKCS#11 token was specified then try to use it */ |
if ((ca = load_pkcs11_key(tmp)) == NULL) |
if ((ca = load_pkcs11_key(tmp)) == NULL) |
fatal("No PKCS#11 key matching %s found", ca_key_path); |
fatal("No PKCS#11 key matching %s found", ca_key_path); |
} else |
} else if (prefer_agent) { |
|
/* |
|
* Agent signature requested. Try to use agent after making |
|
* sure the public key specified is actually present in the |
|
* agent. |
|
*/ |
|
if ((r = sshkey_load_public(tmp, &ca, NULL)) != 0) |
|
fatal("Cannot load CA public key %s: %s", |
|
tmp, ssh_err(r)); |
|
if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) |
|
fatal("Cannot use public key for CA signature: %s", |
|
ssh_err(r)); |
|
if ((r = ssh_fetch_identitylist(agent_fd, &agent_ids)) != 0) |
|
fatal("Retrieve agent key list: %s", ssh_err(r)); |
|
found = 0; |
|
for (j = 0; j < agent_ids->nkeys; j++) { |
|
if (sshkey_equal(ca, agent_ids->keys[j])) { |
|
found = 1; |
|
break; |
|
} |
|
} |
|
if (!found) |
|
fatal("CA key %s not found in agent", tmp); |
|
ssh_free_identitylist(agent_ids); |
|
ca->flags |= SSHKEY_FLAG_EXT; |
|
} else { |
|
/* CA key is assumed to be a private key on the filesystem */ |
ca = load_identity(tmp); |
ca = load_identity(tmp); |
|
} |
free(tmp); |
free(tmp); |
|
|
if (key_type_name != NULL && |
if (key_type_name != NULL && |
|
|
} |
} |
free(otmp); |
free(otmp); |
} |
} |
|
if (n > SSHKEY_CERT_MAX_PRINCIPALS) |
|
fatal("Too many certificate principals specified"); |
|
|
tmp = tilde_expand_filename(argv[i], pw->pw_uid); |
tmp = tilde_expand_filename(argv[i], pw->pw_uid); |
if ((r = sshkey_load_public(tmp, &public, &comment)) != 0) |
if ((r = sshkey_load_public(tmp, &public, &comment)) != 0) |
fatal("%s: unable to open \"%s\": %s", |
fatal("%s: unable to open \"%s\": %s", |
__func__, tmp, ssh_err(r)); |
__func__, tmp, ssh_err(r)); |
if (public->type != KEY_RSA && public->type != KEY_DSA && |
if (public->type != KEY_RSA && public->type != KEY_DSA && |
public->type != KEY_ECDSA && public->type != KEY_ED25519) |
public->type != KEY_ECDSA && public->type != KEY_ED25519 && |
|
public->type != KEY_XMSS) |
fatal("%s: key \"%s\" type %s cannot be certified", |
fatal("%s: key \"%s\" type %s cannot be certified", |
__func__, tmp, sshkey_type(public)); |
__func__, tmp, sshkey_type(public)); |
|
|
|
|
OPTIONS_EXTENSIONS); |
OPTIONS_EXTENSIONS); |
if ((r = sshkey_from_private(ca, |
if ((r = sshkey_from_private(ca, |
&public->cert->signature_key)) != 0) |
&public->cert->signature_key)) != 0) |
fatal("key_from_private (ca key): %s", ssh_err(r)); |
fatal("sshkey_from_private (ca key): %s", ssh_err(r)); |
|
|
if ((r = sshkey_certify(public, ca, key_type_name)) != 0) |
if (agent_fd != -1 && (ca->flags & SSHKEY_FLAG_EXT) != 0) { |
fatal("Couldn't certify key %s: %s", tmp, ssh_err(r)); |
if ((r = sshkey_certify_custom(public, ca, |
|
key_type_name, agent_signer, &agent_fd)) != 0) |
|
fatal("Couldn't certify key %s via agent: %s", |
|
tmp, ssh_err(r)); |
|
} else { |
|
if ((sshkey_certify(public, ca, key_type_name)) != 0) |
|
fatal("Couldn't certify key %s: %s", |
|
tmp, ssh_err(r)); |
|
} |
|
|
if ((cp = strrchr(tmp, '.')) != NULL && strcmp(cp, ".pub") == 0) |
if ((cp = strrchr(tmp, '.')) != NULL && strcmp(cp, ".pub") == 0) |
*cp = '\0'; |
*cp = '\0'; |
|
|
return now + (u_int64_t)(secs * mul); |
return now + (u_int64_t)(secs * mul); |
} |
} |
|
|
static u_int64_t |
|
parse_absolute_time(const char *s) |
|
{ |
|
struct tm tm; |
|
time_t tt; |
|
char buf[32], *fmt; |
|
|
|
/* |
|
* POSIX strptime says "The application shall ensure that there |
|
* is white-space or other non-alphanumeric characters between |
|
* any two conversion specifications" so arrange things this way. |
|
*/ |
|
switch (strlen(s)) { |
|
case 8: |
|
fmt = "%Y-%m-%d"; |
|
snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2s", s, s + 4, s + 6); |
|
break; |
|
case 14: |
|
fmt = "%Y-%m-%dT%H:%M:%S"; |
|
snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2sT%.2s:%.2s:%.2s", |
|
s, s + 4, s + 6, s + 8, s + 10, s + 12); |
|
break; |
|
default: |
|
fatal("Invalid certificate time format %s", s); |
|
} |
|
|
|
memset(&tm, 0, sizeof(tm)); |
|
if (strptime(buf, fmt, &tm) == NULL) |
|
fatal("Invalid certificate time %s", s); |
|
if ((tt = mktime(&tm)) < 0) |
|
fatal("Certificate time %s cannot be represented", s); |
|
return (u_int64_t)tt; |
|
} |
|
|
|
static void |
static void |
parse_cert_times(char *timespec) |
parse_cert_times(char *timespec) |
{ |
{ |
|
|
|
|
/* |
/* |
* from:to, where |
* from:to, where |
* from := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS |
* from := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS | "always" |
* to := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS |
* to := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS | "forever" |
*/ |
*/ |
from = xstrdup(timespec); |
from = xstrdup(timespec); |
to = strchr(from, ':'); |
to = strchr(from, ':'); |
|
|
|
|
if (*from == '-' || *from == '+') |
if (*from == '-' || *from == '+') |
cert_valid_from = parse_relative_time(from, now); |
cert_valid_from = parse_relative_time(from, now); |
else |
else if (strcmp(from, "always") == 0) |
cert_valid_from = parse_absolute_time(from); |
cert_valid_from = 0; |
|
else if (parse_absolute_time(from, &cert_valid_from) != 0) |
|
fatal("Invalid from time \"%s\"", from); |
|
|
if (*to == '-' || *to == '+') |
if (*to == '-' || *to == '+') |
cert_valid_to = parse_relative_time(to, now); |
cert_valid_to = parse_relative_time(to, now); |
else |
else if (strcmp(to, "forever") == 0) |
cert_valid_to = parse_absolute_time(to); |
cert_valid_to = ~(u_int64_t)0; |
|
else if (parse_absolute_time(to, &cert_valid_to) != 0) |
|
fatal("Invalid to time \"%s\"", to); |
|
|
if (cert_valid_to <= cert_valid_from) |
if (cert_valid_to <= cert_valid_from) |
fatal("Empty certificate validity interval"); |
fatal("Empty certificate validity interval"); |
|
|
static void |
static void |
add_cert_option(char *opt) |
add_cert_option(char *opt) |
{ |
{ |
char *val; |
char *val, *cp; |
|
int iscrit = 0; |
|
|
if (strcasecmp(opt, "clear") == 0) |
if (strcasecmp(opt, "clear") == 0) |
certflags_flags = 0; |
certflags_flags = 0; |
|
|
if (addr_match_cidr_list(NULL, val) != 0) |
if (addr_match_cidr_list(NULL, val) != 0) |
fatal("Invalid source-address list"); |
fatal("Invalid source-address list"); |
certflags_src_addr = xstrdup(val); |
certflags_src_addr = xstrdup(val); |
|
} else if (strncasecmp(opt, "extension:", 10) == 0 || |
|
(iscrit = (strncasecmp(opt, "critical:", 9) == 0))) { |
|
val = xstrdup(strchr(opt, ':') + 1); |
|
if ((cp = strchr(val, '=')) != NULL) |
|
*cp++ = '\0'; |
|
cert_userext = xreallocarray(cert_userext, ncert_userext + 1, |
|
sizeof(*cert_userext)); |
|
cert_userext[ncert_userext].key = val; |
|
cert_userext[ncert_userext].val = cp == NULL ? |
|
NULL : xstrdup(cp); |
|
cert_userext[ncert_userext].crit = iscrit; |
|
ncert_userext++; |
} else |
} else |
fatal("Unsupported certificate option \"%s\"", opt); |
fatal("Unsupported certificate option \"%s\"", opt); |
} |
} |
|
|
if (*cp == '#' || *cp == '\0') |
if (*cp == '#' || *cp == '\0') |
continue; |
continue; |
if ((key = sshkey_new(KEY_UNSPEC)) == NULL) |
if ((key = sshkey_new(KEY_UNSPEC)) == NULL) |
fatal("key_new"); |
fatal("sshkey_new"); |
if ((r = sshkey_read(key, &cp)) != 0) { |
if ((r = sshkey_read(key, &cp)) != 0) { |
error("%s:%lu: invalid key: %s", path, |
error("%s:%lu: invalid key: %s", path, |
lnum, ssh_err(r)); |
lnum, ssh_err(r)); |
|
|
*/ |
*/ |
} |
} |
if ((key = sshkey_new(KEY_UNSPEC)) == NULL) |
if ((key = sshkey_new(KEY_UNSPEC)) == NULL) |
fatal("key_new"); |
fatal("sshkey_new"); |
if ((r = sshkey_read(key, &cp)) != 0) |
if ((r = sshkey_read(key, &cp)) != 0) |
fatal("%s:%lu: invalid key: %s", |
fatal("%s:%lu: invalid key: %s", |
path, lnum, ssh_err(r)); |
path, lnum, ssh_err(r)); |
|
|
usage(void) |
usage(void) |
{ |
{ |
fprintf(stderr, |
fprintf(stderr, |
"usage: ssh-keygen [-q] [-b bits] [-t dsa | ecdsa | ed25519 | rsa | rsa1]\n" |
"usage: ssh-keygen [-q] [-b bits] [-t dsa | ecdsa | ed25519 | rsa]\n" |
" [-N new_passphrase] [-C comment] [-f output_keyfile]\n" |
" [-N new_passphrase] [-C comment] [-f output_keyfile]\n" |
" ssh-keygen -p [-P old_passphrase] [-N new_passphrase] [-f keyfile]\n" |
" ssh-keygen -p [-P old_passphrase] [-N new_passphrase] [-f keyfile]\n" |
" ssh-keygen -i [-m key_format] [-f input_keyfile]\n" |
" ssh-keygen -i [-m key_format] [-f input_keyfile]\n" |
|
|
" ssh-keygen -T output_file -f input_file [-v] [-a rounds] [-J num_lines]\n" |
" ssh-keygen -T output_file -f input_file [-v] [-a rounds] [-J num_lines]\n" |
" [-j start_line] [-K checkpt] [-W generator]\n" |
" [-j start_line] [-K checkpt] [-W generator]\n" |
#endif |
#endif |
" ssh-keygen -s ca_key -I certificate_identity [-h] [-n principals]\n" |
" ssh-keygen -s ca_key -I certificate_identity [-h] [-U]\n" |
" [-O option] [-V validity_interval] [-z serial_number] file ...\n" |
" [-D pkcs11_provider] [-n principals] [-O option]\n" |
|
" [-V validity_interval] [-z serial_number] file ...\n" |
" ssh-keygen -L [-f input_keyfile]\n" |
" ssh-keygen -L [-f input_keyfile]\n" |
" ssh-keygen -A\n" |
" ssh-keygen -A\n" |
" ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n" |
" ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n" |
|
|
OpenSSL_add_all_algorithms(); |
OpenSSL_add_all_algorithms(); |
log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1); |
log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1); |
|
|
|
setlocale(LC_CTYPE, ""); |
|
|
/* we need this for the home * directory. */ |
/* we need this for the home * directory. */ |
pw = getpwuid(getuid()); |
pw = getpwuid(getuid()); |
if (!pw) |
if (!pw) |
|
|
if (gethostname(hostname, sizeof(hostname)) < 0) |
if (gethostname(hostname, sizeof(hostname)) < 0) |
fatal("gethostname: %s", strerror(errno)); |
fatal("gethostname: %s", strerror(errno)); |
|
|
/* Remaining characters: UYdw */ |
/* Remaining characters: Ydw */ |
while ((opt = getopt(argc, argv, "ABHLQXceghiklopquvxy" |
while ((opt = getopt(argc, argv, "ABHLQUXceghiklopquvxy" |
"C:D:E:F:G:I:J:K:M:N:O:P:R:S:T:V:W:Z:" |
"C:D:E:F:G:I:J:K:M:N:O:P:R:S:T:V:W:Z:" |
"a:b:f:g:j:m:n:r:s:t:z:")) != -1) { |
"a:b:f:g:j:m:n:r:s:t:z:")) != -1) { |
switch (opt) { |
switch (opt) { |
|
|
gen_all_hostkeys = 1; |
gen_all_hostkeys = 1; |
break; |
break; |
case 'b': |
case 'b': |
bits = (u_int32_t)strtonum(optarg, 256, 32768, &errstr); |
bits = (u_int32_t)strtonum(optarg, 10, 32768, &errstr); |
if (errstr) |
if (errstr) |
fatal("Bits has bad value %s (%s)", |
fatal("Bits has bad value %s (%s)", |
optarg, errstr); |
optarg, errstr); |
|
|
case 'D': |
case 'D': |
pkcs11provider = optarg; |
pkcs11provider = optarg; |
break; |
break; |
|
case 'U': |
|
prefer_agent = 1; |
|
break; |
case 'u': |
case 'u': |
update_krl = 1; |
update_krl = 1; |
break; |
break; |
|
|
break; |
break; |
case 'J': |
case 'J': |
lines_to_process = strtoul(optarg, NULL, 10); |
lines_to_process = strtoul(optarg, NULL, 10); |
break; |
break; |
case 'j': |
case 'j': |
start_lineno = strtoul(optarg, NULL, 10); |
start_lineno = strtoul(optarg, NULL, 10); |
break; |
break; |
case 'K': |
case 'K': |
if (strlen(optarg) >= PATH_MAX) |
if (strlen(optarg) >= PATH_MAX) |
fatal("Checkpoint filename too long"); |
fatal("Checkpoint filename too long"); |
|
|
_PATH_HOST_ECDSA_KEY_FILE, rr_hostname); |
_PATH_HOST_ECDSA_KEY_FILE, rr_hostname); |
n += do_print_resource_record(pw, |
n += do_print_resource_record(pw, |
_PATH_HOST_ED25519_KEY_FILE, rr_hostname); |
_PATH_HOST_ED25519_KEY_FILE, rr_hostname); |
|
n += do_print_resource_record(pw, |
|
_PATH_HOST_XMSS_KEY_FILE, rr_hostname); |
if (n == 0) |
if (n == 0) |
fatal("no keys found."); |
fatal("no keys found."); |
exit(0); |
exit(0); |
|
|
printf("Generating public/private %s key pair.\n", |
printf("Generating public/private %s key pair.\n", |
key_type_name); |
key_type_name); |
if ((r = sshkey_generate(type, bits, &private)) != 0) |
if ((r = sshkey_generate(type, bits, &private)) != 0) |
fatal("key_generate failed"); |
fatal("sshkey_generate failed"); |
if ((r = sshkey_from_private(private, &public)) != 0) |
if ((r = sshkey_from_private(private, &public)) != 0) |
fatal("key_from_private failed: %s\n", ssh_err(r)); |
fatal("sshkey_from_private failed: %s\n", ssh_err(r)); |
|
|
if (!have_identity) |
if (!have_identity) |
ask_filename(pw, "Enter file in which to save the key"); |
ask_filename(pw, "Enter file in which to save the key"); |
|
|
if ((r = sshkey_write(public, f)) != 0) |
if ((r = sshkey_write(public, f)) != 0) |
error("write key failed: %s", ssh_err(r)); |
error("write key failed: %s", ssh_err(r)); |
fprintf(f, " %s\n", comment); |
fprintf(f, " %s\n", comment); |
fclose(f); |
if (ferror(f) || fclose(f) != 0) |
|
fatal("write public failed: %s", strerror(errno)); |
|
|
if (!quiet) { |
if (!quiet) { |
fp = sshkey_fingerprint(public, fingerprint_hash, |
fp = sshkey_fingerprint(public, fingerprint_hash, |