summaryrefslogtreecommitdiff
path: root/ssh-keygen.c
diff options
context:
space:
mode:
Diffstat (limited to 'ssh-keygen.c')
-rw-r--r--ssh-keygen.c292
1 files changed, 203 insertions, 89 deletions
diff --git a/ssh-keygen.c b/ssh-keygen.c
index f17af036bbfa..835f7d0169ba 100644
--- a/ssh-keygen.c
+++ b/ssh-keygen.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-keygen.c,v 1.299 2017/03/10 04:26:06 djm Exp $ */
+/* $OpenBSD: ssh-keygen.c,v 1.307 2017/07/07 03:53:12 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -41,7 +41,6 @@
#include "xmalloc.h"
#include "sshkey.h"
-#include "rsa.h"
#include "authfile.h"
#include "uuencode.h"
#include "sshbuf.h"
@@ -59,6 +58,7 @@
#include "krl.h"
#include "digest.h"
#include "utf8.h"
+#include "authfd.h"
#ifdef WITH_OPENSSL
# define DEFAULT_KEY_TYPE_NAME "rsa"
@@ -121,6 +121,9 @@ char *identity_comment = NULL;
/* Path to CA key when certifying keys. */
char *ca_key_path = NULL;
+/* Prefer to use agent keys for CA signing */
+int prefer_agent = 0;
+
/* Certificate serial number */
unsigned long long cert_serial = 0;
@@ -149,6 +152,15 @@ u_int32_t certflags_flags = CERTOPT_DEFAULT;
char *certflags_command = 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 */
int convert_to = 0;
int convert_from = 0;
@@ -217,13 +229,21 @@ type_bits_valid(int type, const char *name, u_int32_t *bitsp)
OPENSSL_DSA_MAX_MODULUS_BITS : OPENSSL_RSA_MAX_MODULUS_BITS;
if (*bitsp > maxbits)
fatal("key bits exceeds maximum %d", maxbits);
- if (type == KEY_DSA && *bitsp != 1024)
- fatal("DSA keys must be 1024 bits");
- else if (type != KEY_ECDSA && type != KEY_ED25519 && *bitsp < 1024)
- fatal("Key must at least be 1024 bits");
- else if (type == KEY_ECDSA && sshkey_ecdsa_bits_to_nid(*bitsp) == -1)
- fatal("Invalid ECDSA key length - valid lengths are "
- "256, 384 or 521 bits");
+ switch (type) {
+ case KEY_DSA:
+ if (*bitsp != 1024)
+ fatal("Invalid DSA key length: must be 1024 bits");
+ break;
+ case KEY_RSA:
+ 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
}
@@ -237,9 +257,6 @@ ask_filename(struct passwd *pw, const char *prompt)
name = _PATH_SSH_CLIENT_ID_RSA;
else {
switch (sshkey_type_from_name(key_type_name)) {
- case KEY_RSA1:
- name = _PATH_SSH_CLIENT_IDENTITY;
- break;
case KEY_DSA_CERT:
case KEY_DSA:
name = _PATH_SSH_CLIENT_ID_DSA;
@@ -311,8 +328,6 @@ do_convert_to_ssh2(struct passwd *pw, struct sshkey *k)
char comment[61];
int r;
- if (k->type == KEY_RSA1)
- fatal("version 1 keys are not supported");
if ((r = sshkey_to_blob(k, &blob, &len)) != 0)
fatal("key_to_blob failed: %s", ssh_err(r));
/* Comment + surrounds must fit into 72 chars (RFC 4716 sec 3.3) */
@@ -334,7 +349,6 @@ static void
do_convert_to_pkcs8(struct sshkey *k)
{
switch (sshkey_type_plain(k->type)) {
- case KEY_RSA1:
case KEY_RSA:
if (!PEM_write_RSA_PUBKEY(stdout, k->rsa))
fatal("PEM_write_RSA_PUBKEY failed");
@@ -359,7 +373,6 @@ static void
do_convert_to_pem(struct sshkey *k)
{
switch (sshkey_type_plain(k->type)) {
- case KEY_RSA1:
case KEY_RSA:
if (!PEM_write_RSAPublicKey(stdout, k->rsa))
fatal("PEM_write_RSAPublicKey failed");
@@ -478,7 +491,7 @@ do_convert_private_ssh2_from_blob(u_char *blob, u_int blen)
return NULL;
}
if ((key = sshkey_new_private(ktype)) == NULL)
- fatal("key_new_private failed");
+ fatal("sshkey_new_private failed");
free(type);
switch (key->type) {
@@ -514,7 +527,7 @@ do_convert_private_ssh2_from_blob(u_char *blob, u_int blen)
buffer_get_bignum_bits(b, key->rsa->iqmp);
buffer_get_bignum_bits(b, key->rsa->q);
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));
break;
}
@@ -760,7 +773,7 @@ do_print_public(struct passwd *pw)
fatal("%s: %s", identity_file, strerror(errno));
prv = load_identity(identity_file);
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);
fprintf(stdout, "\n");
exit(0);
@@ -816,13 +829,6 @@ try_read_key(char **cpp)
struct sshkey *ret;
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)
fatal("sshkey_new failed");
if ((r = sshkey_read(ret, cpp)) == 0)
@@ -978,9 +984,6 @@ do_gen_all_hostkeys(struct passwd *pw)
char *path;
} key_types[] = {
#ifdef WITH_OPENSSL
-#ifdef WITH_SSH1
- { "rsa1", "RSA1", _PATH_HOST_KEY_FILE },
-#endif /* WITH_SSH1 */
{ "rsa", "RSA" ,_PATH_HOST_RSA_KEY_FILE },
{ "dsa", "DSA", _PATH_HOST_DSA_KEY_FILE },
#ifdef OPENSSL_HAS_ECC
@@ -994,20 +997,38 @@ do_gen_all_hostkeys(struct passwd *pw)
int first = 0;
struct stat st;
struct sshkey *private, *public;
- char comment[1024];
+ char comment[1024], *prv_tmp, *pub_tmp, *prv_file, *pub_file;
int i, type, fd, r;
FILE *f;
for (i = 0; key_types[i].key_type; i++) {
- if (stat(key_types[i].path, &st) == 0)
- continue;
- if (errno != ENOENT) {
+ public = private = NULL;
+ prv_tmp = pub_tmp = prv_file = pub_file = NULL;
+
+ 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,
strerror(errno));
- first = 0;
- continue;
+ goto failnext;
}
+ /*
+ * 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) {
first = 1;
printf("%s: generating new host keys: ", __progname);
@@ -1015,56 +1036,76 @@ do_gen_all_hostkeys(struct passwd *pw)
printf("%s ", key_types[i].key_type_display);
fflush(stdout);
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;
type_bits_valid(type, NULL, &bits);
if ((r = sshkey_generate(type, bits, &private)) != 0) {
- error("key_generate failed: %s", ssh_err(r));
- first = 0;
- continue;
+ error("sshkey_generate failed: %s", ssh_err(r));
+ goto failnext;
}
if ((r = sshkey_from_private(private, &public)) != 0)
fatal("sshkey_from_private failed: %s", ssh_err(r));
snprintf(comment, sizeof comment, "%s@%s", pw->pw_name,
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) {
error("Saving key \"%s\" failed: %s",
- identity_file, ssh_err(r));
- sshkey_free(private);
- sshkey_free(public);
- first = 0;
- continue;
+ prv_tmp, ssh_err(r));
+ goto failnext;
}
- sshkey_free(private);
- strlcat(identity_file, ".pub", sizeof(identity_file));
- fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
- if (fd == -1) {
- error("Could not save your public key in %s",
- identity_file);
- sshkey_free(public);
- first = 0;
- continue;
+ if ((fd = mkstemp(pub_tmp)) == -1) {
+ error("Could not save your public key in %s: %s",
+ pub_tmp, strerror(errno));
+ goto failnext;
}
+ (void)fchmod(fd, 0644);
f = fdopen(fd, "w");
if (f == NULL) {
- error("fdopen %s failed", identity_file);
+ error("fdopen %s failed: %s", pub_tmp, strerror(errno));
close(fd);
- sshkey_free(public);
- first = 0;
- continue;
+ goto failnext;
}
if ((r = sshkey_write(public, f)) != 0) {
error("write key failed: %s", ssh_err(r));
fclose(f);
- sshkey_free(public);
- first = 0;
- continue;
+ goto failnext;
}
fprintf(f, " %s\n", comment);
- fclose(f);
- sshkey_free(public);
+ if (ferror(f) != 0) {
+ 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)
printf("\n");
@@ -1436,9 +1477,8 @@ do_change_comment(struct passwd *pw)
}
}
- if (private->type != KEY_RSA1 && private->type != KEY_ED25519 &&
- !use_new_format) {
- error("Comments are only supported for RSA1 or keys stored in "
+ if (private->type != KEY_ED25519 && !use_new_format) {
+ error("Comments are only supported for keys stored in "
"the new format (-o).");
explicit_bzero(passphrase, strlen(passphrase));
sshkey_free(private);
@@ -1476,7 +1516,7 @@ do_change_comment(struct passwd *pw)
explicit_bzero(passphrase, strlen(passphrase));
free(passphrase);
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);
strlcat(identity_file, ".pub", sizeof(identity_file));
@@ -1531,6 +1571,8 @@ add_string_option(struct sshbuf *c, const char *name, const char *value)
static void
prepare_options_buf(struct sshbuf *c, int which)
{
+ size_t i;
+
sshbuf_reset(c);
if ((which & OPTIONS_CRITICAL) != 0 &&
certflags_command != NULL)
@@ -1553,6 +1595,17 @@ prepare_options_buf(struct sshbuf *c, int which)
if ((which & OPTIONS_CRITICAL) != 0 &&
certflags_src_addr != NULL)
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 *
@@ -1585,24 +1638,66 @@ load_pkcs11_key(char *path)
#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
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;
struct sshkey *ca, *public;
char valid[64], *otmp, *tmp, *cp, *out, *comment, **plist = NULL;
FILE *f;
+ struct ssh_identitylist *agent_ids;
+ size_t j;
#ifdef ENABLE_PKCS11
pkcs11_init(1);
#endif
tmp = tilde_expand_filename(ca_key_path, pw->pw_uid);
if (pkcs11provider != NULL) {
+ /* If a PKCS#11 token was specified then try to use it */
if ((ca = load_pkcs11_key(tmp)) == NULL)
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);
+ }
free(tmp);
if (key_type_name != NULL &&
@@ -1650,10 +1745,18 @@ do_ca_sign(struct passwd *pw, int argc, char **argv)
OPTIONS_EXTENSIONS);
if ((r = sshkey_from_private(ca,
&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)
- fatal("Couldn't certify key %s: %s", tmp, ssh_err(r));
+ if (agent_fd != -1 && (ca->flags & SSHKEY_FLAG_EXT) != 0) {
+ 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)
*cp = '\0';
@@ -1789,7 +1892,8 @@ parse_cert_times(char *timespec)
static void
add_cert_option(char *opt)
{
- char *val;
+ char *val, *cp;
+ int iscrit = 0;
if (strcasecmp(opt, "clear") == 0)
certflags_flags = 0;
@@ -1829,6 +1933,18 @@ add_cert_option(char *opt)
if (addr_match_cidr_list(NULL, val) != 0)
fatal("Invalid source-address list");
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
fatal("Unsupported certificate option \"%s\"", opt);
}
@@ -1955,7 +2071,7 @@ do_show_cert(struct passwd *pw)
if (*cp == '#' || *cp == '\0')
continue;
if ((key = sshkey_new(KEY_UNSPEC)) == NULL)
- fatal("key_new");
+ fatal("sshkey_new");
if ((r = sshkey_read(key, &cp)) != 0) {
error("%s:%lu: invalid key: %s", path,
lnum, ssh_err(r));
@@ -2101,7 +2217,7 @@ update_krl_from_file(struct passwd *pw, const char *file, int wild_ca,
*/
}
if ((key = sshkey_new(KEY_UNSPEC)) == NULL)
- fatal("key_new");
+ fatal("sshkey_new");
if ((r = sshkey_read(key, &cp)) != 0)
fatal("%s:%lu: invalid key: %s",
path, lnum, ssh_err(r));
@@ -2209,17 +2325,11 @@ do_check_krl(struct passwd *pw, int argc, char **argv)
exit(ret);
}
-#ifdef WITH_SSH1
-# define RSA1_USAGE " | rsa1"
-#else
-# define RSA1_USAGE ""
-#endif
-
static void
usage(void)
{
fprintf(stderr,
- "usage: ssh-keygen [-q] [-b bits] [-t dsa | ecdsa | ed25519 | rsa%s]\n"
+ "usage: ssh-keygen [-q] [-b bits] [-t dsa | ecdsa | ed25519 | rsa]\n"
" [-N new_passphrase] [-C comment] [-f output_keyfile]\n"
" ssh-keygen -p [-P old_passphrase] [-N new_passphrase] [-f keyfile]\n"
" ssh-keygen -i [-m key_format] [-f input_keyfile]\n"
@@ -2227,7 +2337,7 @@ usage(void)
" ssh-keygen -y [-f input_keyfile]\n"
" ssh-keygen -c [-P passphrase] [-C comment] [-f keyfile]\n"
" ssh-keygen -l [-v] [-E fingerprint_hash] [-f input_keyfile]\n"
- " ssh-keygen -B [-f input_keyfile]\n", RSA1_USAGE);
+ " ssh-keygen -B [-f input_keyfile]\n");
#ifdef ENABLE_PKCS11
fprintf(stderr,
" ssh-keygen -D pkcs11\n");
@@ -2242,8 +2352,9 @@ usage(void)
" ssh-keygen -T output_file -f input_file [-v] [-a rounds] [-J num_lines]\n"
" [-j start_line] [-K checkpt] [-W generator]\n"
#endif
- " ssh-keygen -s ca_key -I certificate_identity [-h] [-n principals]\n"
- " [-O option] [-V validity_interval] [-z serial_number] file ...\n"
+ " ssh-keygen -s ca_key -I certificate_identity [-h] [-U]\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 -A\n"
" ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n"
@@ -2301,8 +2412,8 @@ main(int argc, char **argv)
if (gethostname(hostname, sizeof(hostname)) < 0)
fatal("gethostname: %s", strerror(errno));
- /* Remaining characters: UYdw */
- while ((opt = getopt(argc, argv, "ABHLQXceghiklopquvxy"
+ /* Remaining characters: Ydw */
+ while ((opt = getopt(argc, argv, "ABHLQUXceghiklopquvxy"
"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) {
switch (opt) {
@@ -2429,6 +2540,9 @@ main(int argc, char **argv)
case 'D':
pkcs11provider = optarg;
break;
+ case 'U':
+ prefer_agent = 1;
+ break;
case 'u':
update_krl = 1;
break;
@@ -2648,9 +2762,9 @@ main(int argc, char **argv)
printf("Generating public/private %s key pair.\n",
key_type_name);
if ((r = sshkey_generate(type, bits, &private)) != 0)
- fatal("key_generate failed");
+ fatal("sshkey_generate failed");
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)
ask_filename(pw, "Enter file in which to save the key");