1 /*
2 * Author: Tatu Ylonen <ylo@cs.hut.fi>
3 * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
4 * All rights reserved
5 * Identity and host key generation and maintenance.
6 *
7 * As far as I am concerned, the code I have written for this software
8 * can be used freely for any purpose. Any derived versions of this
9 * software must be clearly marked as such, and if the derived work is
10 * incompatible with the protocol description in the RFC file, it must be
11 * called by a name other than "ssh" or "Secure Shell".
12 */
13
14 /* $OpenBSD: ssh-keygen.c,v 1.160 2007/01/21 01:41:54 stevesk Exp $ */
15
16 #include "includes.h"
17 #include <openssl/evp.h>
18 #include <openssl/pem.h>
19
20 #include "xmalloc.h"
21 #include "key.h"
22 #include "rsa.h"
23 #include "authfile.h"
24 #include "uuencode.h"
25 #include "buffer.h"
26 #include "bufaux.h"
27 #include "pathnames.h"
28 #include "log.h"
29 #include "readpass.h"
30 #include "misc.h"
31 #include <langinfo.h>
32 #include "match.h"
33 #include "hostfile.h"
34 #include "tildexpand.h"
35
36 /* Number of bits in the RSA/DSA key. This value can be set on the command line. */
37 #define DEFAULT_BITS_RSA 2048
38 #define DEFAULT_BITS_DSA 1024
39 u_int32_t bits = 0;
40
41 /*
42 * Flag indicating that we just want to change the passphrase. This can be
43 * set on the command line.
44 */
45 int change_passphrase = 0;
46
47 /*
48 * Flag indicating that we just want to change the comment. This can be set
49 * on the command line.
50 */
51 int change_comment = 0;
52
53 int quiet = 0;
54
55 /* Flag indicating that we want to hash a known_hosts file */
56 int hash_hosts = 0;
57 /* Flag indicating that we want to lookup a host in known_hosts file */
58 int find_host = 0;
59 /* Flag indicating that we want to delete a host from a known_hosts file */
60 int delete_host = 0;
61
62 /* Flag indicating that we just want to see the key fingerprint */
63 int print_fingerprint = 0;
64 int print_bubblebabble = 0;
65
66 /* The identity file name, given on the command line or entered by the user. */
67 char identity_file[1024];
68 int have_identity = 0;
69
70 /* This is set to the passphrase if given on the command line. */
71 char *identity_passphrase = NULL;
72
73 /* This is set to the new passphrase if given on the command line. */
74 char *identity_new_passphrase = NULL;
75
76 /* This is set to the new comment if given on the command line. */
77 char *identity_comment = NULL;
78
79 /* Dump public key file in format used by real and the original SSH 2 */
80 int convert_to_ssh2 = 0;
81 int convert_from_ssh2 = 0;
82 int print_public = 0;
83
84 char *key_type_name = NULL;
85
86 /* argv0 */
87 #ifdef HAVE___PROGNAME
88 extern char *__progname;
89 #else
90 char *__progname;
91 #endif
92
93 char hostname[MAXHOSTNAMELEN];
94
95 static void
ask_filename(struct passwd * pw,const char * prompt)96 ask_filename(struct passwd *pw, const char *prompt)
97 {
98 char buf[1024];
99 char *name = NULL;
100
101 if (key_type_name == NULL)
102 name = _PATH_SSH_CLIENT_ID_RSA;
103 else {
104 switch (key_type_from_name(key_type_name)) {
105 case KEY_RSA1:
106 name = _PATH_SSH_CLIENT_IDENTITY;
107 break;
108 case KEY_DSA:
109 name = _PATH_SSH_CLIENT_ID_DSA;
110 break;
111 case KEY_RSA:
112 name = _PATH_SSH_CLIENT_ID_RSA;
113 break;
114 default:
115 fprintf(stderr, gettext("bad key type"));
116 exit(1);
117 break;
118 }
119 }
120 snprintf(identity_file, sizeof(identity_file), "%s/%s", pw->pw_dir, name);
121 fprintf(stderr, "%s (%s): ", gettext(prompt), identity_file);
122 if (fgets(buf, sizeof(buf), stdin) == NULL)
123 exit(1);
124 if (strchr(buf, '\n'))
125 *strchr(buf, '\n') = 0;
126 if (strcmp(buf, "") != 0)
127 strlcpy(identity_file, buf, sizeof(identity_file));
128 have_identity = 1;
129 }
130
131 static Key *
load_identity(char * filename)132 load_identity(char *filename)
133 {
134 char *pass;
135 Key *prv;
136
137 prv = key_load_private(filename, "", NULL);
138 if (prv == NULL) {
139 if (identity_passphrase)
140 pass = xstrdup(identity_passphrase);
141 else
142 pass = read_passphrase(gettext("Enter passphrase: "),
143 RP_ALLOW_STDIN);
144 prv = key_load_private(filename, pass, NULL);
145 memset(pass, 0, strlen(pass));
146 xfree(pass);
147 }
148 return prv;
149 }
150
151 #define SSH_COM_PUBLIC_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----"
152 #define SSH_COM_PUBLIC_END "---- END SSH2 PUBLIC KEY ----"
153 #define SSH_COM_PRIVATE_BEGIN "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----"
154 #define SSH_COM_PRIVATE_KEY_MAGIC 0x3f6ff9eb
155
156 static void
do_convert_to_ssh2(struct passwd * pw)157 do_convert_to_ssh2(struct passwd *pw)
158 {
159 Key *k;
160 u_int len;
161 u_char *blob;
162 struct stat st;
163
164 if (!have_identity)
165 ask_filename(pw, gettext("Enter file in which the key is"));
166 if (stat(identity_file, &st) < 0) {
167 perror(identity_file);
168 exit(1);
169 }
170 if ((k = key_load_public(identity_file, NULL)) == NULL) {
171 if ((k = load_identity(identity_file)) == NULL) {
172 fprintf(stderr, gettext("load failed\n"));
173 exit(1);
174 }
175 }
176 if (key_to_blob(k, &blob, &len) <= 0) {
177 fprintf(stderr, gettext("key_to_blob failed\n"));
178 exit(1);
179 }
180 fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN);
181 fprintf(stdout, gettext(
182 "Comment: \"%u-bit %s, converted from OpenSSH by %s@%s\"\n"),
183 key_size(k), key_type(k),
184 pw->pw_name, hostname);
185 dump_base64(stdout, blob, len);
186 fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END);
187 key_free(k);
188 xfree(blob);
189 exit(0);
190 }
191
192 static void
buffer_get_bignum_bits(Buffer * b,BIGNUM * value)193 buffer_get_bignum_bits(Buffer *b, BIGNUM *value)
194 {
195 u_int bignum_bits = buffer_get_int(b);
196 u_int bytes = (bignum_bits + 7) / 8;
197
198 if (buffer_len(b) < bytes)
199 fatal("buffer_get_bignum_bits: input buffer too small: "
200 "need %d have %d", bytes, buffer_len(b));
201 if (BN_bin2bn(buffer_ptr(b), bytes, value) == NULL)
202 fatal("buffer_get_bignum_bits: BN_bin2bn failed");
203 buffer_consume(b, bytes);
204 }
205
206 static Key *
do_convert_private_ssh2_from_blob(u_char * blob,u_int blen)207 do_convert_private_ssh2_from_blob(u_char *blob, u_int blen)
208 {
209 Buffer b;
210 Key *key = NULL;
211 char *type, *cipher;
212 u_char *sig, data[] = "abcde12345";
213 int magic, rlen, ktype, i1, i2, i3, i4;
214 u_int slen;
215 u_long e;
216
217 buffer_init(&b);
218 buffer_append(&b, blob, blen);
219
220 magic = buffer_get_int(&b);
221 if (magic != SSH_COM_PRIVATE_KEY_MAGIC) {
222 error("bad magic 0x%x != 0x%x", magic, SSH_COM_PRIVATE_KEY_MAGIC);
223 buffer_free(&b);
224 return NULL;
225 }
226 i1 = buffer_get_int(&b);
227 type = buffer_get_string(&b, NULL);
228 cipher = buffer_get_string(&b, NULL);
229 i2 = buffer_get_int(&b);
230 i3 = buffer_get_int(&b);
231 i4 = buffer_get_int(&b);
232 debug("ignore (%d %d %d %d)", i1, i2, i3, i4);
233 if (strcmp(cipher, "none") != 0) {
234 error("unsupported cipher %s", cipher);
235 xfree(cipher);
236 buffer_free(&b);
237 xfree(type);
238 return NULL;
239 }
240 xfree(cipher);
241
242 if (strstr(type, "dsa")) {
243 ktype = KEY_DSA;
244 } else if (strstr(type, "rsa")) {
245 ktype = KEY_RSA;
246 } else {
247 buffer_free(&b);
248 xfree(type);
249 return NULL;
250 }
251 key = key_new_private(ktype);
252 xfree(type);
253
254 switch (key->type) {
255 case KEY_DSA:
256 buffer_get_bignum_bits(&b, key->dsa->p);
257 buffer_get_bignum_bits(&b, key->dsa->g);
258 buffer_get_bignum_bits(&b, key->dsa->q);
259 buffer_get_bignum_bits(&b, key->dsa->pub_key);
260 buffer_get_bignum_bits(&b, key->dsa->priv_key);
261 break;
262 case KEY_RSA:
263 e = buffer_get_char(&b);
264 debug("e %lx", e);
265 if (e < 30) {
266 e <<= 8;
267 e += buffer_get_char(&b);
268 debug("e %lx", e);
269 e <<= 8;
270 e += buffer_get_char(&b);
271 debug("e %lx", e);
272 }
273 if (!BN_set_word(key->rsa->e, e)) {
274 buffer_free(&b);
275 key_free(key);
276 return NULL;
277 }
278 buffer_get_bignum_bits(&b, key->rsa->d);
279 buffer_get_bignum_bits(&b, key->rsa->n);
280 buffer_get_bignum_bits(&b, key->rsa->iqmp);
281 buffer_get_bignum_bits(&b, key->rsa->q);
282 buffer_get_bignum_bits(&b, key->rsa->p);
283 rsa_generate_additional_parameters(key->rsa);
284 break;
285 }
286 rlen = buffer_len(&b);
287 if (rlen != 0)
288 error("do_convert_private_ssh2_from_blob: "
289 "remaining bytes in key blob %d", rlen);
290 buffer_free(&b);
291
292 /* try the key */
293 key_sign(key, &sig, &slen, data, sizeof(data));
294 key_verify(key, sig, slen, data, sizeof(data));
295 xfree(sig);
296 return key;
297 }
298
299 static int
get_line(FILE * fp,char * line,size_t len)300 get_line(FILE *fp, char *line, size_t len)
301 {
302 int c;
303 size_t pos = 0;
304
305 line[0] = '\0';
306 while ((c = fgetc(fp)) != EOF) {
307 if (pos >= len - 1) {
308 fprintf(stderr, "input line too long.\n");
309 exit(1);
310 }
311 switch (c) {
312 case '\r':
313 c = fgetc(fp);
314 if (c != EOF && c != '\n' && ungetc(c, fp) == EOF) {
315 fprintf(stderr, "unget: %s\n", strerror(errno));
316 exit(1);
317 }
318 return pos;
319 case '\n':
320 return pos;
321 }
322 line[pos++] = c;
323 line[pos] = '\0';
324 }
325 /* We reached EOF */
326 return -1;
327 }
328
329 static void
do_convert_from_ssh2(struct passwd * pw)330 do_convert_from_ssh2(struct passwd *pw)
331 {
332 Key *k;
333 int blen;
334 u_int len;
335 char line[1024];
336 u_char blob[8096];
337 char encoded[8096];
338 struct stat st;
339 int escaped = 0, private = 0, ok;
340 FILE *fp;
341
342 if (!have_identity)
343 ask_filename(pw, gettext("Enter file in which the key is"));
344 if (stat(identity_file, &st) < 0) {
345 perror(identity_file);
346 exit(1);
347 }
348 fp = fopen(identity_file, "r");
349 if (fp == NULL) {
350 perror(identity_file);
351 exit(1);
352 }
353 encoded[0] = '\0';
354 while ((blen = get_line(fp, line, sizeof(line))) != -1) {
355 if (line[blen - 1] == '\\')
356 escaped++;
357 if (strncmp(line, "----", 4) == 0 ||
358 strstr(line, ": ") != NULL) {
359 if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL)
360 private = 1;
361 if (strstr(line, " END ") != NULL) {
362 break;
363 }
364 /* fprintf(stderr, "ignore: %s", line); */
365 continue;
366 }
367 if (escaped) {
368 escaped--;
369 /* fprintf(stderr, "escaped: %s", line); */
370 continue;
371 }
372 strlcat(encoded, line, sizeof(encoded));
373 }
374 len = strlen(encoded);
375 if (((len % 4) == 3) &&
376 (encoded[len-1] == '=') &&
377 (encoded[len-2] == '=') &&
378 (encoded[len-3] == '='))
379 encoded[len-3] = '\0';
380 blen = uudecode(encoded, blob, sizeof(blob));
381 if (blen < 0) {
382 fprintf(stderr, gettext("uudecode failed.\n"));
383 exit(1);
384 }
385 k = private ?
386 do_convert_private_ssh2_from_blob(blob, blen) :
387 key_from_blob(blob, blen);
388 if (k == NULL) {
389 fprintf(stderr, gettext("decode blob failed.\n"));
390 exit(1);
391 }
392 ok = private ?
393 (k->type == KEY_DSA ?
394 PEM_write_DSAPrivateKey(stdout, k->dsa, NULL, NULL, 0, NULL, NULL) :
395 PEM_write_RSAPrivateKey(stdout, k->rsa, NULL, NULL, 0, NULL, NULL)) :
396 key_write(k, stdout);
397 if (!ok) {
398 fprintf(stderr, gettext("key write failed"));
399 exit(1);
400 }
401 key_free(k);
402 if (!private)
403 fprintf(stdout, "\n");
404 fclose(fp);
405 exit(0);
406 }
407
408 static void
do_print_public(struct passwd * pw)409 do_print_public(struct passwd *pw)
410 {
411 Key *prv;
412 struct stat st;
413
414 if (!have_identity)
415 ask_filename(pw, gettext("Enter file in which the key is"));
416 if (stat(identity_file, &st) < 0) {
417 perror(identity_file);
418 exit(1);
419 }
420 prv = load_identity(identity_file);
421 if (prv == NULL) {
422 fprintf(stderr, gettext("load failed\n"));
423 exit(1);
424 }
425 if (!key_write(prv, stdout))
426 fprintf(stderr, gettext("key_write failed"));
427 key_free(prv);
428 fprintf(stdout, "\n");
429 exit(0);
430 }
431
432 static void
do_fingerprint(struct passwd * pw)433 do_fingerprint(struct passwd *pw)
434 {
435 FILE *f;
436 Key *public;
437 char *comment = NULL, *cp, *ep, line[16*1024], *fp;
438 int i, skip = 0, num = 1, invalid = 1;
439 enum fp_rep rep;
440 enum fp_type fptype;
441 struct stat st;
442
443 fptype = print_bubblebabble ? SSH_FP_SHA1 : SSH_FP_MD5;
444 rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_HEX;
445
446 if (!have_identity)
447 ask_filename(pw, gettext("Enter file in which the key is"));
448 if (stat(identity_file, &st) < 0) {
449 perror(identity_file);
450 exit(1);
451 }
452 public = key_load_public(identity_file, &comment);
453 if (public != NULL) {
454 fp = key_fingerprint(public, fptype, rep);
455 printf("%u %s %s\n", key_size(public), fp, comment);
456 key_free(public);
457 xfree(comment);
458 xfree(fp);
459 exit(0);
460 }
461 if (comment) {
462 xfree(comment);
463 comment = NULL;
464 }
465
466 f = fopen(identity_file, "r");
467 if (f != NULL) {
468 while (fgets(line, sizeof(line), f)) {
469 i = strlen(line) - 1;
470 if (line[i] != '\n') {
471 error("line %d too long: %.40s...", num, line);
472 skip = 1;
473 continue;
474 }
475 num++;
476 if (skip) {
477 skip = 0;
478 continue;
479 }
480 line[i] = '\0';
481
482 /* Skip leading whitespace, empty and comment lines. */
483 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
484 ;
485 if (!*cp || *cp == '\n' || *cp == '#')
486 continue;
487 i = strtol(cp, &ep, 10);
488 if (i == 0 || ep == NULL || (*ep != ' ' && *ep != '\t')) {
489 int quoted = 0;
490 comment = cp;
491 for (; *cp && (quoted || (*cp != ' ' &&
492 *cp != '\t')); cp++) {
493 if (*cp == '\\' && cp[1] == '"')
494 cp++; /* Skip both */
495 else if (*cp == '"')
496 quoted = !quoted;
497 }
498 if (!*cp)
499 continue;
500 *cp++ = '\0';
501 }
502 ep = cp;
503 public = key_new(KEY_RSA1);
504 if (key_read(public, &cp) != 1) {
505 cp = ep;
506 key_free(public);
507 public = key_new(KEY_UNSPEC);
508 if (key_read(public, &cp) != 1) {
509 key_free(public);
510 continue;
511 }
512 }
513 comment = *cp ? cp : comment;
514 fp = key_fingerprint(public, fptype, rep);
515 printf("%u %s %s\n", key_size(public), fp,
516 comment ? comment : gettext("no comment"));
517 xfree(fp);
518 key_free(public);
519 invalid = 0;
520 }
521 fclose(f);
522 }
523 if (invalid) {
524 printf(gettext("%s is not a public key file.\n"),
525 identity_file);
526 exit(1);
527 }
528 exit(0);
529 }
530
531 static void
print_host(FILE * f,const char * name,Key * public,int hash)532 print_host(FILE *f, const char *name, Key *public, int hash)
533 {
534 if (hash && (name = host_hash(name, NULL, 0)) == NULL)
535 fatal("hash_host failed");
536 fprintf(f, "%s ", name);
537 if (!key_write(public, f))
538 fatal("key_write failed");
539 fprintf(f, "\n");
540 }
541
542 static void
do_known_hosts(struct passwd * pw,const char * name)543 do_known_hosts(struct passwd *pw, const char *name)
544 {
545 FILE *in, *out = stdout;
546 Key *public;
547 char *cp, *cp2, *kp, *kp2;
548 char line[16*1024], tmp[MAXPATHLEN], old[MAXPATHLEN];
549 int c, i, skip = 0, inplace = 0, num = 0, invalid = 0, has_unhashed = 0;
550
551 if (!have_identity) {
552 cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid);
553 if (strlcpy(identity_file, cp, sizeof(identity_file)) >=
554 sizeof(identity_file))
555 fatal("Specified known hosts path too long");
556 xfree(cp);
557 have_identity = 1;
558 }
559 if ((in = fopen(identity_file, "r")) == NULL)
560 fatal("fopen: %s", strerror(errno));
561
562 /*
563 * Find hosts goes to stdout, hash and deletions happen in-place
564 * A corner case is ssh-keygen -HF foo, which should go to stdout
565 */
566 if (!find_host && (hash_hosts || delete_host)) {
567 if (strlcpy(tmp, identity_file, sizeof(tmp)) >= sizeof(tmp) ||
568 strlcat(tmp, ".XXXXXXXXXX", sizeof(tmp)) >= sizeof(tmp) ||
569 strlcpy(old, identity_file, sizeof(old)) >= sizeof(old) ||
570 strlcat(old, ".old", sizeof(old)) >= sizeof(old))
571 fatal("known_hosts path too long");
572 umask(077);
573 if ((c = mkstemp(tmp)) == -1)
574 fatal("mkstemp: %s", strerror(errno));
575 if ((out = fdopen(c, "w")) == NULL) {
576 c = errno;
577 unlink(tmp);
578 fatal("fdopen: %s", strerror(c));
579 }
580 inplace = 1;
581 }
582
583 while (fgets(line, sizeof(line), in)) {
584 num++;
585 i = strlen(line) - 1;
586 if (line[i] != '\n') {
587 error("line %d too long: %.40s...", num, line);
588 skip = 1;
589 invalid = 1;
590 continue;
591 }
592 if (skip) {
593 skip = 0;
594 continue;
595 }
596 line[i] = '\0';
597
598 /* Skip leading whitespace, empty and comment lines. */
599 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
600 ;
601 if (!*cp || *cp == '\n' || *cp == '#') {
602 if (inplace)
603 fprintf(out, "%s\n", cp);
604 continue;
605 }
606 /* Find the end of the host name portion. */
607 for (kp = cp; *kp && *kp != ' ' && *kp != '\t'; kp++)
608 ;
609 if (*kp == '\0' || *(kp + 1) == '\0') {
610 error("line %d missing key: %.40s...",
611 num, line);
612 invalid = 1;
613 continue;
614 }
615 *kp++ = '\0';
616 kp2 = kp;
617
618 public = key_new(KEY_RSA1);
619 if (key_read(public, &kp) != 1) {
620 kp = kp2;
621 key_free(public);
622 public = key_new(KEY_UNSPEC);
623 if (key_read(public, &kp) != 1) {
624 error("line %d invalid key: %.40s...",
625 num, line);
626 key_free(public);
627 invalid = 1;
628 continue;
629 }
630 }
631
632 if (*cp == HASH_DELIM) {
633 if (find_host || delete_host) {
634 cp2 = host_hash(name, cp, strlen(cp));
635 if (cp2 == NULL) {
636 error("line %d: invalid hashed "
637 "name: %.64s...", num, line);
638 invalid = 1;
639 continue;
640 }
641 c = (strcmp(cp2, cp) == 0);
642 if (find_host && c) {
643 printf(gettext("# Host %s found: "
644 "line %d type %s\n"), name,
645 num, key_type(public));
646 print_host(out, cp, public, 0);
647 }
648 if (delete_host && !c)
649 print_host(out, cp, public, 0);
650 } else if (hash_hosts)
651 print_host(out, cp, public, 0);
652 } else {
653 if (find_host || delete_host) {
654 c = (match_hostname(name, cp,
655 strlen(cp)) == 1);
656 if (find_host && c) {
657 printf(gettext("# Host %s found: "
658 "line %d type %s\n"), name,
659 num, key_type(public));
660 print_host(out, name, public, hash_hosts);
661 }
662 if (delete_host && !c)
663 print_host(out, cp, public, 0);
664 } else if (hash_hosts) {
665 for (cp2 = strsep(&cp, ",");
666 cp2 != NULL && *cp2 != '\0';
667 cp2 = strsep(&cp, ",")) {
668 if (strcspn(cp2, "*?!") != strlen(cp2))
669 fprintf(stderr, gettext("Warning: "
670 "ignoring host name with "
671 "metacharacters: %.64s\n"),
672 cp2);
673 else
674 print_host(out, cp2, public, 1);
675 }
676 has_unhashed = 1;
677 }
678 }
679 key_free(public);
680 }
681 fclose(in);
682
683 if (invalid) {
684 fprintf(stderr, gettext("%s is not a valid known_host file.\n"),
685 identity_file);
686 if (inplace) {
687 fprintf(stderr, gettext("Not replacing existing known_hosts "
688 "file because of errors\n"));
689 fclose(out);
690 unlink(tmp);
691 }
692 exit(1);
693 }
694
695 if (inplace) {
696 fclose(out);
697
698 /* Backup existing file */
699 if (unlink(old) == -1 && errno != ENOENT)
700 fatal("unlink %.100s: %s", old, strerror(errno));
701 if (link(identity_file, old) == -1)
702 fatal("link %.100s to %.100s: %s", identity_file, old,
703 strerror(errno));
704 /* Move new one into place */
705 if (rename(tmp, identity_file) == -1) {
706 error("rename\"%s\" to \"%s\": %s", tmp, identity_file,
707 strerror(errno));
708 unlink(tmp);
709 unlink(old);
710 exit(1);
711 }
712
713 fprintf(stderr, gettext("%s updated.\n"), identity_file);
714 fprintf(stderr, gettext("Original contents retained as %s\n"), old);
715 if (has_unhashed) {
716 fprintf(stderr, gettext("WARNING: %s contains unhashed "
717 "entries\n"), old);
718 fprintf(stderr, gettext("Delete this file to ensure privacy "
719 "of hostnames\n"));
720 }
721 }
722
723 exit(0);
724 }
725
726 /*
727 * Perform changing a passphrase. The argument is the passwd structure
728 * for the current user.
729 */
730 static void
do_change_passphrase(struct passwd * pw)731 do_change_passphrase(struct passwd *pw)
732 {
733 char *comment;
734 char *old_passphrase, *passphrase1, *passphrase2;
735 struct stat st;
736 Key *private;
737
738 if (!have_identity)
739 ask_filename(pw, gettext("Enter file in which the key is"));
740 if (stat(identity_file, &st) < 0) {
741 perror(identity_file);
742 exit(1);
743 }
744 /* Try to load the file with empty passphrase. */
745 private = key_load_private(identity_file, "", &comment);
746 if (private == NULL) {
747 if (identity_passphrase)
748 old_passphrase = xstrdup(identity_passphrase);
749 else
750 old_passphrase =
751 read_passphrase(gettext("Enter old passphrase: "),
752 RP_ALLOW_STDIN);
753 private = key_load_private(identity_file, old_passphrase,
754 &comment);
755 memset(old_passphrase, 0, strlen(old_passphrase));
756 xfree(old_passphrase);
757 if (private == NULL) {
758 printf(gettext("Bad passphrase.\n"));
759 exit(1);
760 }
761 }
762 printf(gettext("Key has comment '%s'\n"), comment);
763
764 /* Ask the new passphrase (twice). */
765 if (identity_new_passphrase) {
766 passphrase1 = xstrdup(identity_new_passphrase);
767 passphrase2 = NULL;
768 } else {
769 passphrase1 =
770 read_passphrase(gettext("Enter new passphrase (empty"
771 " for no passphrase): "), RP_ALLOW_STDIN);
772 passphrase2 = read_passphrase(gettext("Enter same "
773 "passphrase again: "), RP_ALLOW_STDIN);
774
775 /* Verify that they are the same. */
776 if (strcmp(passphrase1, passphrase2) != 0) {
777 memset(passphrase1, 0, strlen(passphrase1));
778 memset(passphrase2, 0, strlen(passphrase2));
779 xfree(passphrase1);
780 xfree(passphrase2);
781 printf(gettext("Pass phrases do not match. Try "
782 "again.\n"));
783 exit(1);
784 }
785 /* Destroy the other copy. */
786 memset(passphrase2, 0, strlen(passphrase2));
787 xfree(passphrase2);
788 }
789
790 /* Save the file using the new passphrase. */
791 if (!key_save_private(private, identity_file, passphrase1, comment)) {
792 printf(gettext("Saving the key failed: %s.\n"), identity_file);
793 memset(passphrase1, 0, strlen(passphrase1));
794 xfree(passphrase1);
795 key_free(private);
796 xfree(comment);
797 exit(1);
798 }
799 /* Destroy the passphrase and the copy of the key in memory. */
800 memset(passphrase1, 0, strlen(passphrase1));
801 xfree(passphrase1);
802 key_free(private); /* Destroys contents */
803 xfree(comment);
804
805 printf(gettext("Your identification has been saved with the new "
806 "passphrase.\n"));
807 exit(0);
808 }
809
810 /*
811 * Change the comment of a private key file.
812 */
813 static void
do_change_comment(struct passwd * pw)814 do_change_comment(struct passwd *pw)
815 {
816 char new_comment[1024], *comment, *passphrase;
817 Key *private;
818 Key *public;
819 struct stat st;
820 FILE *f;
821 int fd;
822
823 if (!have_identity)
824 ask_filename(pw, gettext("Enter file in which the key is"));
825 if (stat(identity_file, &st) < 0) {
826 perror(identity_file);
827 exit(1);
828 }
829 private = key_load_private(identity_file, "", &comment);
830 if (private == NULL) {
831 if (identity_passphrase)
832 passphrase = xstrdup(identity_passphrase);
833 else if (identity_new_passphrase)
834 passphrase = xstrdup(identity_new_passphrase);
835 else
836 passphrase =
837 read_passphrase(gettext("Enter passphrase: "),
838 RP_ALLOW_STDIN);
839 /* Try to load using the passphrase. */
840 private = key_load_private(identity_file, passphrase, &comment);
841 if (private == NULL) {
842 memset(passphrase, 0, strlen(passphrase));
843 xfree(passphrase);
844 printf(gettext("Bad passphrase.\n"));
845 exit(1);
846 }
847 } else {
848 passphrase = xstrdup("");
849 }
850 if (private->type != KEY_RSA1) {
851 fprintf(stderr, gettext("Comments are only supported for "
852 "RSA1 keys.\n"));
853 key_free(private);
854 exit(1);
855 }
856 printf(gettext("Key now has comment '%s'\n"), comment);
857
858 if (identity_comment) {
859 strlcpy(new_comment, identity_comment, sizeof(new_comment));
860 } else {
861 printf(gettext("Enter new comment: "));
862 fflush(stdout);
863 if (!fgets(new_comment, sizeof(new_comment), stdin)) {
864 memset(passphrase, 0, strlen(passphrase));
865 key_free(private);
866 exit(1);
867 }
868 if (strchr(new_comment, '\n'))
869 *strchr(new_comment, '\n') = 0;
870 }
871
872 /* Save the file using the new passphrase. */
873 if (!key_save_private(private, identity_file, passphrase, new_comment)) {
874 printf(gettext("Saving the key failed: %s.\n"), identity_file);
875 memset(passphrase, 0, strlen(passphrase));
876 xfree(passphrase);
877 key_free(private);
878 xfree(comment);
879 exit(1);
880 }
881 memset(passphrase, 0, strlen(passphrase));
882 xfree(passphrase);
883 public = key_from_private(private);
884 key_free(private);
885
886 strlcat(identity_file, ".pub", sizeof(identity_file));
887 fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
888 if (fd == -1) {
889 printf(gettext("Could not save your public key in %s\n"),
890 identity_file);
891 exit(1);
892 }
893 f = fdopen(fd, "w");
894 if (f == NULL) {
895 printf(gettext("fdopen %s failed"), identity_file);
896 exit(1);
897 }
898 if (!key_write(public, f))
899 fprintf(stderr, gettext("write key failed"));
900 key_free(public);
901 fprintf(f, " %s\n", new_comment);
902 fclose(f);
903
904 xfree(comment);
905
906 printf(gettext("The comment in your key file has been changed.\n"));
907 exit(0);
908 }
909
910 static void
usage(void)911 usage(void)
912 {
913 fprintf(stderr, gettext(
914 "Usage: %s [options]\n"
915 "Options:\n"
916 " -b bits Number of bits in the key to create.\n"
917 " -B Show bubblebabble digest of key file.\n"
918 " -c Change comment in private and public key files.\n"
919 " -C comment Provide new comment.\n"
920 " -e Convert OpenSSH to IETF SECSH key file.\n"
921 " -f filename Filename of the key file.\n"
922 " -F hostname Find hostname in known hosts file.\n"
923 " -H Hash names in known_hosts file.\n"
924 " -i Convert IETF SECSH to OpenSSH key file.\n"
925 " -l Show fingerprint of key file.\n"
926 " -N phrase Provide new passphrase.\n"
927 " -p Change passphrase of private key file.\n"
928 " -P phrase Provide old passphrase.\n"
929 " -q Quiet.\n"
930 " -R hostname Remove host from known_hosts file.\n"
931 " -t type Specify type of key to create.\n"
932 " -y Read private key file and print public key.\n"
933 ), __progname);
934
935 exit(1);
936 }
937
938 /*
939 * Main program for key management.
940 */
941 int
main(int argc,char ** argv)942 main(int argc, char **argv)
943 {
944 char dotsshdir[MAXPATHLEN], comment[1024], *passphrase1, *passphrase2;
945 char *rr_hostname = NULL;
946 Key *private, *public;
947 struct passwd *pw;
948 struct stat st;
949 int opt, type, fd;
950 FILE *f;
951
952 extern int optind;
953 extern char *optarg;
954
955 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
956 sanitise_stdfd();
957
958 __progname = get_progname(argv[0]);
959
960 g11n_setlocale(LC_ALL, "");
961
962 SSLeay_add_all_algorithms();
963 init_rng();
964 seed_rng();
965
966 /* we need this for the home * directory. */
967 pw = getpwuid(getuid());
968 if (!pw) {
969 printf(gettext("You don't exist, go away!\n"));
970 exit(1);
971 }
972 if (gethostname(hostname, sizeof(hostname)) < 0) {
973 perror("gethostname");
974 exit(1);
975 }
976
977 #define GETOPT_ARGS "BcdeHilpqxXyb:C:f:F:N:P:R:t:"
978
979 while ((opt = getopt(argc, argv, GETOPT_ARGS)) != -1) {
980 switch (opt) {
981 case 'b':
982 bits = atoi(optarg);
983 if (bits < 512 || bits > 32768) {
984 printf(gettext("Bits has bad value.\n"));
985 exit(1);
986 }
987 break;
988 case 'F':
989 find_host = 1;
990 rr_hostname = optarg;
991 break;
992 case 'H':
993 hash_hosts = 1;
994 break;
995 case 'R':
996 delete_host = 1;
997 rr_hostname = optarg;
998 break;
999 case 'l':
1000 print_fingerprint = 1;
1001 break;
1002 case 'B':
1003 print_bubblebabble = 1;
1004 break;
1005 case 'p':
1006 change_passphrase = 1;
1007 break;
1008 case 'c':
1009 change_comment = 1;
1010 break;
1011 case 'f':
1012 strlcpy(identity_file, optarg, sizeof(identity_file));
1013 have_identity = 1;
1014 break;
1015 case 'P':
1016 identity_passphrase = optarg;
1017 break;
1018 case 'N':
1019 identity_new_passphrase = optarg;
1020 break;
1021 case 'C':
1022 identity_comment = optarg;
1023 break;
1024 case 'q':
1025 quiet = 1;
1026 break;
1027 case 'e':
1028 case 'x':
1029 /* export key */
1030 convert_to_ssh2 = 1;
1031 break;
1032 case 'i':
1033 case 'X':
1034 /* import key */
1035 convert_from_ssh2 = 1;
1036 break;
1037 case 'y':
1038 print_public = 1;
1039 break;
1040 case 'd':
1041 key_type_name = "dsa";
1042 break;
1043 case 't':
1044 key_type_name = optarg;
1045 break;
1046 case '?':
1047 default:
1048 usage();
1049 }
1050 }
1051 if (optind < argc) {
1052 printf(gettext("Too many arguments.\n"));
1053 usage();
1054 }
1055 if (change_passphrase && change_comment) {
1056 printf(gettext("Can only have one of -p and -c.\n"));
1057 usage();
1058 }
1059 if (delete_host || hash_hosts || find_host)
1060 do_known_hosts(pw, rr_hostname);
1061 if (print_fingerprint || print_bubblebabble)
1062 do_fingerprint(pw);
1063 if (change_passphrase)
1064 do_change_passphrase(pw);
1065 if (change_comment)
1066 do_change_comment(pw);
1067 if (convert_to_ssh2)
1068 do_convert_to_ssh2(pw);
1069 if (convert_from_ssh2)
1070 do_convert_from_ssh2(pw);
1071 if (print_public)
1072 do_print_public(pw);
1073
1074 arc4random_stir();
1075
1076 if (key_type_name == NULL) {
1077 printf(gettext("You must specify a key type (-t).\n"));
1078 usage();
1079 }
1080 type = key_type_from_name(key_type_name);
1081 if (type == KEY_UNSPEC) {
1082 fprintf(stderr, gettext("unknown key type %s\n"),
1083 key_type_name);
1084 exit(1);
1085 }
1086 if (bits == 0)
1087 bits = (type == KEY_DSA) ? DEFAULT_BITS_DSA : DEFAULT_BITS_RSA;
1088
1089 if (!quiet)
1090 printf(gettext("Generating public/private %s key pair.\n"),
1091 key_type_name);
1092 private = key_generate(type, bits);
1093 if (private == NULL) {
1094 fprintf(stderr, gettext("key_generate failed"));
1095 exit(1);
1096 }
1097 public = key_from_private(private);
1098
1099 if (!have_identity)
1100 ask_filename(pw, gettext("Enter file in which to save the key"));
1101
1102 /* Create ~/.ssh directory if it doesn't already exist. */
1103 snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, _PATH_SSH_USER_DIR);
1104 if (strstr(identity_file, dotsshdir) != NULL &&
1105 stat(dotsshdir, &st) < 0) {
1106 if (mkdir(dotsshdir, 0700) < 0)
1107 error("Could not create directory '%s'.", dotsshdir);
1108 else if (!quiet)
1109 printf(gettext("Created directory '%s'.\n"), dotsshdir);
1110 }
1111 /* If the file already exists, ask the user to confirm. */
1112 if (stat(identity_file, &st) >= 0) {
1113 char yesno[128];
1114 printf(gettext("%s already exists.\n"), identity_file);
1115 printf(gettext("Overwrite (%s/%s)? "),
1116 nl_langinfo(YESSTR), nl_langinfo(NOSTR));
1117 fflush(stdout);
1118 if (fgets(yesno, sizeof(yesno), stdin) == NULL)
1119 exit(1);
1120 if (strcasecmp(chop(yesno), nl_langinfo(YESSTR)) != 0)
1121 exit(1);
1122 }
1123 /* Ask for a passphrase (twice). */
1124 if (identity_passphrase)
1125 passphrase1 = xstrdup(identity_passphrase);
1126 else if (identity_new_passphrase)
1127 passphrase1 = xstrdup(identity_new_passphrase);
1128 else {
1129 passphrase_again:
1130 passphrase1 =
1131 read_passphrase(gettext("Enter passphrase (empty "
1132 "for no passphrase): "), RP_ALLOW_STDIN);
1133 passphrase2 = read_passphrase(gettext("Enter same "
1134 "passphrase again: "), RP_ALLOW_STDIN);
1135 if (strcmp(passphrase1, passphrase2) != 0) {
1136 /*
1137 * The passphrases do not match. Clear them and
1138 * retry.
1139 */
1140 memset(passphrase1, 0, strlen(passphrase1));
1141 memset(passphrase2, 0, strlen(passphrase2));
1142 xfree(passphrase1);
1143 xfree(passphrase2);
1144 printf(gettext("Passphrases do not match. Try "
1145 "again.\n"));
1146 goto passphrase_again;
1147 }
1148 /* Clear the other copy of the passphrase. */
1149 memset(passphrase2, 0, strlen(passphrase2));
1150 xfree(passphrase2);
1151 }
1152
1153 if (identity_comment) {
1154 strlcpy(comment, identity_comment, sizeof(comment));
1155 } else {
1156 /* Create default commend field for the passphrase. */
1157 snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname);
1158 }
1159
1160 /* Save the key with the given passphrase and comment. */
1161 if (!key_save_private(private, identity_file, passphrase1, comment)) {
1162 printf(gettext("Saving the key failed: %s.\n"), identity_file);
1163 memset(passphrase1, 0, strlen(passphrase1));
1164 xfree(passphrase1);
1165 exit(1);
1166 }
1167 /* Clear the passphrase. */
1168 memset(passphrase1, 0, strlen(passphrase1));
1169 xfree(passphrase1);
1170
1171 /* Clear the private key and the random number generator. */
1172 key_free(private);
1173 arc4random_stir();
1174
1175 if (!quiet)
1176 printf(gettext("Your identification has been saved in %s.\n"),
1177 identity_file);
1178
1179 strlcat(identity_file, ".pub", sizeof(identity_file));
1180 fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
1181 if (fd == -1) {
1182 printf(gettext("Could not save your public key in %s\n"),
1183 identity_file);
1184 exit(1);
1185 }
1186 f = fdopen(fd, "w");
1187 if (f == NULL) {
1188 printf(gettext("fdopen %s failed"), identity_file);
1189 exit(1);
1190 }
1191 if (!key_write(public, f))
1192 fprintf(stderr, gettext("write key failed"));
1193 fprintf(f, " %s\n", comment);
1194 fclose(f);
1195
1196 if (!quiet) {
1197 char *fp = key_fingerprint(public, SSH_FP_MD5, SSH_FP_HEX);
1198 printf(gettext("Your public key has been saved in %s.\n"),
1199 identity_file);
1200 printf(gettext("The key fingerprint is:\n"));
1201 printf("%s %s\n", fp, comment);
1202 xfree(fp);
1203 }
1204
1205 key_free(public);
1206 return(0);
1207 /* NOTREACHED */
1208 }
1209