]>
Commit | Line | Data |
---|---|---|
8efc0c15 | 1 | /* |
5260325f | 2 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
3 | * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | |
4 | * All rights reserved | |
5260325f | 5 | * Identity and host key generation and maintenance. |
bcbf86ec | 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". | |
5260325f | 12 | */ |
8efc0c15 | 13 | |
14 | #include "includes.h" | |
94ec8c6b | 15 | RCSID("$OpenBSD: ssh-keygen.c,v 1.32 2000/10/09 21:30:44 markus Exp $"); |
8efc0c15 | 16 | |
a306f2dd | 17 | #include <openssl/evp.h> |
18 | #include <openssl/pem.h> | |
19 | #include <openssl/rsa.h> | |
20 | #include <openssl/dsa.h> | |
21 | ||
8efc0c15 | 22 | #include "ssh.h" |
23 | #include "xmalloc.h" | |
a306f2dd | 24 | #include "key.h" |
25 | #include "rsa.h" | |
26 | #include "dsa.h" | |
27 | #include "authfile.h" | |
28 | #include "uuencode.h" | |
8efc0c15 | 29 | |
94ec8c6b | 30 | #include "buffer.h" |
31 | #include "bufaux.h" | |
32 | ||
a306f2dd | 33 | /* Number of bits in the RSA/DSA key. This value can be changed on the command line. */ |
8efc0c15 | 34 | int bits = 1024; |
35 | ||
aa3378df | 36 | /* |
37 | * Flag indicating that we just want to change the passphrase. This can be | |
38 | * set on the command line. | |
39 | */ | |
8efc0c15 | 40 | int change_passphrase = 0; |
41 | ||
aa3378df | 42 | /* |
43 | * Flag indicating that we just want to change the comment. This can be set | |
44 | * on the command line. | |
45 | */ | |
8efc0c15 | 46 | int change_comment = 0; |
47 | ||
48 | int quiet = 0; | |
49 | ||
f095fcc7 | 50 | /* Flag indicating that we just want to see the key fingerprint */ |
51 | int print_fingerprint = 0; | |
52 | ||
5ad13cd7 | 53 | /* The identity file name, given on the command line or entered by the user. */ |
54 | char identity_file[1024]; | |
55 | int have_identity = 0; | |
8efc0c15 | 56 | |
57 | /* This is set to the passphrase if given on the command line. */ | |
58 | char *identity_passphrase = NULL; | |
59 | ||
60 | /* This is set to the new passphrase if given on the command line. */ | |
61 | char *identity_new_passphrase = NULL; | |
62 | ||
63 | /* This is set to the new comment if given on the command line. */ | |
64 | char *identity_comment = NULL; | |
65 | ||
a306f2dd | 66 | /* Dump public key file in format used by real and the original SSH 2 */ |
67 | int convert_to_ssh2 = 0; | |
68 | int convert_from_ssh2 = 0; | |
69 | int print_public = 0; | |
70 | int dsa_mode = 0; | |
71 | ||
5ad13cd7 | 72 | /* argv0 */ |
5260325f | 73 | #ifdef HAVE___PROGNAME |
5ad13cd7 | 74 | extern char *__progname; |
5260325f | 75 | #else /* HAVE___PROGNAME */ |
3fd95d9a | 76 | static const char *__progname = "ssh-keygen"; |
5260325f | 77 | #endif /* HAVE___PROGNAME */ |
8efc0c15 | 78 | |
a306f2dd | 79 | char hostname[MAXHOSTNAMELEN]; |
80 | ||
5ad13cd7 | 81 | void |
82 | ask_filename(struct passwd *pw, const char *prompt) | |
8efc0c15 | 83 | { |
5260325f | 84 | char buf[1024]; |
85 | snprintf(identity_file, sizeof(identity_file), "%s/%s", | |
1d1ffb87 | 86 | pw->pw_dir, |
87 | dsa_mode ? SSH_CLIENT_ID_DSA: SSH_CLIENT_IDENTITY); | |
5260325f | 88 | printf("%s (%s): ", prompt, identity_file); |
89 | fflush(stdout); | |
90 | if (fgets(buf, sizeof(buf), stdin) == NULL) | |
91 | exit(1); | |
92 | if (strchr(buf, '\n')) | |
93 | *strchr(buf, '\n') = 0; | |
94 | if (strcmp(buf, "") != 0) | |
95 | strlcpy(identity_file, buf, sizeof(identity_file)); | |
96 | have_identity = 1; | |
f095fcc7 | 97 | } |
8efc0c15 | 98 | |
a306f2dd | 99 | int |
100 | try_load_key(char *filename, Key *k) | |
101 | { | |
102 | int success = 1; | |
103 | if (!load_private_key(filename, "", k, NULL)) { | |
104 | char *pass = read_passphrase("Enter passphrase: ", 1); | |
105 | if (!load_private_key(filename, pass, k, NULL)) { | |
106 | success = 0; | |
107 | } | |
108 | memset(pass, 0, strlen(pass)); | |
109 | xfree(pass); | |
110 | } | |
111 | return success; | |
112 | } | |
113 | ||
94ec8c6b | 114 | #define SSH_COM_PUBLIC_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----" |
115 | #define SSH_COM_PUBLIC_END "---- END SSH2 PUBLIC KEY ----" | |
116 | #define SSH_COM_PRIVATE_BEGIN "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----" | |
117 | #define SSH_COM_PRIVATE_KEY_MAGIC 0x3f6ff9eb | |
a306f2dd | 118 | |
119 | void | |
120 | do_convert_to_ssh2(struct passwd *pw) | |
121 | { | |
122 | Key *k; | |
123 | int len; | |
124 | unsigned char *blob; | |
125 | struct stat st; | |
126 | ||
127 | if (!have_identity) | |
128 | ask_filename(pw, "Enter file in which the key is"); | |
129 | if (stat(identity_file, &st) < 0) { | |
130 | perror(identity_file); | |
131 | exit(1); | |
132 | } | |
133 | k = key_new(KEY_DSA); | |
134 | if (!try_load_key(identity_file, k)) { | |
135 | fprintf(stderr, "load failed\n"); | |
136 | exit(1); | |
137 | } | |
138 | dsa_make_key_blob(k, &blob, &len); | |
94ec8c6b | 139 | fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN); |
a306f2dd | 140 | fprintf(stdout, |
94ec8c6b | 141 | "Comment: \"%d-bit %s, converted from OpenSSH by %s@%s\"\n", |
142 | key_size(k), key_type(k), | |
a306f2dd | 143 | pw->pw_name, hostname); |
144 | dump_base64(stdout, blob, len); | |
94ec8c6b | 145 | fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END); |
a306f2dd | 146 | key_free(k); |
147 | xfree(blob); | |
148 | exit(0); | |
149 | } | |
150 | ||
94ec8c6b | 151 | void |
152 | buffer_get_bignum_bits(Buffer *b, BIGNUM *value) | |
153 | { | |
154 | int bits = buffer_get_int(b); | |
155 | int bytes = (bits + 7) / 8; | |
156 | if (buffer_len(b) < bytes) | |
157 | fatal("buffer_get_bignum_bits: input buffer too small"); | |
158 | BN_bin2bn((unsigned char *)buffer_ptr(b), bytes, value); | |
159 | buffer_consume(b, bytes); | |
160 | } | |
161 | ||
162 | Key * | |
163 | do_convert_private_ssh2_from_blob(char *blob, int blen) | |
164 | { | |
165 | Buffer b; | |
166 | DSA *dsa; | |
167 | Key *key = NULL; | |
168 | int ignore, magic, rlen; | |
169 | char *type, *cipher; | |
170 | ||
171 | buffer_init(&b); | |
172 | buffer_append(&b, blob, blen); | |
173 | ||
174 | magic = buffer_get_int(&b); | |
175 | if (magic != SSH_COM_PRIVATE_KEY_MAGIC) { | |
176 | error("bad magic 0x%x != 0x%x", magic, SSH_COM_PRIVATE_KEY_MAGIC); | |
177 | buffer_free(&b); | |
178 | return NULL; | |
179 | } | |
180 | ignore = buffer_get_int(&b); | |
181 | type = buffer_get_string(&b, NULL); | |
182 | cipher = buffer_get_string(&b, NULL); | |
183 | ignore = buffer_get_int(&b); | |
184 | ignore = buffer_get_int(&b); | |
185 | ignore = buffer_get_int(&b); | |
186 | xfree(type); | |
187 | ||
188 | if (strcmp(cipher, "none") != 0) { | |
189 | error("unsupported cipher %s", cipher); | |
190 | xfree(cipher); | |
191 | buffer_free(&b); | |
192 | return NULL; | |
193 | } | |
194 | xfree(cipher); | |
195 | ||
196 | key = key_new(KEY_DSA); | |
197 | dsa = key->dsa; | |
198 | dsa->priv_key = BN_new(); | |
199 | if (dsa->priv_key == NULL) { | |
200 | error("alloc priv_key failed"); | |
201 | key_free(key); | |
202 | return NULL; | |
203 | } | |
204 | buffer_get_bignum_bits(&b, dsa->p); | |
205 | buffer_get_bignum_bits(&b, dsa->g); | |
206 | buffer_get_bignum_bits(&b, dsa->q); | |
207 | buffer_get_bignum_bits(&b, dsa->pub_key); | |
208 | buffer_get_bignum_bits(&b, dsa->priv_key); | |
209 | rlen = buffer_len(&b); | |
210 | if(rlen != 0) | |
211 | error("do_convert_private_ssh2_from_blob: remaining bytes in key blob %d", rlen); | |
212 | buffer_free(&b); | |
213 | return key; | |
214 | } | |
215 | ||
a306f2dd | 216 | void |
217 | do_convert_from_ssh2(struct passwd *pw) | |
218 | { | |
219 | Key *k; | |
220 | int blen; | |
221 | char line[1024], *p; | |
222 | char blob[8096]; | |
223 | char encoded[8096]; | |
224 | struct stat st; | |
94ec8c6b | 225 | int escaped = 0, private = 0, ok; |
a306f2dd | 226 | FILE *fp; |
227 | ||
228 | if (!have_identity) | |
229 | ask_filename(pw, "Enter file in which the key is"); | |
230 | if (stat(identity_file, &st) < 0) { | |
231 | perror(identity_file); | |
232 | exit(1); | |
233 | } | |
234 | fp = fopen(identity_file, "r"); | |
235 | if (fp == NULL) { | |
236 | perror(identity_file); | |
237 | exit(1); | |
238 | } | |
239 | encoded[0] = '\0'; | |
240 | while (fgets(line, sizeof(line), fp)) { | |
d0c832f3 | 241 | if (!(p = strchr(line, '\n'))) { |
242 | fprintf(stderr, "input line too long.\n"); | |
243 | exit(1); | |
244 | } | |
245 | if (p > line && p[-1] == '\\') | |
246 | escaped++; | |
a306f2dd | 247 | if (strncmp(line, "----", 4) == 0 || |
248 | strstr(line, ": ") != NULL) { | |
94ec8c6b | 249 | if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL) |
250 | private = 1; | |
a306f2dd | 251 | fprintf(stderr, "ignore: %s", line); |
252 | continue; | |
253 | } | |
d0c832f3 | 254 | if (escaped) { |
255 | escaped--; | |
256 | fprintf(stderr, "escaped: %s", line); | |
257 | continue; | |
a306f2dd | 258 | } |
259 | *p = '\0'; | |
260 | strlcat(encoded, line, sizeof(encoded)); | |
261 | } | |
262 | blen = uudecode(encoded, (unsigned char *)blob, sizeof(blob)); | |
263 | if (blen < 0) { | |
264 | fprintf(stderr, "uudecode failed.\n"); | |
265 | exit(1); | |
266 | } | |
94ec8c6b | 267 | k = private ? |
268 | do_convert_private_ssh2_from_blob(blob, blen) : | |
269 | dsa_key_from_blob(blob, blen); | |
270 | if (k == NULL) { | |
271 | fprintf(stderr, "decode blob failed.\n"); | |
272 | exit(1); | |
273 | } | |
274 | ok = private ? | |
275 | PEM_write_DSAPrivateKey(stdout, k->dsa, NULL, NULL, 0, NULL, NULL) : | |
276 | key_write(k, stdout); | |
277 | if (!ok) { | |
278 | fprintf(stderr, "key write failed"); | |
279 | exit(1); | |
280 | } | |
a306f2dd | 281 | key_free(k); |
282 | fprintf(stdout, "\n"); | |
283 | fclose(fp); | |
284 | exit(0); | |
285 | } | |
286 | ||
287 | void | |
288 | do_print_public(struct passwd *pw) | |
289 | { | |
290 | Key *k; | |
291 | int len; | |
292 | unsigned char *blob; | |
293 | struct stat st; | |
294 | ||
295 | if (!have_identity) | |
296 | ask_filename(pw, "Enter file in which the key is"); | |
297 | if (stat(identity_file, &st) < 0) { | |
298 | perror(identity_file); | |
299 | exit(1); | |
300 | } | |
301 | k = key_new(KEY_DSA); | |
302 | if (!try_load_key(identity_file, k)) { | |
303 | fprintf(stderr, "load failed\n"); | |
304 | exit(1); | |
305 | } | |
306 | dsa_make_key_blob(k, &blob, &len); | |
307 | if (!key_write(k, stdout)) | |
308 | fprintf(stderr, "key_write failed"); | |
309 | key_free(k); | |
310 | xfree(blob); | |
311 | fprintf(stdout, "\n"); | |
312 | exit(0); | |
313 | } | |
314 | ||
f095fcc7 | 315 | void |
316 | do_fingerprint(struct passwd *pw) | |
317 | { | |
2e73a022 | 318 | /* XXX RSA1 only */ |
319 | ||
c8d54615 | 320 | FILE *f; |
a306f2dd | 321 | Key *public; |
c8d54615 | 322 | char *comment = NULL, *cp, *ep, line[16*1024]; |
323 | int i, skip = 0, num = 1, invalid = 1; | |
610cd5c6 | 324 | unsigned int ignore; |
5260325f | 325 | struct stat st; |
326 | ||
327 | if (!have_identity) | |
328 | ask_filename(pw, "Enter file in which the key is"); | |
329 | if (stat(identity_file, &st) < 0) { | |
330 | perror(identity_file); | |
331 | exit(1); | |
332 | } | |
a306f2dd | 333 | public = key_new(KEY_RSA); |
334 | if (load_public_key(identity_file, public, &comment)) { | |
335 | printf("%d %s %s\n", BN_num_bits(public->rsa->n), | |
336 | key_fingerprint(public), comment); | |
337 | key_free(public); | |
c8d54615 | 338 | exit(0); |
339 | } | |
c8d54615 | 340 | |
341 | f = fopen(identity_file, "r"); | |
342 | if (f != NULL) { | |
c8d54615 | 343 | while (fgets(line, sizeof(line), f)) { |
344 | i = strlen(line) - 1; | |
345 | if (line[i] != '\n') { | |
346 | error("line %d too long: %.40s...", num, line); | |
347 | skip = 1; | |
348 | continue; | |
349 | } | |
350 | num++; | |
351 | if (skip) { | |
352 | skip = 0; | |
353 | continue; | |
354 | } | |
355 | line[i] = '\0'; | |
356 | ||
357 | /* Skip leading whitespace, empty and comment lines. */ | |
358 | for (cp = line; *cp == ' ' || *cp == '\t'; cp++) | |
359 | ; | |
360 | if (!*cp || *cp == '\n' || *cp == '#') | |
361 | continue ; | |
362 | i = strtol(cp, &ep, 10); | |
363 | if (i == 0 || ep == NULL || (*ep != ' ' && *ep != '\t')) { | |
364 | int quoted = 0; | |
365 | comment = cp; | |
366 | for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { | |
367 | if (*cp == '\\' && cp[1] == '"') | |
368 | cp++; /* Skip both */ | |
369 | else if (*cp == '"') | |
370 | quoted = !quoted; | |
371 | } | |
372 | if (!*cp) | |
373 | continue; | |
374 | *cp++ = '\0'; | |
375 | } | |
376 | ep = cp; | |
2e73a022 | 377 | if (auth_rsa_read_key(&cp, &ignore, public->rsa->e, public->rsa->n)) { |
c8d54615 | 378 | invalid = 0; |
379 | comment = *cp ? cp : comment; | |
2e73a022 | 380 | printf("%d %s %s\n", key_size(public), |
381 | key_fingerprint(public), | |
c8d54615 | 382 | comment ? comment : "no comment"); |
5260325f | 383 | } |
5260325f | 384 | } |
c8d54615 | 385 | fclose(f); |
386 | } | |
2e73a022 | 387 | key_free(public); |
c8d54615 | 388 | if (invalid) { |
389 | printf("%s is not a valid key file.\n", identity_file); | |
390 | exit(1); | |
5260325f | 391 | } |
5260325f | 392 | exit(0); |
f095fcc7 | 393 | } |
394 | ||
5260325f | 395 | /* |
396 | * Perform changing a passphrase. The argument is the passwd structure | |
397 | * for the current user. | |
398 | */ | |
f095fcc7 | 399 | void |
400 | do_change_passphrase(struct passwd *pw) | |
401 | { | |
5260325f | 402 | char *comment; |
403 | char *old_passphrase, *passphrase1, *passphrase2; | |
404 | struct stat st; | |
a306f2dd | 405 | Key *private; |
406 | Key *public; | |
407 | int type = dsa_mode ? KEY_DSA : KEY_RSA; | |
5260325f | 408 | |
409 | if (!have_identity) | |
410 | ask_filename(pw, "Enter file in which the key is"); | |
5260325f | 411 | if (stat(identity_file, &st) < 0) { |
412 | perror(identity_file); | |
413 | exit(1); | |
414 | } | |
a306f2dd | 415 | |
416 | if (type == KEY_RSA) { | |
417 | /* XXX this works currently only for RSA */ | |
418 | public = key_new(type); | |
419 | if (!load_public_key(identity_file, public, NULL)) { | |
420 | printf("%s is not a valid key file.\n", identity_file); | |
421 | exit(1); | |
422 | } | |
423 | /* Clear the public key since we are just about to load the whole file. */ | |
424 | key_free(public); | |
5260325f | 425 | } |
5260325f | 426 | |
427 | /* Try to load the file with empty passphrase. */ | |
a306f2dd | 428 | private = key_new(type); |
429 | if (!load_private_key(identity_file, "", private, &comment)) { | |
5260325f | 430 | if (identity_passphrase) |
431 | old_passphrase = xstrdup(identity_passphrase); | |
432 | else | |
433 | old_passphrase = read_passphrase("Enter old passphrase: ", 1); | |
a306f2dd | 434 | if (!load_private_key(identity_file, old_passphrase, private, &comment)) { |
5260325f | 435 | memset(old_passphrase, 0, strlen(old_passphrase)); |
436 | xfree(old_passphrase); | |
437 | printf("Bad passphrase.\n"); | |
438 | exit(1); | |
439 | } | |
5260325f | 440 | memset(old_passphrase, 0, strlen(old_passphrase)); |
441 | xfree(old_passphrase); | |
442 | } | |
443 | printf("Key has comment '%s'\n", comment); | |
444 | ||
445 | /* Ask the new passphrase (twice). */ | |
446 | if (identity_new_passphrase) { | |
447 | passphrase1 = xstrdup(identity_new_passphrase); | |
448 | passphrase2 = NULL; | |
449 | } else { | |
450 | passphrase1 = | |
451 | read_passphrase("Enter new passphrase (empty for no passphrase): ", 1); | |
452 | passphrase2 = read_passphrase("Enter same passphrase again: ", 1); | |
453 | ||
454 | /* Verify that they are the same. */ | |
455 | if (strcmp(passphrase1, passphrase2) != 0) { | |
456 | memset(passphrase1, 0, strlen(passphrase1)); | |
457 | memset(passphrase2, 0, strlen(passphrase2)); | |
458 | xfree(passphrase1); | |
459 | xfree(passphrase2); | |
460 | printf("Pass phrases do not match. Try again.\n"); | |
461 | exit(1); | |
462 | } | |
463 | /* Destroy the other copy. */ | |
464 | memset(passphrase2, 0, strlen(passphrase2)); | |
465 | xfree(passphrase2); | |
8efc0c15 | 466 | } |
8efc0c15 | 467 | |
5260325f | 468 | /* Save the file using the new passphrase. */ |
a306f2dd | 469 | if (!save_private_key(identity_file, passphrase1, private, comment)) { |
5260325f | 470 | printf("Saving the key failed: %s: %s.\n", |
471 | identity_file, strerror(errno)); | |
472 | memset(passphrase1, 0, strlen(passphrase1)); | |
473 | xfree(passphrase1); | |
a306f2dd | 474 | key_free(private); |
5260325f | 475 | xfree(comment); |
476 | exit(1); | |
477 | } | |
478 | /* Destroy the passphrase and the copy of the key in memory. */ | |
479 | memset(passphrase1, 0, strlen(passphrase1)); | |
480 | xfree(passphrase1); | |
a306f2dd | 481 | key_free(private); /* Destroys contents */ |
5260325f | 482 | xfree(comment); |
483 | ||
484 | printf("Your identification has been saved with the new passphrase.\n"); | |
485 | exit(0); | |
486 | } | |
8efc0c15 | 487 | |
5260325f | 488 | /* |
489 | * Change the comment of a private key file. | |
490 | */ | |
8efc0c15 | 491 | void |
492 | do_change_comment(struct passwd *pw) | |
493 | { | |
5260325f | 494 | char new_comment[1024], *comment; |
a306f2dd | 495 | Key *private; |
496 | Key *public; | |
5260325f | 497 | char *passphrase; |
498 | struct stat st; | |
499 | FILE *f; | |
5260325f | 500 | |
501 | if (!have_identity) | |
502 | ask_filename(pw, "Enter file in which the key is"); | |
5260325f | 503 | if (stat(identity_file, &st) < 0) { |
504 | perror(identity_file); | |
505 | exit(1); | |
506 | } | |
aa3378df | 507 | /* |
508 | * Try to load the public key from the file the verify that it is | |
509 | * readable and of the proper format. | |
510 | */ | |
a306f2dd | 511 | public = key_new(KEY_RSA); |
512 | if (!load_public_key(identity_file, public, NULL)) { | |
5260325f | 513 | printf("%s is not a valid key file.\n", identity_file); |
514 | exit(1); | |
8efc0c15 | 515 | } |
aa3378df | 516 | |
a306f2dd | 517 | private = key_new(KEY_RSA); |
518 | if (load_private_key(identity_file, "", private, &comment)) | |
5260325f | 519 | passphrase = xstrdup(""); |
520 | else { | |
5260325f | 521 | if (identity_passphrase) |
522 | passphrase = xstrdup(identity_passphrase); | |
523 | else if (identity_new_passphrase) | |
524 | passphrase = xstrdup(identity_new_passphrase); | |
525 | else | |
526 | passphrase = read_passphrase("Enter passphrase: ", 1); | |
527 | /* Try to load using the passphrase. */ | |
a306f2dd | 528 | if (!load_private_key(identity_file, passphrase, private, &comment)) { |
5260325f | 529 | memset(passphrase, 0, strlen(passphrase)); |
530 | xfree(passphrase); | |
531 | printf("Bad passphrase.\n"); | |
532 | exit(1); | |
533 | } | |
534 | } | |
535 | printf("Key now has comment '%s'\n", comment); | |
536 | ||
537 | if (identity_comment) { | |
538 | strlcpy(new_comment, identity_comment, sizeof(new_comment)); | |
539 | } else { | |
540 | printf("Enter new comment: "); | |
541 | fflush(stdout); | |
542 | if (!fgets(new_comment, sizeof(new_comment), stdin)) { | |
543 | memset(passphrase, 0, strlen(passphrase)); | |
a306f2dd | 544 | key_free(private); |
5260325f | 545 | exit(1); |
546 | } | |
5260325f | 547 | if (strchr(new_comment, '\n')) |
548 | *strchr(new_comment, '\n') = 0; | |
549 | } | |
550 | ||
551 | /* Save the file using the new passphrase. */ | |
a306f2dd | 552 | if (!save_private_key(identity_file, passphrase, private, new_comment)) { |
5260325f | 553 | printf("Saving the key failed: %s: %s.\n", |
554 | identity_file, strerror(errno)); | |
555 | memset(passphrase, 0, strlen(passphrase)); | |
556 | xfree(passphrase); | |
a306f2dd | 557 | key_free(private); |
5260325f | 558 | xfree(comment); |
559 | exit(1); | |
8efc0c15 | 560 | } |
5260325f | 561 | memset(passphrase, 0, strlen(passphrase)); |
562 | xfree(passphrase); | |
a306f2dd | 563 | key_free(private); |
5260325f | 564 | |
5260325f | 565 | strlcat(identity_file, ".pub", sizeof(identity_file)); |
566 | f = fopen(identity_file, "w"); | |
567 | if (!f) { | |
568 | printf("Could not save your public key in %s\n", identity_file); | |
569 | exit(1); | |
570 | } | |
a306f2dd | 571 | if (!key_write(public, f)) |
572 | fprintf(stderr, "write key failed"); | |
573 | key_free(public); | |
574 | fprintf(f, " %s\n", new_comment); | |
5260325f | 575 | fclose(f); |
576 | ||
577 | xfree(comment); | |
578 | ||
579 | printf("The comment in your key file has been changed.\n"); | |
580 | exit(0); | |
8efc0c15 | 581 | } |
582 | ||
5ad13cd7 | 583 | void |
584 | usage(void) | |
585 | { | |
1d1ffb87 | 586 | printf("Usage: %s [-lpqxXydc] [-b bits] [-f file] [-C comment] [-N new-pass] [-P pass]\n", __progname); |
5260325f | 587 | exit(1); |
5ad13cd7 | 588 | } |
589 | ||
5260325f | 590 | /* |
591 | * Main program for key management. | |
592 | */ | |
8efc0c15 | 593 | int |
594 | main(int ac, char **av) | |
595 | { | |
5260325f | 596 | char dotsshdir[16 * 1024], comment[1024], *passphrase1, *passphrase2; |
597 | struct passwd *pw; | |
5260325f | 598 | int opt; |
599 | struct stat st; | |
600 | FILE *f; | |
a306f2dd | 601 | Key *private; |
602 | Key *public; | |
5260325f | 603 | extern int optind; |
604 | extern char *optarg; | |
605 | ||
264dce47 | 606 | init_rng(); |
607 | ||
fa649821 | 608 | SSLeay_add_all_algorithms(); |
a306f2dd | 609 | |
aa3378df | 610 | /* we need this for the home * directory. */ |
5260325f | 611 | pw = getpwuid(getuid()); |
612 | if (!pw) { | |
613 | printf("You don't exist, go away!\n"); | |
614 | exit(1); | |
615 | } | |
a306f2dd | 616 | if (gethostname(hostname, sizeof(hostname)) < 0) { |
617 | perror("gethostname"); | |
618 | exit(1); | |
619 | } | |
aa3378df | 620 | |
a306f2dd | 621 | while ((opt = getopt(ac, av, "dqpclRxXyb:f:P:N:C:")) != EOF) { |
5260325f | 622 | switch (opt) { |
623 | case 'b': | |
624 | bits = atoi(optarg); | |
625 | if (bits < 512 || bits > 32768) { | |
626 | printf("Bits has bad value.\n"); | |
627 | exit(1); | |
628 | } | |
629 | break; | |
630 | ||
631 | case 'l': | |
632 | print_fingerprint = 1; | |
633 | break; | |
634 | ||
635 | case 'p': | |
636 | change_passphrase = 1; | |
637 | break; | |
638 | ||
639 | case 'c': | |
640 | change_comment = 1; | |
641 | break; | |
642 | ||
643 | case 'f': | |
644 | strlcpy(identity_file, optarg, sizeof(identity_file)); | |
645 | have_identity = 1; | |
646 | break; | |
647 | ||
648 | case 'P': | |
649 | identity_passphrase = optarg; | |
650 | break; | |
651 | ||
652 | case 'N': | |
653 | identity_new_passphrase = optarg; | |
654 | break; | |
655 | ||
656 | case 'C': | |
657 | identity_comment = optarg; | |
658 | break; | |
659 | ||
660 | case 'q': | |
661 | quiet = 1; | |
662 | break; | |
663 | ||
a306f2dd | 664 | case 'R': |
665 | if (rsa_alive() == 0) | |
666 | exit(1); | |
667 | else | |
668 | exit(0); | |
669 | break; | |
670 | ||
671 | case 'x': | |
672 | convert_to_ssh2 = 1; | |
673 | break; | |
674 | ||
675 | case 'X': | |
676 | convert_from_ssh2 = 1; | |
677 | break; | |
678 | ||
679 | case 'y': | |
680 | print_public = 1; | |
681 | break; | |
682 | ||
683 | case 'd': | |
684 | dsa_mode = 1; | |
685 | break; | |
686 | ||
5260325f | 687 | case '?': |
688 | default: | |
689 | usage(); | |
690 | } | |
691 | } | |
692 | if (optind < ac) { | |
693 | printf("Too many arguments.\n"); | |
694 | usage(); | |
695 | } | |
696 | if (change_passphrase && change_comment) { | |
697 | printf("Can only have one of -p and -c.\n"); | |
698 | usage(); | |
699 | } | |
a306f2dd | 700 | /* check if RSA support is needed and exists */ |
701 | if (dsa_mode == 0 && rsa_alive() == 0) { | |
702 | fprintf(stderr, | |
703 | "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", | |
704 | __progname); | |
705 | exit(1); | |
706 | } | |
5260325f | 707 | if (print_fingerprint) |
708 | do_fingerprint(pw); | |
5260325f | 709 | if (change_passphrase) |
710 | do_change_passphrase(pw); | |
5260325f | 711 | if (change_comment) |
712 | do_change_comment(pw); | |
a306f2dd | 713 | if (convert_to_ssh2) |
714 | do_convert_to_ssh2(pw); | |
715 | if (convert_from_ssh2) | |
716 | do_convert_from_ssh2(pw); | |
717 | if (print_public) | |
718 | do_print_public(pw); | |
5260325f | 719 | |
720 | arc4random_stir(); | |
721 | ||
a306f2dd | 722 | if (dsa_mode != 0) { |
723 | if (!quiet) | |
724 | printf("Generating DSA parameter and key.\n"); | |
725 | public = private = dsa_generate_key(bits); | |
726 | if (private == NULL) { | |
727 | fprintf(stderr, "dsa_generate_keys failed"); | |
728 | exit(1); | |
729 | } | |
730 | } else { | |
731 | if (quiet) | |
732 | rsa_set_verbose(0); | |
733 | /* Generate the rsa key pair. */ | |
734 | public = key_new(KEY_RSA); | |
735 | private = key_new(KEY_RSA); | |
736 | rsa_generate_key(private->rsa, public->rsa, bits); | |
737 | } | |
5260325f | 738 | |
739 | if (!have_identity) | |
740 | ask_filename(pw, "Enter file in which to save the key"); | |
741 | ||
742 | /* Create ~/.ssh directory if it doesn\'t already exist. */ | |
743 | snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, SSH_USER_DIR); | |
744 | if (strstr(identity_file, dotsshdir) != NULL && | |
745 | stat(dotsshdir, &st) < 0) { | |
704b1659 | 746 | if (mkdir(dotsshdir, 0700) < 0) |
5260325f | 747 | error("Could not create directory '%s'.", dotsshdir); |
748 | else if (!quiet) | |
749 | printf("Created directory '%s'.\n", dotsshdir); | |
750 | } | |
751 | /* If the file already exists, ask the user to confirm. */ | |
752 | if (stat(identity_file, &st) >= 0) { | |
753 | char yesno[3]; | |
754 | printf("%s already exists.\n", identity_file); | |
755 | printf("Overwrite (y/n)? "); | |
756 | fflush(stdout); | |
757 | if (fgets(yesno, sizeof(yesno), stdin) == NULL) | |
758 | exit(1); | |
759 | if (yesno[0] != 'y' && yesno[0] != 'Y') | |
760 | exit(1); | |
761 | } | |
762 | /* Ask for a passphrase (twice). */ | |
763 | if (identity_passphrase) | |
764 | passphrase1 = xstrdup(identity_passphrase); | |
765 | else if (identity_new_passphrase) | |
766 | passphrase1 = xstrdup(identity_new_passphrase); | |
767 | else { | |
768 | passphrase_again: | |
769 | passphrase1 = | |
770 | read_passphrase("Enter passphrase (empty for no passphrase): ", 1); | |
771 | passphrase2 = read_passphrase("Enter same passphrase again: ", 1); | |
772 | if (strcmp(passphrase1, passphrase2) != 0) { | |
773 | /* The passphrases do not match. Clear them and retry. */ | |
774 | memset(passphrase1, 0, strlen(passphrase1)); | |
775 | memset(passphrase2, 0, strlen(passphrase2)); | |
776 | xfree(passphrase1); | |
777 | xfree(passphrase2); | |
778 | printf("Passphrases do not match. Try again.\n"); | |
779 | goto passphrase_again; | |
780 | } | |
781 | /* Clear the other copy of the passphrase. */ | |
782 | memset(passphrase2, 0, strlen(passphrase2)); | |
783 | xfree(passphrase2); | |
784 | } | |
785 | ||
5260325f | 786 | if (identity_comment) { |
787 | strlcpy(comment, identity_comment, sizeof(comment)); | |
788 | } else { | |
6ae2364d | 789 | /* Create default commend field for the passphrase. */ |
5260325f | 790 | snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname); |
791 | } | |
792 | ||
793 | /* Save the key with the given passphrase and comment. */ | |
a306f2dd | 794 | if (!save_private_key(identity_file, passphrase1, private, comment)) { |
5260325f | 795 | printf("Saving the key failed: %s: %s.\n", |
a306f2dd | 796 | identity_file, strerror(errno)); |
5260325f | 797 | memset(passphrase1, 0, strlen(passphrase1)); |
798 | xfree(passphrase1); | |
799 | exit(1); | |
800 | } | |
801 | /* Clear the passphrase. */ | |
802 | memset(passphrase1, 0, strlen(passphrase1)); | |
803 | xfree(passphrase1); | |
804 | ||
805 | /* Clear the private key and the random number generator. */ | |
a306f2dd | 806 | if (private != public) { |
807 | key_free(private); | |
808 | } | |
5260325f | 809 | arc4random_stir(); |
810 | ||
811 | if (!quiet) | |
812 | printf("Your identification has been saved in %s.\n", identity_file); | |
813 | ||
5260325f | 814 | strlcat(identity_file, ".pub", sizeof(identity_file)); |
815 | f = fopen(identity_file, "w"); | |
816 | if (!f) { | |
817 | printf("Could not save your public key in %s\n", identity_file); | |
818 | exit(1); | |
819 | } | |
a306f2dd | 820 | if (!key_write(public, f)) |
821 | fprintf(stderr, "write key failed"); | |
822 | fprintf(f, " %s\n", comment); | |
5260325f | 823 | fclose(f); |
824 | ||
825 | if (!quiet) { | |
a306f2dd | 826 | printf("Your public key has been saved in %s.\n", |
827 | identity_file); | |
5260325f | 828 | printf("The key fingerprint is:\n"); |
a306f2dd | 829 | printf("%s %s\n", key_fingerprint(public), comment); |
8efc0c15 | 830 | } |
a306f2dd | 831 | |
832 | key_free(public); | |
5260325f | 833 | exit(0); |
8efc0c15 | 834 | } |