diff options
Diffstat (limited to 'ssh-keygen.c')
-rw-r--r-- | ssh-keygen.c | 594 |
1 files changed, 488 insertions, 106 deletions
diff --git a/ssh-keygen.c b/ssh-keygen.c index 3898b281e..8c829cad6 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-keygen.c,v 1.329 2019/03/25 16:19:44 dtucker Exp $ */ | 1 | /* $OpenBSD: ssh-keygen.c,v 1.355 2019/10/03 17:07:50 jmc Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -24,6 +24,9 @@ | |||
24 | #include "openbsd-compat/openssl-compat.h" | 24 | #include "openbsd-compat/openssl-compat.h" |
25 | #endif | 25 | #endif |
26 | 26 | ||
27 | #ifdef HAVE_STDINT_H | ||
28 | # include <stdint.h> | ||
29 | #endif | ||
27 | #include <errno.h> | 30 | #include <errno.h> |
28 | #include <fcntl.h> | 31 | #include <fcntl.h> |
29 | #include <netdb.h> | 32 | #include <netdb.h> |
@@ -43,7 +46,6 @@ | |||
43 | #include "xmalloc.h" | 46 | #include "xmalloc.h" |
44 | #include "sshkey.h" | 47 | #include "sshkey.h" |
45 | #include "authfile.h" | 48 | #include "authfile.h" |
46 | #include "uuencode.h" | ||
47 | #include "sshbuf.h" | 49 | #include "sshbuf.h" |
48 | #include "pathnames.h" | 50 | #include "pathnames.h" |
49 | #include "log.h" | 51 | #include "log.h" |
@@ -60,6 +62,7 @@ | |||
60 | #include "digest.h" | 62 | #include "digest.h" |
61 | #include "utf8.h" | 63 | #include "utf8.h" |
62 | #include "authfd.h" | 64 | #include "authfd.h" |
65 | #include "sshsig.h" | ||
63 | 66 | ||
64 | #ifdef WITH_OPENSSL | 67 | #ifdef WITH_OPENSSL |
65 | # define DEFAULT_KEY_TYPE_NAME "rsa" | 68 | # define DEFAULT_KEY_TYPE_NAME "rsa" |
@@ -92,7 +95,7 @@ static int print_bubblebabble = 0; | |||
92 | static int fingerprint_hash = SSH_FP_HASH_DEFAULT; | 95 | static int fingerprint_hash = SSH_FP_HASH_DEFAULT; |
93 | 96 | ||
94 | /* The identity file name, given on the command line or entered by the user. */ | 97 | /* The identity file name, given on the command line or entered by the user. */ |
95 | static char identity_file[1024]; | 98 | static char identity_file[PATH_MAX]; |
96 | static int have_identity = 0; | 99 | static int have_identity = 0; |
97 | 100 | ||
98 | /* This is set to the passphrase if given on the command line. */ | 101 | /* This is set to the passphrase if given on the command line. */ |
@@ -147,11 +150,11 @@ static char *key_type_name = NULL; | |||
147 | /* Load key from this PKCS#11 provider */ | 150 | /* Load key from this PKCS#11 provider */ |
148 | static char *pkcs11provider = NULL; | 151 | static char *pkcs11provider = NULL; |
149 | 152 | ||
150 | /* Use new OpenSSH private key format when writing SSH2 keys instead of PEM */ | 153 | /* Format for writing private keys */ |
151 | static int use_new_format = 1; | 154 | static int private_key_format = SSHKEY_PRIVATE_OPENSSH; |
152 | 155 | ||
153 | /* Cipher for new-format private keys */ | 156 | /* Cipher for new-format private keys */ |
154 | static char *new_format_cipher = NULL; | 157 | static char *openssh_format_cipher = NULL; |
155 | 158 | ||
156 | /* | 159 | /* |
157 | * Number of KDF rounds to derive new format keys / | 160 | * Number of KDF rounds to derive new format keys / |
@@ -174,31 +177,30 @@ int prime_test(FILE *, FILE *, u_int32_t, u_int32_t, char *, unsigned long, | |||
174 | static void | 177 | static void |
175 | type_bits_valid(int type, const char *name, u_int32_t *bitsp) | 178 | type_bits_valid(int type, const char *name, u_int32_t *bitsp) |
176 | { | 179 | { |
177 | #ifdef WITH_OPENSSL | ||
178 | u_int maxbits, nid; | ||
179 | #endif | ||
180 | |||
181 | if (type == KEY_UNSPEC) | 180 | if (type == KEY_UNSPEC) |
182 | fatal("unknown key type %s", key_type_name); | 181 | fatal("unknown key type %s", key_type_name); |
183 | if (*bitsp == 0) { | 182 | if (*bitsp == 0) { |
184 | #ifdef WITH_OPENSSL | 183 | #ifdef WITH_OPENSSL |
185 | if (type == KEY_DSA) | 184 | u_int nid; |
185 | |||
186 | switch(type) { | ||
187 | case KEY_DSA: | ||
186 | *bitsp = DEFAULT_BITS_DSA; | 188 | *bitsp = DEFAULT_BITS_DSA; |
187 | else if (type == KEY_ECDSA) { | 189 | break; |
190 | case KEY_ECDSA: | ||
188 | if (name != NULL && | 191 | if (name != NULL && |
189 | (nid = sshkey_ecdsa_nid_from_name(name)) > 0) | 192 | (nid = sshkey_ecdsa_nid_from_name(name)) > 0) |
190 | *bitsp = sshkey_curve_nid_to_bits(nid); | 193 | *bitsp = sshkey_curve_nid_to_bits(nid); |
191 | if (*bitsp == 0) | 194 | if (*bitsp == 0) |
192 | *bitsp = DEFAULT_BITS_ECDSA; | 195 | *bitsp = DEFAULT_BITS_ECDSA; |
193 | } else | 196 | break; |
194 | #endif | 197 | case KEY_RSA: |
195 | *bitsp = DEFAULT_BITS; | 198 | *bitsp = DEFAULT_BITS; |
199 | break; | ||
200 | } | ||
201 | #endif | ||
196 | } | 202 | } |
197 | #ifdef WITH_OPENSSL | 203 | #ifdef WITH_OPENSSL |
198 | maxbits = (type == KEY_DSA) ? | ||
199 | OPENSSL_DSA_MAX_MODULUS_BITS : OPENSSL_RSA_MAX_MODULUS_BITS; | ||
200 | if (*bitsp > maxbits) | ||
201 | fatal("key bits exceeds maximum %d", maxbits); | ||
202 | switch (type) { | 204 | switch (type) { |
203 | case KEY_DSA: | 205 | case KEY_DSA: |
204 | if (*bitsp != 1024) | 206 | if (*bitsp != 1024) |
@@ -208,6 +210,9 @@ type_bits_valid(int type, const char *name, u_int32_t *bitsp) | |||
208 | if (*bitsp < SSH_RSA_MINIMUM_MODULUS_SIZE) | 210 | if (*bitsp < SSH_RSA_MINIMUM_MODULUS_SIZE) |
209 | fatal("Invalid RSA key length: minimum is %d bits", | 211 | fatal("Invalid RSA key length: minimum is %d bits", |
210 | SSH_RSA_MINIMUM_MODULUS_SIZE); | 212 | SSH_RSA_MINIMUM_MODULUS_SIZE); |
213 | else if (*bitsp > OPENSSL_RSA_MAX_MODULUS_BITS) | ||
214 | fatal("Invalid RSA key length: maximum is %d bits", | ||
215 | OPENSSL_RSA_MAX_MODULUS_BITS); | ||
211 | break; | 216 | break; |
212 | case KEY_ECDSA: | 217 | case KEY_ECDSA: |
213 | if (sshkey_ecdsa_bits_to_nid(*bitsp) == -1) | 218 | if (sshkey_ecdsa_bits_to_nid(*bitsp) == -1) |
@@ -221,6 +226,30 @@ type_bits_valid(int type, const char *name, u_int32_t *bitsp) | |||
221 | #endif | 226 | #endif |
222 | } | 227 | } |
223 | 228 | ||
229 | /* | ||
230 | * Checks whether a file exists and, if so, asks the user whether they wish | ||
231 | * to overwrite it. | ||
232 | * Returns nonzero if the file does not already exist or if the user agrees to | ||
233 | * overwrite, or zero otherwise. | ||
234 | */ | ||
235 | static int | ||
236 | confirm_overwrite(const char *filename) | ||
237 | { | ||
238 | char yesno[3]; | ||
239 | struct stat st; | ||
240 | |||
241 | if (stat(filename, &st) != 0) | ||
242 | return 1; | ||
243 | printf("%s already exists.\n", filename); | ||
244 | printf("Overwrite (y/n)? "); | ||
245 | fflush(stdout); | ||
246 | if (fgets(yesno, sizeof(yesno), stdin) == NULL) | ||
247 | return 0; | ||
248 | if (yesno[0] != 'y' && yesno[0] != 'Y') | ||
249 | return 0; | ||
250 | return 1; | ||
251 | } | ||
252 | |||
224 | static void | 253 | static void |
225 | ask_filename(struct passwd *pw, const char *prompt) | 254 | ask_filename(struct passwd *pw, const char *prompt) |
226 | { | 255 | { |
@@ -270,13 +299,15 @@ ask_filename(struct passwd *pw, const char *prompt) | |||
270 | } | 299 | } |
271 | 300 | ||
272 | static struct sshkey * | 301 | static struct sshkey * |
273 | load_identity(char *filename) | 302 | load_identity(const char *filename, char **commentp) |
274 | { | 303 | { |
275 | char *pass; | 304 | char *pass; |
276 | struct sshkey *prv; | 305 | struct sshkey *prv; |
277 | int r; | 306 | int r; |
278 | 307 | ||
279 | if ((r = sshkey_load_private(filename, "", &prv, NULL)) == 0) | 308 | if (commentp != NULL) |
309 | *commentp = NULL; | ||
310 | if ((r = sshkey_load_private(filename, "", &prv, commentp)) == 0) | ||
280 | return prv; | 311 | return prv; |
281 | if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) | 312 | if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) |
282 | fatal("Load key \"%s\": %s", filename, ssh_err(r)); | 313 | fatal("Load key \"%s\": %s", filename, ssh_err(r)); |
@@ -284,7 +315,7 @@ load_identity(char *filename) | |||
284 | pass = xstrdup(identity_passphrase); | 315 | pass = xstrdup(identity_passphrase); |
285 | else | 316 | else |
286 | pass = read_passphrase("Enter passphrase: ", RP_ALLOW_STDIN); | 317 | pass = read_passphrase("Enter passphrase: ", RP_ALLOW_STDIN); |
287 | r = sshkey_load_private(filename, pass, &prv, NULL); | 318 | r = sshkey_load_private(filename, pass, &prv, commentp); |
288 | explicit_bzero(pass, strlen(pass)); | 319 | explicit_bzero(pass, strlen(pass)); |
289 | free(pass); | 320 | free(pass); |
290 | if (r != 0) | 321 | if (r != 0) |
@@ -301,25 +332,30 @@ load_identity(char *filename) | |||
301 | static void | 332 | static void |
302 | do_convert_to_ssh2(struct passwd *pw, struct sshkey *k) | 333 | do_convert_to_ssh2(struct passwd *pw, struct sshkey *k) |
303 | { | 334 | { |
304 | size_t len; | 335 | struct sshbuf *b; |
305 | u_char *blob; | 336 | char comment[61], *b64; |
306 | char comment[61]; | ||
307 | int r; | 337 | int r; |
308 | 338 | ||
309 | if ((r = sshkey_to_blob(k, &blob, &len)) != 0) | 339 | if ((b = sshbuf_new()) == NULL) |
340 | fatal("%s: sshbuf_new failed", __func__); | ||
341 | if ((r = sshkey_putb(k, b)) != 0) | ||
310 | fatal("key_to_blob failed: %s", ssh_err(r)); | 342 | fatal("key_to_blob failed: %s", ssh_err(r)); |
343 | if ((b64 = sshbuf_dtob64_string(b, 1)) == NULL) | ||
344 | fatal("%s: sshbuf_dtob64_string failed", __func__); | ||
345 | |||
311 | /* Comment + surrounds must fit into 72 chars (RFC 4716 sec 3.3) */ | 346 | /* Comment + surrounds must fit into 72 chars (RFC 4716 sec 3.3) */ |
312 | snprintf(comment, sizeof(comment), | 347 | snprintf(comment, sizeof(comment), |
313 | "%u-bit %s, converted by %s@%s from OpenSSH", | 348 | "%u-bit %s, converted by %s@%s from OpenSSH", |
314 | sshkey_size(k), sshkey_type(k), | 349 | sshkey_size(k), sshkey_type(k), |
315 | pw->pw_name, hostname); | 350 | pw->pw_name, hostname); |
316 | 351 | ||
352 | sshkey_free(k); | ||
353 | sshbuf_free(b); | ||
354 | |||
317 | fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN); | 355 | fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN); |
318 | fprintf(stdout, "Comment: \"%s\"\n", comment); | 356 | fprintf(stdout, "Comment: \"%s\"\n%s", comment, b64); |
319 | dump_base64(stdout, blob, len); | ||
320 | fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END); | 357 | fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END); |
321 | sshkey_free(k); | 358 | free(b64); |
322 | free(blob); | ||
323 | exit(0); | 359 | exit(0); |
324 | } | 360 | } |
325 | 361 | ||
@@ -370,10 +406,10 @@ do_convert_to(struct passwd *pw) | |||
370 | 406 | ||
371 | if (!have_identity) | 407 | if (!have_identity) |
372 | ask_filename(pw, "Enter file in which the key is"); | 408 | ask_filename(pw, "Enter file in which the key is"); |
373 | if (stat(identity_file, &st) < 0) | 409 | if (stat(identity_file, &st) == -1) |
374 | fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); | 410 | fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); |
375 | if ((r = sshkey_load_public(identity_file, &k, NULL)) != 0) | 411 | if ((r = sshkey_load_public(identity_file, &k, NULL)) != 0) |
376 | k = load_identity(identity_file); | 412 | k = load_identity(identity_file, NULL); |
377 | switch (convert_format) { | 413 | switch (convert_format) { |
378 | case FMT_RFC4716: | 414 | case FMT_RFC4716: |
379 | do_convert_to_ssh2(pw, k); | 415 | do_convert_to_ssh2(pw, k); |
@@ -413,9 +449,8 @@ buffer_get_bignum_bits(struct sshbuf *b, BIGNUM *value) | |||
413 | } | 449 | } |
414 | 450 | ||
415 | static struct sshkey * | 451 | static struct sshkey * |
416 | do_convert_private_ssh2_from_blob(u_char *blob, u_int blen) | 452 | do_convert_private_ssh2(struct sshbuf *b) |
417 | { | 453 | { |
418 | struct sshbuf *b; | ||
419 | struct sshkey *key = NULL; | 454 | struct sshkey *key = NULL; |
420 | char *type, *cipher; | 455 | char *type, *cipher; |
421 | u_char e1, e2, e3, *sig = NULL, data[] = "abcde12345"; | 456 | u_char e1, e2, e3, *sig = NULL, data[] = "abcde12345"; |
@@ -427,15 +462,13 @@ do_convert_private_ssh2_from_blob(u_char *blob, u_int blen) | |||
427 | BIGNUM *dsa_pub_key = NULL, *dsa_priv_key = NULL; | 462 | BIGNUM *dsa_pub_key = NULL, *dsa_priv_key = NULL; |
428 | BIGNUM *rsa_n = NULL, *rsa_e = NULL, *rsa_d = NULL; | 463 | BIGNUM *rsa_n = NULL, *rsa_e = NULL, *rsa_d = NULL; |
429 | BIGNUM *rsa_p = NULL, *rsa_q = NULL, *rsa_iqmp = NULL; | 464 | BIGNUM *rsa_p = NULL, *rsa_q = NULL, *rsa_iqmp = NULL; |
430 | if ((b = sshbuf_from(blob, blen)) == NULL) | 465 | |
431 | fatal("%s: sshbuf_from failed", __func__); | ||
432 | if ((r = sshbuf_get_u32(b, &magic)) != 0) | 466 | if ((r = sshbuf_get_u32(b, &magic)) != 0) |
433 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | 467 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); |
434 | 468 | ||
435 | if (magic != SSH_COM_PRIVATE_KEY_MAGIC) { | 469 | if (magic != SSH_COM_PRIVATE_KEY_MAGIC) { |
436 | error("bad magic 0x%x != 0x%x", magic, | 470 | error("bad magic 0x%x != 0x%x", magic, |
437 | SSH_COM_PRIVATE_KEY_MAGIC); | 471 | SSH_COM_PRIVATE_KEY_MAGIC); |
438 | sshbuf_free(b); | ||
439 | return NULL; | 472 | return NULL; |
440 | } | 473 | } |
441 | if ((r = sshbuf_get_u32(b, &i1)) != 0 || | 474 | if ((r = sshbuf_get_u32(b, &i1)) != 0 || |
@@ -449,7 +482,6 @@ do_convert_private_ssh2_from_blob(u_char *blob, u_int blen) | |||
449 | if (strcmp(cipher, "none") != 0) { | 482 | if (strcmp(cipher, "none") != 0) { |
450 | error("unsupported cipher %s", cipher); | 483 | error("unsupported cipher %s", cipher); |
451 | free(cipher); | 484 | free(cipher); |
452 | sshbuf_free(b); | ||
453 | free(type); | 485 | free(type); |
454 | return NULL; | 486 | return NULL; |
455 | } | 487 | } |
@@ -460,7 +492,6 @@ do_convert_private_ssh2_from_blob(u_char *blob, u_int blen) | |||
460 | } else if (strstr(type, "rsa")) { | 492 | } else if (strstr(type, "rsa")) { |
461 | ktype = KEY_RSA; | 493 | ktype = KEY_RSA; |
462 | } else { | 494 | } else { |
463 | sshbuf_free(b); | ||
464 | free(type); | 495 | free(type); |
465 | return NULL; | 496 | return NULL; |
466 | } | 497 | } |
@@ -507,7 +538,6 @@ do_convert_private_ssh2_from_blob(u_char *blob, u_int blen) | |||
507 | fatal("%s: BN_new", __func__); | 538 | fatal("%s: BN_new", __func__); |
508 | if (!BN_set_word(rsa_e, e)) { | 539 | if (!BN_set_word(rsa_e, e)) { |
509 | BN_clear_free(rsa_e); | 540 | BN_clear_free(rsa_e); |
510 | sshbuf_free(b); | ||
511 | sshkey_free(key); | 541 | sshkey_free(key); |
512 | return NULL; | 542 | return NULL; |
513 | } | 543 | } |
@@ -535,9 +565,7 @@ do_convert_private_ssh2_from_blob(u_char *blob, u_int blen) | |||
535 | } | 565 | } |
536 | rlen = sshbuf_len(b); | 566 | rlen = sshbuf_len(b); |
537 | if (rlen != 0) | 567 | if (rlen != 0) |
538 | error("do_convert_private_ssh2_from_blob: " | 568 | error("%s: remaining bytes in key blob %d", __func__, rlen); |
539 | "remaining bytes in key blob %d", rlen); | ||
540 | sshbuf_free(b); | ||
541 | 569 | ||
542 | /* try the key */ | 570 | /* try the key */ |
543 | if (sshkey_sign(key, &sig, &slen, data, sizeof(data), NULL, 0) != 0 || | 571 | if (sshkey_sign(key, &sig, &slen, data, sizeof(data), NULL, 0) != 0 || |
@@ -582,10 +610,12 @@ do_convert_from_ssh2(struct passwd *pw, struct sshkey **k, int *private) | |||
582 | int r, blen, escaped = 0; | 610 | int r, blen, escaped = 0; |
583 | u_int len; | 611 | u_int len; |
584 | char line[1024]; | 612 | char line[1024]; |
585 | u_char blob[8096]; | 613 | struct sshbuf *buf; |
586 | char encoded[8096]; | 614 | char encoded[8096]; |
587 | FILE *fp; | 615 | FILE *fp; |
588 | 616 | ||
617 | if ((buf = sshbuf_new()) == NULL) | ||
618 | fatal("sshbuf_new failed"); | ||
589 | if ((fp = fopen(identity_file, "r")) == NULL) | 619 | if ((fp = fopen(identity_file, "r")) == NULL) |
590 | fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); | 620 | fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); |
591 | encoded[0] = '\0'; | 621 | encoded[0] = '\0'; |
@@ -615,12 +645,11 @@ do_convert_from_ssh2(struct passwd *pw, struct sshkey **k, int *private) | |||
615 | (encoded[len-2] == '=') && | 645 | (encoded[len-2] == '=') && |
616 | (encoded[len-3] == '=')) | 646 | (encoded[len-3] == '=')) |
617 | encoded[len-3] = '\0'; | 647 | encoded[len-3] = '\0'; |
618 | blen = uudecode(encoded, blob, sizeof(blob)); | 648 | if ((r = sshbuf_b64tod(buf, encoded)) != 0) |
619 | if (blen < 0) | 649 | fatal("%s: base64 decoding failed: %s", __func__, ssh_err(r)); |
620 | fatal("uudecode failed."); | ||
621 | if (*private) | 650 | if (*private) |
622 | *k = do_convert_private_ssh2_from_blob(blob, blen); | 651 | *k = do_convert_private_ssh2(buf); |
623 | else if ((r = sshkey_from_blob(blob, blen, k)) != 0) | 652 | else if ((r = sshkey_fromb(buf, k)) != 0) |
624 | fatal("decode blob failed: %s", ssh_err(r)); | 653 | fatal("decode blob failed: %s", ssh_err(r)); |
625 | fclose(fp); | 654 | fclose(fp); |
626 | } | 655 | } |
@@ -696,7 +725,7 @@ do_convert_from(struct passwd *pw) | |||
696 | 725 | ||
697 | if (!have_identity) | 726 | if (!have_identity) |
698 | ask_filename(pw, "Enter file in which the key is"); | 727 | ask_filename(pw, "Enter file in which the key is"); |
699 | if (stat(identity_file, &st) < 0) | 728 | if (stat(identity_file, &st) == -1) |
700 | fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); | 729 | fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); |
701 | 730 | ||
702 | switch (convert_format) { | 731 | switch (convert_format) { |
@@ -753,16 +782,20 @@ do_print_public(struct passwd *pw) | |||
753 | struct sshkey *prv; | 782 | struct sshkey *prv; |
754 | struct stat st; | 783 | struct stat st; |
755 | int r; | 784 | int r; |
785 | char *comment = NULL; | ||
756 | 786 | ||
757 | if (!have_identity) | 787 | if (!have_identity) |
758 | ask_filename(pw, "Enter file in which the key is"); | 788 | ask_filename(pw, "Enter file in which the key is"); |
759 | if (stat(identity_file, &st) < 0) | 789 | if (stat(identity_file, &st) == -1) |
760 | fatal("%s: %s", identity_file, strerror(errno)); | 790 | fatal("%s: %s", identity_file, strerror(errno)); |
761 | prv = load_identity(identity_file); | 791 | prv = load_identity(identity_file, &comment); |
762 | if ((r = sshkey_write(prv, stdout)) != 0) | 792 | if ((r = sshkey_write(prv, stdout)) != 0) |
763 | error("sshkey_write failed: %s", ssh_err(r)); | 793 | error("sshkey_write failed: %s", ssh_err(r)); |
764 | sshkey_free(prv); | 794 | sshkey_free(prv); |
795 | if (comment != NULL && *comment != '\0') | ||
796 | fprintf(stdout, " %s", comment); | ||
765 | fprintf(stdout, "\n"); | 797 | fprintf(stdout, "\n"); |
798 | free(comment); | ||
766 | exit(0); | 799 | exit(0); |
767 | } | 800 | } |
768 | 801 | ||
@@ -854,7 +887,7 @@ fingerprint_private(const char *path) | |||
854 | struct sshkey *public = NULL; | 887 | struct sshkey *public = NULL; |
855 | int r; | 888 | int r; |
856 | 889 | ||
857 | if (stat(identity_file, &st) < 0) | 890 | if (stat(identity_file, &st) == -1) |
858 | fatal("%s: %s", path, strerror(errno)); | 891 | fatal("%s: %s", path, strerror(errno)); |
859 | if ((r = sshkey_load_public(path, &public, &comment)) != 0) { | 892 | if ((r = sshkey_load_public(path, &public, &comment)) != 0) { |
860 | debug("load public \"%s\": %s", path, ssh_err(r)); | 893 | debug("load public \"%s\": %s", path, ssh_err(r)); |
@@ -988,7 +1021,7 @@ do_gen_all_hostkeys(struct passwd *pw) | |||
988 | { NULL, NULL, NULL } | 1021 | { NULL, NULL, NULL } |
989 | }; | 1022 | }; |
990 | 1023 | ||
991 | u_int bits = 0; | 1024 | u_int32_t bits = 0; |
992 | int first = 0; | 1025 | int first = 0; |
993 | struct stat st; | 1026 | struct stat st; |
994 | struct sshkey *private, *public; | 1027 | struct sshkey *private, *public; |
@@ -1048,7 +1081,8 @@ do_gen_all_hostkeys(struct passwd *pw) | |||
1048 | snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, | 1081 | snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, |
1049 | hostname); | 1082 | hostname); |
1050 | if ((r = sshkey_save_private(private, prv_tmp, "", | 1083 | if ((r = sshkey_save_private(private, prv_tmp, "", |
1051 | comment, use_new_format, new_format_cipher, rounds)) != 0) { | 1084 | comment, private_key_format, openssh_format_cipher, |
1085 | rounds)) != 0) { | ||
1052 | error("Saving key \"%s\" failed: %s", | 1086 | error("Saving key \"%s\" failed: %s", |
1053 | prv_tmp, ssh_err(r)); | 1087 | prv_tmp, ssh_err(r)); |
1054 | goto failnext; | 1088 | goto failnext; |
@@ -1174,7 +1208,7 @@ known_hosts_find_delete(struct hostkey_foreach_line *l, void *_ctx) | |||
1174 | struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx; | 1208 | struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx; |
1175 | enum sshkey_fp_rep rep; | 1209 | enum sshkey_fp_rep rep; |
1176 | int fptype; | 1210 | int fptype; |
1177 | char *fp; | 1211 | char *fp = NULL, *ra = NULL; |
1178 | 1212 | ||
1179 | fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash; | 1213 | fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash; |
1180 | rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT; | 1214 | rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT; |
@@ -1208,8 +1242,16 @@ known_hosts_find_delete(struct hostkey_foreach_line *l, void *_ctx) | |||
1208 | known_hosts_hash(l, ctx); | 1242 | known_hosts_hash(l, ctx); |
1209 | else if (print_fingerprint) { | 1243 | else if (print_fingerprint) { |
1210 | fp = sshkey_fingerprint(l->key, fptype, rep); | 1244 | fp = sshkey_fingerprint(l->key, fptype, rep); |
1245 | ra = sshkey_fingerprint(l->key, | ||
1246 | fingerprint_hash, SSH_FP_RANDOMART); | ||
1247 | if (fp == NULL || ra == NULL) | ||
1248 | fatal("%s: sshkey_fingerprint failed", | ||
1249 | __func__); | ||
1211 | mprintf("%s %s %s %s\n", ctx->host, | 1250 | mprintf("%s %s %s %s\n", ctx->host, |
1212 | sshkey_type(l->key), fp, l->comment); | 1251 | sshkey_type(l->key), fp, l->comment); |
1252 | if (log_level_get() >= SYSLOG_LEVEL_VERBOSE) | ||
1253 | printf("%s\n", ra); | ||
1254 | free(ra); | ||
1213 | free(fp); | 1255 | free(fp); |
1214 | } else | 1256 | } else |
1215 | fprintf(ctx->out, "%s\n", l->line); | 1257 | fprintf(ctx->out, "%s\n", l->line); |
@@ -1340,7 +1382,7 @@ do_change_passphrase(struct passwd *pw) | |||
1340 | 1382 | ||
1341 | if (!have_identity) | 1383 | if (!have_identity) |
1342 | ask_filename(pw, "Enter file in which the key is"); | 1384 | ask_filename(pw, "Enter file in which the key is"); |
1343 | if (stat(identity_file, &st) < 0) | 1385 | if (stat(identity_file, &st) == -1) |
1344 | fatal("%s: %s", identity_file, strerror(errno)); | 1386 | fatal("%s: %s", identity_file, strerror(errno)); |
1345 | /* Try to load the file with empty passphrase. */ | 1387 | /* Try to load the file with empty passphrase. */ |
1346 | r = sshkey_load_private(identity_file, "", &private, &comment); | 1388 | r = sshkey_load_private(identity_file, "", &private, &comment); |
@@ -1391,7 +1433,7 @@ do_change_passphrase(struct passwd *pw) | |||
1391 | 1433 | ||
1392 | /* Save the file using the new passphrase. */ | 1434 | /* Save the file using the new passphrase. */ |
1393 | if ((r = sshkey_save_private(private, identity_file, passphrase1, | 1435 | if ((r = sshkey_save_private(private, identity_file, passphrase1, |
1394 | comment, use_new_format, new_format_cipher, rounds)) != 0) { | 1436 | comment, private_key_format, openssh_format_cipher, rounds)) != 0) { |
1395 | error("Saving key \"%s\" failed: %s.", | 1437 | error("Saving key \"%s\" failed: %s.", |
1396 | identity_file, ssh_err(r)); | 1438 | identity_file, ssh_err(r)); |
1397 | explicit_bzero(passphrase1, strlen(passphrase1)); | 1439 | explicit_bzero(passphrase1, strlen(passphrase1)); |
@@ -1424,7 +1466,7 @@ do_print_resource_record(struct passwd *pw, char *fname, char *hname, | |||
1424 | 1466 | ||
1425 | if (fname == NULL) | 1467 | if (fname == NULL) |
1426 | fatal("%s: no filename", __func__); | 1468 | fatal("%s: no filename", __func__); |
1427 | if (stat(fname, &st) < 0) { | 1469 | if (stat(fname, &st) == -1) { |
1428 | if (errno == ENOENT) | 1470 | if (errno == ENOENT) |
1429 | return 0; | 1471 | return 0; |
1430 | fatal("%s: %s", fname, strerror(errno)); | 1472 | fatal("%s: %s", fname, strerror(errno)); |
@@ -1453,7 +1495,7 @@ do_change_comment(struct passwd *pw, const char *identity_comment) | |||
1453 | 1495 | ||
1454 | if (!have_identity) | 1496 | if (!have_identity) |
1455 | ask_filename(pw, "Enter file in which the key is"); | 1497 | ask_filename(pw, "Enter file in which the key is"); |
1456 | if (stat(identity_file, &st) < 0) | 1498 | if (stat(identity_file, &st) == -1) |
1457 | fatal("%s: %s", identity_file, strerror(errno)); | 1499 | fatal("%s: %s", identity_file, strerror(errno)); |
1458 | if ((r = sshkey_load_private(identity_file, "", | 1500 | if ((r = sshkey_load_private(identity_file, "", |
1459 | &private, &comment)) == 0) | 1501 | &private, &comment)) == 0) |
@@ -1480,7 +1522,7 @@ do_change_comment(struct passwd *pw, const char *identity_comment) | |||
1480 | } | 1522 | } |
1481 | 1523 | ||
1482 | if (private->type != KEY_ED25519 && private->type != KEY_XMSS && | 1524 | if (private->type != KEY_ED25519 && private->type != KEY_XMSS && |
1483 | !use_new_format) { | 1525 | private_key_format != SSHKEY_PRIVATE_OPENSSH) { |
1484 | error("Comments are only supported for keys stored in " | 1526 | error("Comments are only supported for keys stored in " |
1485 | "the new format (-o)."); | 1527 | "the new format (-o)."); |
1486 | explicit_bzero(passphrase, strlen(passphrase)); | 1528 | explicit_bzero(passphrase, strlen(passphrase)); |
@@ -1488,14 +1530,14 @@ do_change_comment(struct passwd *pw, const char *identity_comment) | |||
1488 | exit(1); | 1530 | exit(1); |
1489 | } | 1531 | } |
1490 | if (comment) | 1532 | if (comment) |
1491 | printf("Key now has comment '%s'\n", comment); | 1533 | printf("Old comment: %s\n", comment); |
1492 | else | 1534 | else |
1493 | printf("Key now has no comment\n"); | 1535 | printf("No existing comment\n"); |
1494 | 1536 | ||
1495 | if (identity_comment) { | 1537 | if (identity_comment) { |
1496 | strlcpy(new_comment, identity_comment, sizeof(new_comment)); | 1538 | strlcpy(new_comment, identity_comment, sizeof(new_comment)); |
1497 | } else { | 1539 | } else { |
1498 | printf("Enter new comment: "); | 1540 | printf("New comment: "); |
1499 | fflush(stdout); | 1541 | fflush(stdout); |
1500 | if (!fgets(new_comment, sizeof(new_comment), stdin)) { | 1542 | if (!fgets(new_comment, sizeof(new_comment), stdin)) { |
1501 | explicit_bzero(passphrase, strlen(passphrase)); | 1543 | explicit_bzero(passphrase, strlen(passphrase)); |
@@ -1504,10 +1546,18 @@ do_change_comment(struct passwd *pw, const char *identity_comment) | |||
1504 | } | 1546 | } |
1505 | new_comment[strcspn(new_comment, "\n")] = '\0'; | 1547 | new_comment[strcspn(new_comment, "\n")] = '\0'; |
1506 | } | 1548 | } |
1549 | if (comment != NULL && strcmp(comment, new_comment) == 0) { | ||
1550 | printf("No change to comment\n"); | ||
1551 | free(passphrase); | ||
1552 | sshkey_free(private); | ||
1553 | free(comment); | ||
1554 | exit(0); | ||
1555 | } | ||
1507 | 1556 | ||
1508 | /* Save the file using the new passphrase. */ | 1557 | /* Save the file using the new passphrase. */ |
1509 | if ((r = sshkey_save_private(private, identity_file, passphrase, | 1558 | if ((r = sshkey_save_private(private, identity_file, passphrase, |
1510 | new_comment, use_new_format, new_format_cipher, rounds)) != 0) { | 1559 | new_comment, private_key_format, openssh_format_cipher, |
1560 | rounds)) != 0) { | ||
1511 | error("Saving key \"%s\" failed: %s", | 1561 | error("Saving key \"%s\" failed: %s", |
1512 | identity_file, ssh_err(r)); | 1562 | identity_file, ssh_err(r)); |
1513 | explicit_bzero(passphrase, strlen(passphrase)); | 1563 | explicit_bzero(passphrase, strlen(passphrase)); |
@@ -1537,7 +1587,11 @@ do_change_comment(struct passwd *pw, const char *identity_comment) | |||
1537 | 1587 | ||
1538 | free(comment); | 1588 | free(comment); |
1539 | 1589 | ||
1540 | printf("The comment in your key file has been changed.\n"); | 1590 | if (strlen(new_comment) > 0) |
1591 | printf("Comment '%s' applied\n", new_comment); | ||
1592 | else | ||
1593 | printf("Comment removed\n"); | ||
1594 | |||
1541 | exit(0); | 1595 | exit(0); |
1542 | } | 1596 | } |
1543 | 1597 | ||
@@ -1643,7 +1697,7 @@ load_pkcs11_key(char *path) | |||
1643 | 1697 | ||
1644 | /* Signer for sshkey_certify_custom that uses the agent */ | 1698 | /* Signer for sshkey_certify_custom that uses the agent */ |
1645 | static int | 1699 | static int |
1646 | agent_signer(const struct sshkey *key, u_char **sigp, size_t *lenp, | 1700 | agent_signer(struct sshkey *key, u_char **sigp, size_t *lenp, |
1647 | const u_char *data, size_t datalen, | 1701 | const u_char *data, size_t datalen, |
1648 | const char *alg, u_int compat, void *ctx) | 1702 | const char *alg, u_int compat, void *ctx) |
1649 | { | 1703 | { |
@@ -1701,7 +1755,7 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent, | |||
1701 | ca->flags |= SSHKEY_FLAG_EXT; | 1755 | ca->flags |= SSHKEY_FLAG_EXT; |
1702 | } else { | 1756 | } else { |
1703 | /* CA key is assumed to be a private key on the filesystem */ | 1757 | /* CA key is assumed to be a private key on the filesystem */ |
1704 | ca = load_identity(tmp); | 1758 | ca = load_identity(tmp, NULL); |
1705 | } | 1759 | } |
1706 | free(tmp); | 1760 | free(tmp); |
1707 | 1761 | ||
@@ -1726,7 +1780,7 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent, | |||
1726 | } | 1780 | } |
1727 | if (n > SSHKEY_CERT_MAX_PRINCIPALS) | 1781 | if (n > SSHKEY_CERT_MAX_PRINCIPALS) |
1728 | fatal("Too many certificate principals specified"); | 1782 | fatal("Too many certificate principals specified"); |
1729 | 1783 | ||
1730 | tmp = tilde_expand_filename(argv[i], pw->pw_uid); | 1784 | tmp = tilde_expand_filename(argv[i], pw->pw_uid); |
1731 | if ((r = sshkey_load_public(tmp, &public, &comment)) != 0) | 1785 | if ((r = sshkey_load_public(tmp, &public, &comment)) != 0) |
1732 | fatal("%s: unable to open \"%s\": %s", | 1786 | fatal("%s: unable to open \"%s\": %s", |
@@ -2034,7 +2088,7 @@ do_show_cert(struct passwd *pw) | |||
2034 | 2088 | ||
2035 | if (!have_identity) | 2089 | if (!have_identity) |
2036 | ask_filename(pw, "Enter file in which the key is"); | 2090 | ask_filename(pw, "Enter file in which the key is"); |
2037 | if (strcmp(identity_file, "-") != 0 && stat(identity_file, &st) < 0) | 2091 | if (strcmp(identity_file, "-") != 0 && stat(identity_file, &st) == -1) |
2038 | fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); | 2092 | fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); |
2039 | 2093 | ||
2040 | path = identity_file; | 2094 | path = identity_file; |
@@ -2372,18 +2426,298 @@ do_check_krl(struct passwd *pw, int argc, char **argv) | |||
2372 | exit(ret); | 2426 | exit(ret); |
2373 | } | 2427 | } |
2374 | 2428 | ||
2429 | static struct sshkey * | ||
2430 | load_sign_key(const char *keypath, const struct sshkey *pubkey) | ||
2431 | { | ||
2432 | size_t i, slen, plen = strlen(keypath); | ||
2433 | char *privpath = xstrdup(keypath); | ||
2434 | const char *suffixes[] = { "-cert.pub", ".pub", NULL }; | ||
2435 | struct sshkey *ret = NULL, *privkey = NULL; | ||
2436 | int r; | ||
2437 | |||
2438 | /* | ||
2439 | * If passed a public key filename, then try to locate the correponding | ||
2440 | * private key. This lets us specify certificates on the command-line | ||
2441 | * and have ssh-keygen find the appropriate private key. | ||
2442 | */ | ||
2443 | for (i = 0; suffixes[i]; i++) { | ||
2444 | slen = strlen(suffixes[i]); | ||
2445 | if (plen <= slen || | ||
2446 | strcmp(privpath + plen - slen, suffixes[i]) != 0) | ||
2447 | continue; | ||
2448 | privpath[plen - slen] = '\0'; | ||
2449 | debug("%s: %s looks like a public key, using private key " | ||
2450 | "path %s instead", __func__, keypath, privpath); | ||
2451 | } | ||
2452 | if ((privkey = load_identity(privpath, NULL)) == NULL) { | ||
2453 | error("Couldn't load identity %s", keypath); | ||
2454 | goto done; | ||
2455 | } | ||
2456 | if (!sshkey_equal_public(pubkey, privkey)) { | ||
2457 | error("Public key %s doesn't match private %s", | ||
2458 | keypath, privpath); | ||
2459 | goto done; | ||
2460 | } | ||
2461 | if (sshkey_is_cert(pubkey) && !sshkey_is_cert(privkey)) { | ||
2462 | /* | ||
2463 | * Graft the certificate onto the private key to make | ||
2464 | * it capable of signing. | ||
2465 | */ | ||
2466 | if ((r = sshkey_to_certified(privkey)) != 0) { | ||
2467 | error("%s: sshkey_to_certified: %s", __func__, | ||
2468 | ssh_err(r)); | ||
2469 | goto done; | ||
2470 | } | ||
2471 | if ((r = sshkey_cert_copy(pubkey, privkey)) != 0) { | ||
2472 | error("%s: sshkey_cert_copy: %s", __func__, ssh_err(r)); | ||
2473 | goto done; | ||
2474 | } | ||
2475 | } | ||
2476 | /* success */ | ||
2477 | ret = privkey; | ||
2478 | privkey = NULL; | ||
2479 | done: | ||
2480 | sshkey_free(privkey); | ||
2481 | free(privpath); | ||
2482 | return ret; | ||
2483 | } | ||
2484 | |||
2485 | static int | ||
2486 | sign_one(struct sshkey *signkey, const char *filename, int fd, | ||
2487 | const char *sig_namespace, sshsig_signer *signer, void *signer_ctx) | ||
2488 | { | ||
2489 | struct sshbuf *sigbuf = NULL, *abuf = NULL; | ||
2490 | int r = SSH_ERR_INTERNAL_ERROR, wfd = -1, oerrno; | ||
2491 | char *wfile = NULL; | ||
2492 | char *asig = NULL; | ||
2493 | |||
2494 | if (!quiet) { | ||
2495 | if (fd == STDIN_FILENO) | ||
2496 | fprintf(stderr, "Signing data on standard input\n"); | ||
2497 | else | ||
2498 | fprintf(stderr, "Signing file %s\n", filename); | ||
2499 | } | ||
2500 | if ((r = sshsig_sign_fd(signkey, NULL, fd, sig_namespace, | ||
2501 | &sigbuf, signer, signer_ctx)) != 0) { | ||
2502 | error("Signing %s failed: %s", filename, ssh_err(r)); | ||
2503 | goto out; | ||
2504 | } | ||
2505 | if ((r = sshsig_armor(sigbuf, &abuf)) != 0) { | ||
2506 | error("%s: sshsig_armor: %s", __func__, ssh_err(r)); | ||
2507 | goto out; | ||
2508 | } | ||
2509 | if ((asig = sshbuf_dup_string(abuf)) == NULL) { | ||
2510 | error("%s: buffer error", __func__); | ||
2511 | r = SSH_ERR_ALLOC_FAIL; | ||
2512 | goto out; | ||
2513 | } | ||
2514 | |||
2515 | if (fd == STDIN_FILENO) { | ||
2516 | fputs(asig, stdout); | ||
2517 | fflush(stdout); | ||
2518 | } else { | ||
2519 | xasprintf(&wfile, "%s.sig", filename); | ||
2520 | if (confirm_overwrite(wfile)) { | ||
2521 | if ((wfd = open(wfile, O_WRONLY|O_CREAT|O_TRUNC, | ||
2522 | 0666)) == -1) { | ||
2523 | oerrno = errno; | ||
2524 | error("Cannot open %s: %s", | ||
2525 | wfile, strerror(errno)); | ||
2526 | errno = oerrno; | ||
2527 | r = SSH_ERR_SYSTEM_ERROR; | ||
2528 | goto out; | ||
2529 | } | ||
2530 | if (atomicio(vwrite, wfd, asig, | ||
2531 | strlen(asig)) != strlen(asig)) { | ||
2532 | oerrno = errno; | ||
2533 | error("Cannot write to %s: %s", | ||
2534 | wfile, strerror(errno)); | ||
2535 | errno = oerrno; | ||
2536 | r = SSH_ERR_SYSTEM_ERROR; | ||
2537 | goto out; | ||
2538 | } | ||
2539 | if (!quiet) { | ||
2540 | fprintf(stderr, "Write signature to %s\n", | ||
2541 | wfile); | ||
2542 | } | ||
2543 | } | ||
2544 | } | ||
2545 | /* success */ | ||
2546 | r = 0; | ||
2547 | out: | ||
2548 | free(wfile); | ||
2549 | free(asig); | ||
2550 | sshbuf_free(abuf); | ||
2551 | sshbuf_free(sigbuf); | ||
2552 | if (wfd != -1) | ||
2553 | close(wfd); | ||
2554 | return r; | ||
2555 | } | ||
2556 | |||
2557 | static int | ||
2558 | sign(const char *keypath, const char *sig_namespace, int argc, char **argv) | ||
2559 | { | ||
2560 | int i, fd = -1, r, ret = -1; | ||
2561 | int agent_fd = -1; | ||
2562 | struct sshkey *pubkey = NULL, *privkey = NULL, *signkey = NULL; | ||
2563 | sshsig_signer *signer = NULL; | ||
2564 | |||
2565 | /* Check file arguments. */ | ||
2566 | for (i = 0; i < argc; i++) { | ||
2567 | if (strcmp(argv[i], "-") != 0) | ||
2568 | continue; | ||
2569 | if (i > 0 || argc > 1) | ||
2570 | fatal("Cannot sign mix of paths and standard input"); | ||
2571 | } | ||
2572 | |||
2573 | if ((r = sshkey_load_public(keypath, &pubkey, NULL)) != 0) { | ||
2574 | error("Couldn't load public key %s: %s", keypath, ssh_err(r)); | ||
2575 | goto done; | ||
2576 | } | ||
2577 | |||
2578 | if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) | ||
2579 | debug("Couldn't get agent socket: %s", ssh_err(r)); | ||
2580 | else { | ||
2581 | if ((r = ssh_agent_has_key(agent_fd, pubkey)) == 0) | ||
2582 | signer = agent_signer; | ||
2583 | else | ||
2584 | debug("Couldn't find key in agent: %s", ssh_err(r)); | ||
2585 | } | ||
2586 | |||
2587 | if (signer == NULL) { | ||
2588 | /* Not using agent - try to load private key */ | ||
2589 | if ((privkey = load_sign_key(keypath, pubkey)) == NULL) | ||
2590 | goto done; | ||
2591 | signkey = privkey; | ||
2592 | } else { | ||
2593 | /* Will use key in agent */ | ||
2594 | signkey = pubkey; | ||
2595 | } | ||
2596 | |||
2597 | if (argc == 0) { | ||
2598 | if ((r = sign_one(signkey, "(stdin)", STDIN_FILENO, | ||
2599 | sig_namespace, signer, &agent_fd)) != 0) | ||
2600 | goto done; | ||
2601 | } else { | ||
2602 | for (i = 0; i < argc; i++) { | ||
2603 | if (strcmp(argv[i], "-") == 0) | ||
2604 | fd = STDIN_FILENO; | ||
2605 | else if ((fd = open(argv[i], O_RDONLY)) == -1) { | ||
2606 | error("Cannot open %s for signing: %s", | ||
2607 | argv[i], strerror(errno)); | ||
2608 | goto done; | ||
2609 | } | ||
2610 | if ((r = sign_one(signkey, argv[i], fd, sig_namespace, | ||
2611 | signer, &agent_fd)) != 0) | ||
2612 | goto done; | ||
2613 | if (fd != STDIN_FILENO) | ||
2614 | close(fd); | ||
2615 | fd = -1; | ||
2616 | } | ||
2617 | } | ||
2618 | |||
2619 | ret = 0; | ||
2620 | done: | ||
2621 | if (fd != -1 && fd != STDIN_FILENO) | ||
2622 | close(fd); | ||
2623 | sshkey_free(pubkey); | ||
2624 | sshkey_free(privkey); | ||
2625 | return ret; | ||
2626 | } | ||
2627 | |||
2628 | static int | ||
2629 | verify(const char *signature, const char *sig_namespace, const char *principal, | ||
2630 | const char *allowed_keys, const char *revoked_keys) | ||
2631 | { | ||
2632 | int r, ret = -1, sigfd = -1; | ||
2633 | struct sshbuf *sigbuf = NULL, *abuf = NULL; | ||
2634 | struct sshkey *sign_key = NULL; | ||
2635 | char *fp = NULL; | ||
2636 | |||
2637 | if ((abuf = sshbuf_new()) == NULL) | ||
2638 | fatal("%s: sshbuf_new() failed", __func__); | ||
2639 | |||
2640 | if ((sigfd = open(signature, O_RDONLY)) < 0) { | ||
2641 | error("Couldn't open signature file %s", signature); | ||
2642 | goto done; | ||
2643 | } | ||
2644 | |||
2645 | if ((r = sshkey_load_file(sigfd, abuf)) != 0) { | ||
2646 | error("Couldn't read signature file: %s", ssh_err(r)); | ||
2647 | goto done; | ||
2648 | } | ||
2649 | if ((r = sshsig_dearmor(abuf, &sigbuf)) != 0) { | ||
2650 | error("%s: sshsig_armor: %s", __func__, ssh_err(r)); | ||
2651 | return r; | ||
2652 | } | ||
2653 | if ((r = sshsig_verify_fd(sigbuf, STDIN_FILENO, sig_namespace, | ||
2654 | &sign_key)) != 0) | ||
2655 | goto done; /* sshsig_verify() prints error */ | ||
2656 | |||
2657 | if ((fp = sshkey_fingerprint(sign_key, fingerprint_hash, | ||
2658 | SSH_FP_DEFAULT)) == NULL) | ||
2659 | fatal("%s: sshkey_fingerprint failed", __func__); | ||
2660 | debug("Valid (unverified) signature from key %s", fp); | ||
2661 | free(fp); | ||
2662 | fp = NULL; | ||
2663 | |||
2664 | if (revoked_keys != NULL) { | ||
2665 | if ((r = sshkey_check_revoked(sign_key, revoked_keys)) != 0) { | ||
2666 | debug3("sshkey_check_revoked failed: %s", ssh_err(r)); | ||
2667 | goto done; | ||
2668 | } | ||
2669 | } | ||
2670 | |||
2671 | if (allowed_keys != NULL && | ||
2672 | (r = sshsig_check_allowed_keys(allowed_keys, sign_key, | ||
2673 | principal, sig_namespace)) != 0) { | ||
2674 | debug3("sshsig_check_allowed_keys failed: %s", ssh_err(r)); | ||
2675 | goto done; | ||
2676 | } | ||
2677 | /* success */ | ||
2678 | ret = 0; | ||
2679 | done: | ||
2680 | if (!quiet) { | ||
2681 | if (ret == 0) { | ||
2682 | if ((fp = sshkey_fingerprint(sign_key, fingerprint_hash, | ||
2683 | SSH_FP_DEFAULT)) == NULL) { | ||
2684 | fatal("%s: sshkey_fingerprint failed", | ||
2685 | __func__); | ||
2686 | } | ||
2687 | if (principal == NULL) { | ||
2688 | printf("Good \"%s\" signature with %s key %s\n", | ||
2689 | sig_namespace, sshkey_type(sign_key), fp); | ||
2690 | |||
2691 | } else { | ||
2692 | printf("Good \"%s\" signature for %s with %s key %s\n", | ||
2693 | sig_namespace, principal, | ||
2694 | sshkey_type(sign_key), fp); | ||
2695 | } | ||
2696 | } else { | ||
2697 | printf("Could not verify signature.\n"); | ||
2698 | } | ||
2699 | } | ||
2700 | if (sigfd != -1) | ||
2701 | close(sigfd); | ||
2702 | sshbuf_free(sigbuf); | ||
2703 | sshbuf_free(abuf); | ||
2704 | sshkey_free(sign_key); | ||
2705 | free(fp); | ||
2706 | return ret; | ||
2707 | } | ||
2708 | |||
2375 | static void | 2709 | static void |
2376 | usage(void) | 2710 | usage(void) |
2377 | { | 2711 | { |
2378 | fprintf(stderr, | 2712 | fprintf(stderr, |
2379 | "usage: ssh-keygen [-q] [-b bits] [-t dsa | ecdsa | ed25519 | rsa] [-m format]\n" | 2713 | "usage: ssh-keygen [-q] [-b bits] [-C comment] [-f output_keyfile] [-m format]\n" |
2380 | " [-N new_passphrase] [-C comment] [-f output_keyfile]\n" | 2714 | " [-N new_passphrase] [-t dsa | ecdsa | ed25519 | rsa]\n" |
2381 | " ssh-keygen -p [-P old_passphrase] [-N new_passphrase] [-m format]\n" | 2715 | " ssh-keygen -p [-f keyfile] [-m format] [-N new_passphrase]\n" |
2382 | " [-f keyfile]\n" | 2716 | " [-P old_passphrase]\n" |
2383 | " ssh-keygen -i [-m key_format] [-f input_keyfile]\n" | 2717 | " ssh-keygen -i [-f input_keyfile] [-m key_format]\n" |
2384 | " ssh-keygen -e [-m key_format] [-f input_keyfile]\n" | 2718 | " ssh-keygen -e [-f input_keyfile] [-m key_format]\n" |
2385 | " ssh-keygen -y [-f input_keyfile]\n" | 2719 | " ssh-keygen -y [-f input_keyfile]\n" |
2386 | " ssh-keygen -c [-P passphrase] [-C comment] [-f keyfile]\n" | 2720 | " ssh-keygen -c [-C comment] [-f keyfile] [-P passphrase]\n" |
2387 | " ssh-keygen -l [-v] [-E fingerprint_hash] [-f input_keyfile]\n" | 2721 | " ssh-keygen -l [-v] [-E fingerprint_hash] [-f input_keyfile]\n" |
2388 | " ssh-keygen -B [-f input_keyfile]\n"); | 2722 | " ssh-keygen -B [-f input_keyfile]\n"); |
2389 | #ifdef ENABLE_PKCS11 | 2723 | #ifdef ENABLE_PKCS11 |
@@ -2391,23 +2725,27 @@ usage(void) | |||
2391 | " ssh-keygen -D pkcs11\n"); | 2725 | " ssh-keygen -D pkcs11\n"); |
2392 | #endif | 2726 | #endif |
2393 | fprintf(stderr, | 2727 | fprintf(stderr, |
2394 | " ssh-keygen -F hostname [-f known_hosts_file] [-l]\n" | 2728 | " ssh-keygen -F hostname [-lv] [-f known_hosts_file]\n" |
2395 | " ssh-keygen -H [-f known_hosts_file]\n" | 2729 | " ssh-keygen -H [-f known_hosts_file]\n" |
2396 | " ssh-keygen -R hostname [-f known_hosts_file]\n" | 2730 | " ssh-keygen -R hostname [-f known_hosts_file]\n" |
2397 | " ssh-keygen -r hostname [-f input_keyfile] [-g]\n" | 2731 | " ssh-keygen -r hostname [-g] [-f input_keyfile]\n" |
2398 | #ifdef WITH_OPENSSL | 2732 | #ifdef WITH_OPENSSL |
2399 | " ssh-keygen -G output_file [-v] [-b bits] [-M memory] [-S start_point]\n" | 2733 | " ssh-keygen -G output_file [-v] [-b bits] [-M memory] [-S start_point]\n" |
2400 | " ssh-keygen -T output_file -f input_file [-v] [-a rounds] [-J num_lines]\n" | 2734 | " ssh-keygen -f input_file -T output_file [-v] [-a rounds] [-J num_lines]\n" |
2401 | " [-j start_line] [-K checkpt] [-W generator]\n" | 2735 | " [-j start_line] [-K checkpt] [-W generator]\n" |
2402 | #endif | 2736 | #endif |
2403 | " ssh-keygen -s ca_key -I certificate_identity [-h] [-U]\n" | 2737 | " ssh-keygen -I certificate_identity -s ca_key [-hU] [-D pkcs11_provider]\n" |
2404 | " [-D pkcs11_provider] [-n principals] [-O option]\n" | 2738 | " [-n principals] [-O option] [-V validity_interval]\n" |
2405 | " [-V validity_interval] [-z serial_number] file ...\n" | 2739 | " [-z serial_number] file ...\n" |
2406 | " ssh-keygen -L [-f input_keyfile]\n" | 2740 | " ssh-keygen -L [-f input_keyfile]\n" |
2407 | " ssh-keygen -A\n" | 2741 | " ssh-keygen -A [-f prefix_path]\n" |
2408 | " ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n" | 2742 | " ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n" |
2409 | " file ...\n" | 2743 | " file ...\n" |
2410 | " ssh-keygen -Q -f krl_file file ...\n"); | 2744 | " ssh-keygen -Q -f krl_file file ...\n" |
2745 | " ssh-keygen -Y check-novalidate -n namespace -s signature_file\n" | ||
2746 | " ssh-keygen -Y sign -f key_file -n namespace file ...\n" | ||
2747 | " ssh-keygen -Y verify -f allowed_signers_file -I signer_identity\n" | ||
2748 | " -n namespace -s signature_file [-r revocation_file]\n"); | ||
2411 | exit(1); | 2749 | exit(1); |
2412 | } | 2750 | } |
2413 | 2751 | ||
@@ -2430,10 +2768,11 @@ main(int argc, char **argv) | |||
2430 | int print_public = 0, print_generic = 0, cert_serial_autoinc = 0; | 2768 | int print_public = 0, print_generic = 0, cert_serial_autoinc = 0; |
2431 | unsigned long long cert_serial = 0; | 2769 | unsigned long long cert_serial = 0; |
2432 | char *identity_comment = NULL, *ca_key_path = NULL; | 2770 | char *identity_comment = NULL, *ca_key_path = NULL; |
2433 | u_int bits = 0; | 2771 | u_int32_t bits = 0; |
2434 | FILE *f; | 2772 | FILE *f; |
2435 | const char *errstr; | 2773 | const char *errstr; |
2436 | int log_level = SYSLOG_LEVEL_INFO; | 2774 | int log_level = SYSLOG_LEVEL_INFO; |
2775 | char *sign_op = NULL; | ||
2437 | #ifdef WITH_OPENSSL | 2776 | #ifdef WITH_OPENSSL |
2438 | /* Moduli generation/screening */ | 2777 | /* Moduli generation/screening */ |
2439 | char out_file[PATH_MAX], *checkpoint = NULL; | 2778 | char out_file[PATH_MAX], *checkpoint = NULL; |
@@ -2446,7 +2785,6 @@ main(int argc, char **argv) | |||
2446 | extern int optind; | 2785 | extern int optind; |
2447 | extern char *optarg; | 2786 | extern char *optarg; |
2448 | 2787 | ||
2449 | ssh_malloc_init(); /* must be called before any mallocs */ | ||
2450 | /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ | 2788 | /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ |
2451 | sanitise_stdfd(); | 2789 | sanitise_stdfd(); |
2452 | 2790 | ||
@@ -2462,19 +2800,20 @@ main(int argc, char **argv) | |||
2462 | pw = getpwuid(getuid()); | 2800 | pw = getpwuid(getuid()); |
2463 | if (!pw) | 2801 | if (!pw) |
2464 | fatal("No user exists for uid %lu", (u_long)getuid()); | 2802 | fatal("No user exists for uid %lu", (u_long)getuid()); |
2465 | if (gethostname(hostname, sizeof(hostname)) < 0) | 2803 | if (gethostname(hostname, sizeof(hostname)) == -1) |
2466 | fatal("gethostname: %s", strerror(errno)); | 2804 | fatal("gethostname: %s", strerror(errno)); |
2467 | 2805 | ||
2468 | /* Remaining characters: Ydw */ | 2806 | /* Remaining characters: dw */ |
2469 | while ((opt = getopt(argc, argv, "ABHLQUXceghiklopquvxy" | 2807 | while ((opt = getopt(argc, argv, "ABHLQUXceghiklopquvxy" |
2470 | "C:D:E:F:G:I:J:K:M:N:O:P:R:S:T:V:W:Z:" | 2808 | "C:D:E:F:G:I:J:K:M:N:O:P:R:S:T:V:W:Y:Z:" |
2471 | "a:b:f:g:j:m:n:r:s:t:z:")) != -1) { | 2809 | "a:b:f:g:j:m:n:r:s:t:z:")) != -1) { |
2472 | switch (opt) { | 2810 | switch (opt) { |
2473 | case 'A': | 2811 | case 'A': |
2474 | gen_all_hostkeys = 1; | 2812 | gen_all_hostkeys = 1; |
2475 | break; | 2813 | break; |
2476 | case 'b': | 2814 | case 'b': |
2477 | bits = (u_int32_t)strtonum(optarg, 10, 32768, &errstr); | 2815 | bits = (u_int32_t)strtonum(optarg, 1, UINT32_MAX, |
2816 | &errstr); | ||
2478 | if (errstr) | 2817 | if (errstr) |
2479 | fatal("Bits has bad value %s (%s)", | 2818 | fatal("Bits has bad value %s (%s)", |
2480 | optarg, errstr); | 2819 | optarg, errstr); |
@@ -2515,11 +2854,12 @@ main(int argc, char **argv) | |||
2515 | } | 2854 | } |
2516 | if (strcasecmp(optarg, "PKCS8") == 0) { | 2855 | if (strcasecmp(optarg, "PKCS8") == 0) { |
2517 | convert_format = FMT_PKCS8; | 2856 | convert_format = FMT_PKCS8; |
2857 | private_key_format = SSHKEY_PRIVATE_PKCS8; | ||
2518 | break; | 2858 | break; |
2519 | } | 2859 | } |
2520 | if (strcasecmp(optarg, "PEM") == 0) { | 2860 | if (strcasecmp(optarg, "PEM") == 0) { |
2521 | convert_format = FMT_PEM; | 2861 | convert_format = FMT_PEM; |
2522 | use_new_format = 0; | 2862 | private_key_format = SSHKEY_PRIVATE_PEM; |
2523 | break; | 2863 | break; |
2524 | } | 2864 | } |
2525 | fatal("Unsupported conversion format \"%s\"", optarg); | 2865 | fatal("Unsupported conversion format \"%s\"", optarg); |
@@ -2557,7 +2897,7 @@ main(int argc, char **argv) | |||
2557 | add_cert_option(optarg); | 2897 | add_cert_option(optarg); |
2558 | break; | 2898 | break; |
2559 | case 'Z': | 2899 | case 'Z': |
2560 | new_format_cipher = optarg; | 2900 | openssh_format_cipher = optarg; |
2561 | break; | 2901 | break; |
2562 | case 'C': | 2902 | case 'C': |
2563 | identity_comment = optarg; | 2903 | identity_comment = optarg; |
@@ -2621,6 +2961,9 @@ main(int argc, char **argv) | |||
2621 | case 'V': | 2961 | case 'V': |
2622 | parse_cert_times(optarg); | 2962 | parse_cert_times(optarg); |
2623 | break; | 2963 | break; |
2964 | case 'Y': | ||
2965 | sign_op = optarg; | ||
2966 | break; | ||
2624 | case 'z': | 2967 | case 'z': |
2625 | errno = 0; | 2968 | errno = 0; |
2626 | if (*optarg == '+') { | 2969 | if (*optarg == '+') { |
@@ -2688,6 +3031,50 @@ main(int argc, char **argv) | |||
2688 | argv += optind; | 3031 | argv += optind; |
2689 | argc -= optind; | 3032 | argc -= optind; |
2690 | 3033 | ||
3034 | if (sign_op != NULL) { | ||
3035 | if (cert_principals == NULL || *cert_principals == '\0') { | ||
3036 | error("Too few arguments for sign/verify: " | ||
3037 | "missing namespace"); | ||
3038 | exit(1); | ||
3039 | } | ||
3040 | if (strncmp(sign_op, "sign", 4) == 0) { | ||
3041 | if (!have_identity) { | ||
3042 | error("Too few arguments for sign: " | ||
3043 | "missing key"); | ||
3044 | exit(1); | ||
3045 | } | ||
3046 | return sign(identity_file, cert_principals, argc, argv); | ||
3047 | } else if (strncmp(sign_op, "check-novalidate", 16) == 0) { | ||
3048 | if (ca_key_path == NULL) { | ||
3049 | error("Too few arguments for check-novalidate: " | ||
3050 | "missing signature file"); | ||
3051 | exit(1); | ||
3052 | } | ||
3053 | return verify(ca_key_path, cert_principals, | ||
3054 | NULL, NULL, NULL); | ||
3055 | } else if (strncmp(sign_op, "verify", 6) == 0) { | ||
3056 | if (ca_key_path == NULL) { | ||
3057 | error("Too few arguments for verify: " | ||
3058 | "missing signature file"); | ||
3059 | exit(1); | ||
3060 | } | ||
3061 | if (!have_identity) { | ||
3062 | error("Too few arguments for sign: " | ||
3063 | "missing allowed keys file"); | ||
3064 | exit(1); | ||
3065 | } | ||
3066 | if (cert_key_id == NULL) { | ||
3067 | error("Too few arguments for verify: " | ||
3068 | "missing principal ID"); | ||
3069 | exit(1); | ||
3070 | } | ||
3071 | return verify(ca_key_path, cert_principals, | ||
3072 | cert_key_id, identity_file, rr_hostname); | ||
3073 | } | ||
3074 | usage(); | ||
3075 | /* NOTREACHED */ | ||
3076 | } | ||
3077 | |||
2691 | if (ca_key_path != NULL) { | 3078 | if (ca_key_path != NULL) { |
2692 | if (argc < 1 && !gen_krl) { | 3079 | if (argc < 1 && !gen_krl) { |
2693 | error("Too few arguments."); | 3080 | error("Too few arguments."); |
@@ -2739,7 +3126,10 @@ main(int argc, char **argv) | |||
2739 | do_convert_to(pw); | 3126 | do_convert_to(pw); |
2740 | if (convert_from) | 3127 | if (convert_from) |
2741 | do_convert_from(pw); | 3128 | do_convert_from(pw); |
2742 | #endif | 3129 | #else /* WITH_OPENSSL */ |
3130 | if (convert_to || convert_from) | ||
3131 | fatal("key conversion disabled at compile time"); | ||
3132 | #endif /* WITH_OPENSSL */ | ||
2743 | if (print_public) | 3133 | if (print_public) |
2744 | do_print_public(pw); | 3134 | do_print_public(pw); |
2745 | if (rr_hostname != NULL) { | 3135 | if (rr_hostname != NULL) { |
@@ -2842,11 +3232,11 @@ main(int argc, char **argv) | |||
2842 | snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", | 3232 | snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", |
2843 | pw->pw_dir, _PATH_SSH_USER_DIR); | 3233 | pw->pw_dir, _PATH_SSH_USER_DIR); |
2844 | if (strstr(identity_file, dotsshdir) != NULL) { | 3234 | if (strstr(identity_file, dotsshdir) != NULL) { |
2845 | if (stat(dotsshdir, &st) < 0) { | 3235 | if (stat(dotsshdir, &st) == -1) { |
2846 | if (errno != ENOENT) { | 3236 | if (errno != ENOENT) { |
2847 | error("Could not stat %s: %s", dotsshdir, | 3237 | error("Could not stat %s: %s", dotsshdir, |
2848 | strerror(errno)); | 3238 | strerror(errno)); |
2849 | } else if (mkdir(dotsshdir, 0700) < 0) { | 3239 | } else if (mkdir(dotsshdir, 0700) == -1) { |
2850 | error("Could not create directory '%s': %s", | 3240 | error("Could not create directory '%s': %s", |
2851 | dotsshdir, strerror(errno)); | 3241 | dotsshdir, strerror(errno)); |
2852 | } else if (!quiet) | 3242 | } else if (!quiet) |
@@ -2854,16 +3244,8 @@ main(int argc, char **argv) | |||
2854 | } | 3244 | } |
2855 | } | 3245 | } |
2856 | /* If the file already exists, ask the user to confirm. */ | 3246 | /* If the file already exists, ask the user to confirm. */ |
2857 | if (stat(identity_file, &st) >= 0) { | 3247 | if (!confirm_overwrite(identity_file)) |
2858 | char yesno[3]; | 3248 | exit(1); |
2859 | printf("%s already exists.\n", identity_file); | ||
2860 | printf("Overwrite (y/n)? "); | ||
2861 | fflush(stdout); | ||
2862 | if (fgets(yesno, sizeof(yesno), stdin) == NULL) | ||
2863 | exit(1); | ||
2864 | if (yesno[0] != 'y' && yesno[0] != 'Y') | ||
2865 | exit(1); | ||
2866 | } | ||
2867 | /* Ask for a passphrase (twice). */ | 3249 | /* Ask for a passphrase (twice). */ |
2868 | if (identity_passphrase) | 3250 | if (identity_passphrase) |
2869 | passphrase1 = xstrdup(identity_passphrase); | 3251 | passphrase1 = xstrdup(identity_passphrase); |
@@ -2902,7 +3284,7 @@ passphrase_again: | |||
2902 | 3284 | ||
2903 | /* Save the key with the given passphrase and comment. */ | 3285 | /* Save the key with the given passphrase and comment. */ |
2904 | if ((r = sshkey_save_private(private, identity_file, passphrase1, | 3286 | if ((r = sshkey_save_private(private, identity_file, passphrase1, |
2905 | comment, use_new_format, new_format_cipher, rounds)) != 0) { | 3287 | comment, private_key_format, openssh_format_cipher, rounds)) != 0) { |
2906 | error("Saving key \"%s\" failed: %s", | 3288 | error("Saving key \"%s\" failed: %s", |
2907 | identity_file, ssh_err(r)); | 3289 | identity_file, ssh_err(r)); |
2908 | explicit_bzero(passphrase1, strlen(passphrase1)); | 3290 | explicit_bzero(passphrase1, strlen(passphrase1)); |