blob: 87be6a5f855e7ec3467949b9596356b83f3e3419 [file] [log] [blame]
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001/*
Damien Miller95def091999-11-25 00:26:21 +11002 * Author: Tatu Ylonen <ylo@cs.hut.fi>
3 * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
4 * All rights reserved
Damien Miller95def091999-11-25 00:26:21 +11005 * Identity and host key generation and maintenance.
Damien Millere4340be2000-09-16 13:29:08 +11006 *
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".
Damien Miller95def091999-11-25 00:26:21 +110012 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +100013
14#include "includes.h"
Ben Lindstrom2941f112000-12-29 16:50:13 +000015RCSID("$OpenBSD: ssh-keygen.c,v 1.38 2000/12/28 18:58:39 markus Exp $");
Damien Millerd4a8b7e1999-10-27 13:42:43 +100016
Damien Millereba71ba2000-04-29 23:57:08 +100017#include <openssl/evp.h>
18#include <openssl/pem.h>
19#include <openssl/rsa.h>
20#include <openssl/dsa.h>
21
Damien Millerd4a8b7e1999-10-27 13:42:43 +100022#include "ssh.h"
23#include "xmalloc.h"
Damien Millereba71ba2000-04-29 23:57:08 +100024#include "key.h"
25#include "rsa.h"
Damien Millereba71ba2000-04-29 23:57:08 +100026#include "authfile.h"
27#include "uuencode.h"
Damien Millerd4a8b7e1999-10-27 13:42:43 +100028
Damien Miller874d77b2000-10-14 16:23:11 +110029#include "buffer.h"
30#include "bufaux.h"
31
Damien Millereba71ba2000-04-29 23:57:08 +100032/* Number of bits in the RSA/DSA key. This value can be changed on the command line. */
Damien Millerd4a8b7e1999-10-27 13:42:43 +100033int bits = 1024;
34
Damien Miller5428f641999-11-25 11:54:57 +110035/*
36 * Flag indicating that we just want to change the passphrase. This can be
37 * set on the command line.
38 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +100039int change_passphrase = 0;
40
Damien Miller5428f641999-11-25 11:54:57 +110041/*
42 * Flag indicating that we just want to change the comment. This can be set
43 * on the command line.
44 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +100045int change_comment = 0;
46
47int quiet = 0;
48
Damien Miller10f6f6b1999-11-17 17:29:08 +110049/* Flag indicating that we just want to see the key fingerprint */
50int print_fingerprint = 0;
51
Damien Miller431f66b1999-11-21 18:31:57 +110052/* The identity file name, given on the command line or entered by the user. */
53char identity_file[1024];
54int have_identity = 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100055
56/* This is set to the passphrase if given on the command line. */
57char *identity_passphrase = NULL;
58
59/* This is set to the new passphrase if given on the command line. */
60char *identity_new_passphrase = NULL;
61
62/* This is set to the new comment if given on the command line. */
63char *identity_comment = NULL;
64
Damien Millereba71ba2000-04-29 23:57:08 +100065/* Dump public key file in format used by real and the original SSH 2 */
66int convert_to_ssh2 = 0;
67int convert_from_ssh2 = 0;
68int print_public = 0;
Damien Miller0bc1bd82000-11-13 22:57:25 +110069
Damien Millere39cacc2000-11-29 12:18:44 +110070/* default to RSA for SSH-1 */
71char *key_type_name = "rsa1";
Damien Millereba71ba2000-04-29 23:57:08 +100072
Damien Miller431f66b1999-11-21 18:31:57 +110073/* argv0 */
Damien Miller95def091999-11-25 00:26:21 +110074#ifdef HAVE___PROGNAME
Damien Miller431f66b1999-11-21 18:31:57 +110075extern char *__progname;
Ben Lindstrom49a79c02000-11-17 03:47:20 +000076#else
77char *__progname;
78#endif
Damien Millerd4a8b7e1999-10-27 13:42:43 +100079
Damien Millereba71ba2000-04-29 23:57:08 +100080char hostname[MAXHOSTNAMELEN];
81
Damien Miller431f66b1999-11-21 18:31:57 +110082void
83ask_filename(struct passwd *pw, const char *prompt)
Damien Millerd4a8b7e1999-10-27 13:42:43 +100084{
Damien Miller95def091999-11-25 00:26:21 +110085 char buf[1024];
Damien Millere39cacc2000-11-29 12:18:44 +110086 char *name = NULL;
87
88 switch (key_type_from_name(key_type_name)) {
89 case KEY_RSA1:
90 name = SSH_CLIENT_IDENTITY;
91 break;
92 case KEY_DSA:
93 name = SSH_CLIENT_ID_DSA;
94 break;
95 case KEY_RSA:
96 name = SSH_CLIENT_ID_RSA;
97 break;
98 default:
99 fprintf(stderr, "bad key type");
100 exit(1);
101 break;
102 }
103 snprintf(identity_file, sizeof(identity_file), "%s/%s", pw->pw_dir, name);
Ben Lindstrom3deda8b2000-12-22 20:27:43 +0000104 fprintf(stderr, "%s (%s): ", prompt, identity_file);
105 fflush(stderr);
Damien Miller95def091999-11-25 00:26:21 +1100106 if (fgets(buf, sizeof(buf), stdin) == NULL)
107 exit(1);
108 if (strchr(buf, '\n'))
109 *strchr(buf, '\n') = 0;
110 if (strcmp(buf, "") != 0)
111 strlcpy(identity_file, buf, sizeof(identity_file));
112 have_identity = 1;
Damien Miller10f6f6b1999-11-17 17:29:08 +1100113}
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000114
Damien Millereba71ba2000-04-29 23:57:08 +1000115int
116try_load_key(char *filename, Key *k)
117{
118 int success = 1;
119 if (!load_private_key(filename, "", k, NULL)) {
120 char *pass = read_passphrase("Enter passphrase: ", 1);
121 if (!load_private_key(filename, pass, k, NULL)) {
122 success = 0;
123 }
124 memset(pass, 0, strlen(pass));
125 xfree(pass);
126 }
127 return success;
128}
129
Damien Miller874d77b2000-10-14 16:23:11 +1100130#define SSH_COM_PUBLIC_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----"
131#define SSH_COM_PUBLIC_END "---- END SSH2 PUBLIC KEY ----"
132#define SSH_COM_PRIVATE_BEGIN "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----"
133#define SSH_COM_PRIVATE_KEY_MAGIC 0x3f6ff9eb
Damien Millereba71ba2000-04-29 23:57:08 +1000134
135void
136do_convert_to_ssh2(struct passwd *pw)
137{
138 Key *k;
139 int len;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000140 u_char *blob;
Damien Millereba71ba2000-04-29 23:57:08 +1000141 struct stat st;
142
143 if (!have_identity)
144 ask_filename(pw, "Enter file in which the key is");
145 if (stat(identity_file, &st) < 0) {
146 perror(identity_file);
147 exit(1);
148 }
Damien Miller0bc1bd82000-11-13 22:57:25 +1100149 k = key_new(KEY_UNSPEC);
Damien Millereba71ba2000-04-29 23:57:08 +1000150 if (!try_load_key(identity_file, k)) {
151 fprintf(stderr, "load failed\n");
152 exit(1);
153 }
Damien Miller0bc1bd82000-11-13 22:57:25 +1100154 key_to_blob(k, &blob, &len);
Damien Miller874d77b2000-10-14 16:23:11 +1100155 fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN);
Damien Millereba71ba2000-04-29 23:57:08 +1000156 fprintf(stdout,
Damien Miller874d77b2000-10-14 16:23:11 +1100157 "Comment: \"%d-bit %s, converted from OpenSSH by %s@%s\"\n",
158 key_size(k), key_type(k),
Damien Millereba71ba2000-04-29 23:57:08 +1000159 pw->pw_name, hostname);
160 dump_base64(stdout, blob, len);
Damien Miller874d77b2000-10-14 16:23:11 +1100161 fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END);
Damien Millereba71ba2000-04-29 23:57:08 +1000162 key_free(k);
163 xfree(blob);
164 exit(0);
165}
166
167void
Damien Miller874d77b2000-10-14 16:23:11 +1100168buffer_get_bignum_bits(Buffer *b, BIGNUM *value)
169{
170 int bits = buffer_get_int(b);
171 int bytes = (bits + 7) / 8;
172 if (buffer_len(b) < bytes)
173 fatal("buffer_get_bignum_bits: input buffer too small");
Ben Lindstrom46c16222000-12-22 01:43:59 +0000174 BN_bin2bn((u_char *)buffer_ptr(b), bytes, value);
Damien Miller874d77b2000-10-14 16:23:11 +1100175 buffer_consume(b, bytes);
176}
177
178Key *
179do_convert_private_ssh2_from_blob(char *blob, int blen)
180{
181 Buffer b;
182 DSA *dsa;
183 Key *key = NULL;
184 int ignore, magic, rlen;
185 char *type, *cipher;
186
187 buffer_init(&b);
188 buffer_append(&b, blob, blen);
189
190 magic = buffer_get_int(&b);
191 if (magic != SSH_COM_PRIVATE_KEY_MAGIC) {
192 error("bad magic 0x%x != 0x%x", magic, SSH_COM_PRIVATE_KEY_MAGIC);
193 buffer_free(&b);
194 return NULL;
195 }
196 ignore = buffer_get_int(&b);
197 type = buffer_get_string(&b, NULL);
198 cipher = buffer_get_string(&b, NULL);
199 ignore = buffer_get_int(&b);
200 ignore = buffer_get_int(&b);
201 ignore = buffer_get_int(&b);
202 xfree(type);
203
204 if (strcmp(cipher, "none") != 0) {
205 error("unsupported cipher %s", cipher);
206 xfree(cipher);
207 buffer_free(&b);
208 return NULL;
209 }
210 xfree(cipher);
211
212 key = key_new(KEY_DSA);
213 dsa = key->dsa;
214 dsa->priv_key = BN_new();
215 if (dsa->priv_key == NULL) {
216 error("alloc priv_key failed");
217 key_free(key);
218 return NULL;
219 }
220 buffer_get_bignum_bits(&b, dsa->p);
221 buffer_get_bignum_bits(&b, dsa->g);
222 buffer_get_bignum_bits(&b, dsa->q);
223 buffer_get_bignum_bits(&b, dsa->pub_key);
224 buffer_get_bignum_bits(&b, dsa->priv_key);
225 rlen = buffer_len(&b);
226 if(rlen != 0)
227 error("do_convert_private_ssh2_from_blob: remaining bytes in key blob %d", rlen);
228 buffer_free(&b);
229 return key;
230}
231
232void
Damien Millereba71ba2000-04-29 23:57:08 +1000233do_convert_from_ssh2(struct passwd *pw)
234{
235 Key *k;
236 int blen;
237 char line[1024], *p;
238 char blob[8096];
239 char encoded[8096];
240 struct stat st;
Damien Miller874d77b2000-10-14 16:23:11 +1100241 int escaped = 0, private = 0, ok;
Damien Millereba71ba2000-04-29 23:57:08 +1000242 FILE *fp;
243
244 if (!have_identity)
245 ask_filename(pw, "Enter file in which the key is");
246 if (stat(identity_file, &st) < 0) {
247 perror(identity_file);
248 exit(1);
249 }
250 fp = fopen(identity_file, "r");
251 if (fp == NULL) {
252 perror(identity_file);
253 exit(1);
254 }
255 encoded[0] = '\0';
256 while (fgets(line, sizeof(line), fp)) {
Damien Miller30c3d422000-05-09 11:02:59 +1000257 if (!(p = strchr(line, '\n'))) {
258 fprintf(stderr, "input line too long.\n");
259 exit(1);
260 }
261 if (p > line && p[-1] == '\\')
262 escaped++;
Damien Millereba71ba2000-04-29 23:57:08 +1000263 if (strncmp(line, "----", 4) == 0 ||
264 strstr(line, ": ") != NULL) {
Damien Miller874d77b2000-10-14 16:23:11 +1100265 if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL)
266 private = 1;
Damien Millereba71ba2000-04-29 23:57:08 +1000267 fprintf(stderr, "ignore: %s", line);
268 continue;
269 }
Damien Miller30c3d422000-05-09 11:02:59 +1000270 if (escaped) {
271 escaped--;
272 fprintf(stderr, "escaped: %s", line);
273 continue;
Damien Millereba71ba2000-04-29 23:57:08 +1000274 }
275 *p = '\0';
276 strlcat(encoded, line, sizeof(encoded));
277 }
Ben Lindstrom46c16222000-12-22 01:43:59 +0000278 blen = uudecode(encoded, (u_char *)blob, sizeof(blob));
Damien Millereba71ba2000-04-29 23:57:08 +1000279 if (blen < 0) {
280 fprintf(stderr, "uudecode failed.\n");
281 exit(1);
282 }
Damien Miller874d77b2000-10-14 16:23:11 +1100283 k = private ?
284 do_convert_private_ssh2_from_blob(blob, blen) :
Damien Miller0bc1bd82000-11-13 22:57:25 +1100285 key_from_blob(blob, blen);
Damien Miller874d77b2000-10-14 16:23:11 +1100286 if (k == NULL) {
287 fprintf(stderr, "decode blob failed.\n");
288 exit(1);
289 }
290 ok = private ?
291 PEM_write_DSAPrivateKey(stdout, k->dsa, NULL, NULL, 0, NULL, NULL) :
292 key_write(k, stdout);
293 if (!ok) {
294 fprintf(stderr, "key write failed");
295 exit(1);
296 }
Damien Millereba71ba2000-04-29 23:57:08 +1000297 key_free(k);
298 fprintf(stdout, "\n");
299 fclose(fp);
300 exit(0);
301}
302
303void
304do_print_public(struct passwd *pw)
305{
306 Key *k;
Damien Millereba71ba2000-04-29 23:57:08 +1000307 struct stat st;
308
309 if (!have_identity)
310 ask_filename(pw, "Enter file in which the key is");
311 if (stat(identity_file, &st) < 0) {
312 perror(identity_file);
313 exit(1);
314 }
Damien Miller0bc1bd82000-11-13 22:57:25 +1100315 k = key_new(KEY_UNSPEC);
Damien Millereba71ba2000-04-29 23:57:08 +1000316 if (!try_load_key(identity_file, k)) {
317 fprintf(stderr, "load failed\n");
318 exit(1);
319 }
Damien Millereba71ba2000-04-29 23:57:08 +1000320 if (!key_write(k, stdout))
321 fprintf(stderr, "key_write failed");
322 key_free(k);
Damien Millereba71ba2000-04-29 23:57:08 +1000323 fprintf(stdout, "\n");
324 exit(0);
325}
326
Damien Miller10f6f6b1999-11-17 17:29:08 +1100327void
328do_fingerprint(struct passwd *pw)
329{
Damien Millerad833b32000-08-23 10:46:23 +1000330
Damien Miller98c7ad62000-03-09 21:27:49 +1100331 FILE *f;
Damien Millereba71ba2000-04-29 23:57:08 +1000332 Key *public;
Damien Miller98c7ad62000-03-09 21:27:49 +1100333 char *comment = NULL, *cp, *ep, line[16*1024];
Damien Miller0bc1bd82000-11-13 22:57:25 +1100334 int i, skip = 0, num = 1, invalid = 1, success = 0;
Damien Miller95def091999-11-25 00:26:21 +1100335 struct stat st;
Damien Miller10f6f6b1999-11-17 17:29:08 +1100336
Damien Miller95def091999-11-25 00:26:21 +1100337 if (!have_identity)
338 ask_filename(pw, "Enter file in which the key is");
339 if (stat(identity_file, &st) < 0) {
340 perror(identity_file);
341 exit(1);
342 }
Damien Miller0bc1bd82000-11-13 22:57:25 +1100343 public = key_new(KEY_RSA1);
Damien Millereba71ba2000-04-29 23:57:08 +1000344 if (load_public_key(identity_file, public, &comment)) {
Damien Miller0bc1bd82000-11-13 22:57:25 +1100345 success = 1;
346 } else {
Damien Millereba71ba2000-04-29 23:57:08 +1000347 key_free(public);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100348 public = key_new(KEY_UNSPEC);
349 if (try_load_public_key(identity_file, public, &comment))
350 success = 1;
351 else
Ben Lindstrom2941f112000-12-29 16:50:13 +0000352 debug("try_load_public_key KEY_UNSPEC failed");
Damien Miller0bc1bd82000-11-13 22:57:25 +1100353 }
354 if (success) {
355 printf("%d %s %s\n", key_size(public), key_fingerprint(public), comment);
356 key_free(public);
357 xfree(comment);
Damien Miller98c7ad62000-03-09 21:27:49 +1100358 exit(0);
359 }
Damien Miller98c7ad62000-03-09 21:27:49 +1100360
361 f = fopen(identity_file, "r");
362 if (f != NULL) {
Damien Miller98c7ad62000-03-09 21:27:49 +1100363 while (fgets(line, sizeof(line), f)) {
364 i = strlen(line) - 1;
365 if (line[i] != '\n') {
366 error("line %d too long: %.40s...", num, line);
367 skip = 1;
368 continue;
Damien Miller95def091999-11-25 00:26:21 +1100369 }
Damien Miller98c7ad62000-03-09 21:27:49 +1100370 num++;
371 if (skip) {
372 skip = 0;
373 continue;
374 }
375 line[i] = '\0';
376
377 /* Skip leading whitespace, empty and comment lines. */
378 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
379 ;
380 if (!*cp || *cp == '\n' || *cp == '#')
381 continue ;
382 i = strtol(cp, &ep, 10);
383 if (i == 0 || ep == NULL || (*ep != ' ' && *ep != '\t')) {
384 int quoted = 0;
385 comment = cp;
386 for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
387 if (*cp == '\\' && cp[1] == '"')
388 cp++; /* Skip both */
389 else if (*cp == '"')
390 quoted = !quoted;
391 }
392 if (!*cp)
393 continue;
394 *cp++ = '\0';
395 }
396 ep = cp;
Ben Lindstrom2941f112000-12-29 16:50:13 +0000397 public = key_new(KEY_RSA1);
398 if (key_read(public, &cp) != 1) {
399 cp = ep;
400 key_free(public);
401 public = key_new(KEY_UNSPEC);
402 if (key_read(public, &cp) != 1) {
403 key_free(public);
404 continue;
405 }
Damien Miller98c7ad62000-03-09 21:27:49 +1100406 }
Ben Lindstrom2941f112000-12-29 16:50:13 +0000407 comment = *cp ? cp : comment;
408 printf("%d %s %s\n", key_size(public),
409 key_fingerprint(public),
410 comment ? comment : "no comment");
411 invalid = 0;
Damien Miller95def091999-11-25 00:26:21 +1100412 }
Damien Miller98c7ad62000-03-09 21:27:49 +1100413 fclose(f);
Damien Miller95def091999-11-25 00:26:21 +1100414 }
Damien Millerad833b32000-08-23 10:46:23 +1000415 key_free(public);
Damien Miller98c7ad62000-03-09 21:27:49 +1100416 if (invalid) {
417 printf("%s is not a valid key file.\n", identity_file);
418 exit(1);
419 }
Damien Miller95def091999-11-25 00:26:21 +1100420 exit(0);
Damien Miller10f6f6b1999-11-17 17:29:08 +1100421}
422
Damien Miller95def091999-11-25 00:26:21 +1100423/*
424 * Perform changing a passphrase. The argument is the passwd structure
425 * for the current user.
426 */
Damien Miller10f6f6b1999-11-17 17:29:08 +1100427void
428do_change_passphrase(struct passwd *pw)
429{
Damien Miller95def091999-11-25 00:26:21 +1100430 char *comment;
431 char *old_passphrase, *passphrase1, *passphrase2;
432 struct stat st;
Damien Millereba71ba2000-04-29 23:57:08 +1000433 Key *private;
434 Key *public;
Damien Miller0bc1bd82000-11-13 22:57:25 +1100435 int type = KEY_RSA1;
Damien Miller10f6f6b1999-11-17 17:29:08 +1100436
Damien Miller95def091999-11-25 00:26:21 +1100437 if (!have_identity)
438 ask_filename(pw, "Enter file in which the key is");
Damien Miller95def091999-11-25 00:26:21 +1100439 if (stat(identity_file, &st) < 0) {
440 perror(identity_file);
441 exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000442 }
Damien Miller0bc1bd82000-11-13 22:57:25 +1100443 public = key_new(type);
444 if (!load_public_key(identity_file, public, NULL)) {
445 type = KEY_UNSPEC;
446 } else {
Damien Millereba71ba2000-04-29 23:57:08 +1000447 /* Clear the public key since we are just about to load the whole file. */
448 key_free(public);
Damien Miller95def091999-11-25 00:26:21 +1100449 }
Damien Miller95def091999-11-25 00:26:21 +1100450 /* Try to load the file with empty passphrase. */
Damien Millereba71ba2000-04-29 23:57:08 +1000451 private = key_new(type);
452 if (!load_private_key(identity_file, "", private, &comment)) {
Damien Miller95def091999-11-25 00:26:21 +1100453 if (identity_passphrase)
454 old_passphrase = xstrdup(identity_passphrase);
455 else
456 old_passphrase = read_passphrase("Enter old passphrase: ", 1);
Damien Millereba71ba2000-04-29 23:57:08 +1000457 if (!load_private_key(identity_file, old_passphrase, private, &comment)) {
Damien Miller95def091999-11-25 00:26:21 +1100458 memset(old_passphrase, 0, strlen(old_passphrase));
459 xfree(old_passphrase);
460 printf("Bad passphrase.\n");
461 exit(1);
462 }
Damien Miller95def091999-11-25 00:26:21 +1100463 memset(old_passphrase, 0, strlen(old_passphrase));
464 xfree(old_passphrase);
465 }
466 printf("Key has comment '%s'\n", comment);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000467
Damien Miller95def091999-11-25 00:26:21 +1100468 /* Ask the new passphrase (twice). */
469 if (identity_new_passphrase) {
470 passphrase1 = xstrdup(identity_new_passphrase);
471 passphrase2 = NULL;
472 } else {
473 passphrase1 =
474 read_passphrase("Enter new passphrase (empty for no passphrase): ", 1);
475 passphrase2 = read_passphrase("Enter same passphrase again: ", 1);
476
477 /* Verify that they are the same. */
478 if (strcmp(passphrase1, passphrase2) != 0) {
479 memset(passphrase1, 0, strlen(passphrase1));
480 memset(passphrase2, 0, strlen(passphrase2));
481 xfree(passphrase1);
482 xfree(passphrase2);
483 printf("Pass phrases do not match. Try again.\n");
484 exit(1);
485 }
486 /* Destroy the other copy. */
487 memset(passphrase2, 0, strlen(passphrase2));
488 xfree(passphrase2);
489 }
490
491 /* Save the file using the new passphrase. */
Damien Millereba71ba2000-04-29 23:57:08 +1000492 if (!save_private_key(identity_file, passphrase1, private, comment)) {
Damien Miller95def091999-11-25 00:26:21 +1100493 printf("Saving the key failed: %s: %s.\n",
494 identity_file, strerror(errno));
495 memset(passphrase1, 0, strlen(passphrase1));
496 xfree(passphrase1);
Damien Millereba71ba2000-04-29 23:57:08 +1000497 key_free(private);
Damien Miller95def091999-11-25 00:26:21 +1100498 xfree(comment);
499 exit(1);
500 }
501 /* Destroy the passphrase and the copy of the key in memory. */
502 memset(passphrase1, 0, strlen(passphrase1));
503 xfree(passphrase1);
Damien Millereba71ba2000-04-29 23:57:08 +1000504 key_free(private); /* Destroys contents */
Damien Miller95def091999-11-25 00:26:21 +1100505 xfree(comment);
506
507 printf("Your identification has been saved with the new passphrase.\n");
508 exit(0);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000509}
510
Damien Miller95def091999-11-25 00:26:21 +1100511/*
512 * Change the comment of a private key file.
513 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000514void
515do_change_comment(struct passwd *pw)
516{
Damien Miller95def091999-11-25 00:26:21 +1100517 char new_comment[1024], *comment;
Damien Millereba71ba2000-04-29 23:57:08 +1000518 Key *private;
519 Key *public;
Damien Miller95def091999-11-25 00:26:21 +1100520 char *passphrase;
521 struct stat st;
522 FILE *f;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000523
Damien Miller95def091999-11-25 00:26:21 +1100524 if (!have_identity)
525 ask_filename(pw, "Enter file in which the key is");
Damien Miller95def091999-11-25 00:26:21 +1100526 if (stat(identity_file, &st) < 0) {
527 perror(identity_file);
528 exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000529 }
Damien Miller5428f641999-11-25 11:54:57 +1100530 /*
531 * Try to load the public key from the file the verify that it is
532 * readable and of the proper format.
533 */
Damien Miller0bc1bd82000-11-13 22:57:25 +1100534 public = key_new(KEY_RSA1);
Damien Millereba71ba2000-04-29 23:57:08 +1000535 if (!load_public_key(identity_file, public, NULL)) {
Damien Miller95def091999-11-25 00:26:21 +1100536 printf("%s is not a valid key file.\n", identity_file);
537 exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000538 }
Damien Miller5428f641999-11-25 11:54:57 +1100539
Damien Miller0bc1bd82000-11-13 22:57:25 +1100540 private = key_new(KEY_RSA1);
Damien Millereba71ba2000-04-29 23:57:08 +1000541 if (load_private_key(identity_file, "", private, &comment))
Damien Miller95def091999-11-25 00:26:21 +1100542 passphrase = xstrdup("");
543 else {
Damien Miller95def091999-11-25 00:26:21 +1100544 if (identity_passphrase)
545 passphrase = xstrdup(identity_passphrase);
546 else if (identity_new_passphrase)
547 passphrase = xstrdup(identity_new_passphrase);
548 else
549 passphrase = read_passphrase("Enter passphrase: ", 1);
550 /* Try to load using the passphrase. */
Damien Millereba71ba2000-04-29 23:57:08 +1000551 if (!load_private_key(identity_file, passphrase, private, &comment)) {
Damien Miller95def091999-11-25 00:26:21 +1100552 memset(passphrase, 0, strlen(passphrase));
553 xfree(passphrase);
554 printf("Bad passphrase.\n");
555 exit(1);
556 }
557 }
558 printf("Key now has comment '%s'\n", comment);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000559
Damien Miller95def091999-11-25 00:26:21 +1100560 if (identity_comment) {
561 strlcpy(new_comment, identity_comment, sizeof(new_comment));
562 } else {
563 printf("Enter new comment: ");
564 fflush(stdout);
565 if (!fgets(new_comment, sizeof(new_comment), stdin)) {
566 memset(passphrase, 0, strlen(passphrase));
Damien Millereba71ba2000-04-29 23:57:08 +1000567 key_free(private);
Damien Miller95def091999-11-25 00:26:21 +1100568 exit(1);
569 }
Damien Miller95def091999-11-25 00:26:21 +1100570 if (strchr(new_comment, '\n'))
571 *strchr(new_comment, '\n') = 0;
572 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000573
Damien Miller95def091999-11-25 00:26:21 +1100574 /* Save the file using the new passphrase. */
Damien Millereba71ba2000-04-29 23:57:08 +1000575 if (!save_private_key(identity_file, passphrase, private, new_comment)) {
Damien Miller95def091999-11-25 00:26:21 +1100576 printf("Saving the key failed: %s: %s.\n",
577 identity_file, strerror(errno));
578 memset(passphrase, 0, strlen(passphrase));
579 xfree(passphrase);
Damien Millereba71ba2000-04-29 23:57:08 +1000580 key_free(private);
Damien Miller95def091999-11-25 00:26:21 +1100581 xfree(comment);
582 exit(1);
583 }
Damien Miller95def091999-11-25 00:26:21 +1100584 memset(passphrase, 0, strlen(passphrase));
585 xfree(passphrase);
Damien Millereba71ba2000-04-29 23:57:08 +1000586 key_free(private);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000587
Damien Miller95def091999-11-25 00:26:21 +1100588 strlcat(identity_file, ".pub", sizeof(identity_file));
589 f = fopen(identity_file, "w");
590 if (!f) {
591 printf("Could not save your public key in %s\n", identity_file);
592 exit(1);
593 }
Damien Millereba71ba2000-04-29 23:57:08 +1000594 if (!key_write(public, f))
595 fprintf(stderr, "write key failed");
596 key_free(public);
597 fprintf(f, " %s\n", new_comment);
Damien Miller95def091999-11-25 00:26:21 +1100598 fclose(f);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000599
Damien Miller95def091999-11-25 00:26:21 +1100600 xfree(comment);
601
602 printf("The comment in your key file has been changed.\n");
603 exit(0);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000604}
605
Damien Miller431f66b1999-11-21 18:31:57 +1100606void
607usage(void)
608{
Damien Miller0bc1bd82000-11-13 22:57:25 +1100609 printf("Usage: %s [-lpqxXyc] [-t type] [-b bits] [-f file] [-C comment] [-N new-pass] [-P pass]\n", __progname);
Damien Miller95def091999-11-25 00:26:21 +1100610 exit(1);
Damien Miller431f66b1999-11-21 18:31:57 +1100611}
612
Damien Miller95def091999-11-25 00:26:21 +1100613/*
614 * Main program for key management.
615 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000616int
617main(int ac, char **av)
618{
Damien Miller95def091999-11-25 00:26:21 +1100619 char dotsshdir[16 * 1024], comment[1024], *passphrase1, *passphrase2;
620 struct passwd *pw;
Damien Millere39cacc2000-11-29 12:18:44 +1100621 int opt, type;
Damien Miller95def091999-11-25 00:26:21 +1100622 struct stat st;
623 FILE *f;
Damien Millereba71ba2000-04-29 23:57:08 +1000624 Key *private;
625 Key *public;
Damien Miller0bc1bd82000-11-13 22:57:25 +1100626
Damien Miller95def091999-11-25 00:26:21 +1100627 extern int optind;
628 extern char *optarg;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000629
Ben Lindstrom49a79c02000-11-17 03:47:20 +0000630 __progname = get_progname(av[0]);
Damien Millerf9b625c2000-07-09 22:42:32 +1000631 init_rng();
632
Damien Millerd3a18572000-06-07 19:55:44 +1000633 SSLeay_add_all_algorithms();
Damien Millereba71ba2000-04-29 23:57:08 +1000634
Damien Miller5428f641999-11-25 11:54:57 +1100635 /* we need this for the home * directory. */
Damien Miller95def091999-11-25 00:26:21 +1100636 pw = getpwuid(getuid());
637 if (!pw) {
638 printf("You don't exist, go away!\n");
639 exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000640 }
Damien Millereba71ba2000-04-29 23:57:08 +1000641 if (gethostname(hostname, sizeof(hostname)) < 0) {
642 perror("gethostname");
643 exit(1);
644 }
Damien Miller5428f641999-11-25 11:54:57 +1100645
Damien Miller0bc1bd82000-11-13 22:57:25 +1100646 while ((opt = getopt(ac, av, "dqpclRxXyb:f:t:P:N:C:")) != EOF) {
Damien Miller95def091999-11-25 00:26:21 +1100647 switch (opt) {
648 case 'b':
649 bits = atoi(optarg);
650 if (bits < 512 || bits > 32768) {
651 printf("Bits has bad value.\n");
652 exit(1);
653 }
654 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000655
Damien Miller95def091999-11-25 00:26:21 +1100656 case 'l':
657 print_fingerprint = 1;
658 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000659
Damien Miller95def091999-11-25 00:26:21 +1100660 case 'p':
661 change_passphrase = 1;
662 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000663
Damien Miller95def091999-11-25 00:26:21 +1100664 case 'c':
665 change_comment = 1;
666 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000667
Damien Miller95def091999-11-25 00:26:21 +1100668 case 'f':
669 strlcpy(identity_file, optarg, sizeof(identity_file));
670 have_identity = 1;
671 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000672
Damien Miller95def091999-11-25 00:26:21 +1100673 case 'P':
674 identity_passphrase = optarg;
675 break;
676
677 case 'N':
678 identity_new_passphrase = optarg;
679 break;
680
681 case 'C':
682 identity_comment = optarg;
683 break;
684
685 case 'q':
686 quiet = 1;
687 break;
688
Damien Millereba71ba2000-04-29 23:57:08 +1000689 case 'R':
Damien Miller0bc1bd82000-11-13 22:57:25 +1100690 /* unused */
691 exit(0);
Damien Millereba71ba2000-04-29 23:57:08 +1000692 break;
693
694 case 'x':
695 convert_to_ssh2 = 1;
696 break;
697
698 case 'X':
699 convert_from_ssh2 = 1;
700 break;
701
702 case 'y':
703 print_public = 1;
704 break;
705
706 case 'd':
Damien Miller0bc1bd82000-11-13 22:57:25 +1100707 key_type_name = "dsa";
Damien Millereba71ba2000-04-29 23:57:08 +1000708 break;
709
Damien Miller0bc1bd82000-11-13 22:57:25 +1100710 case 't':
711 key_type_name = optarg;
Damien Miller0bc1bd82000-11-13 22:57:25 +1100712 break;
713
Damien Miller95def091999-11-25 00:26:21 +1100714 case '?':
715 default:
716 usage();
717 }
718 }
719 if (optind < ac) {
720 printf("Too many arguments.\n");
721 usage();
722 }
723 if (change_passphrase && change_comment) {
724 printf("Can only have one of -p and -c.\n");
725 usage();
726 }
727 if (print_fingerprint)
728 do_fingerprint(pw);
Damien Miller95def091999-11-25 00:26:21 +1100729 if (change_passphrase)
730 do_change_passphrase(pw);
Damien Miller95def091999-11-25 00:26:21 +1100731 if (change_comment)
732 do_change_comment(pw);
Damien Millereba71ba2000-04-29 23:57:08 +1000733 if (convert_to_ssh2)
734 do_convert_to_ssh2(pw);
735 if (convert_from_ssh2)
736 do_convert_from_ssh2(pw);
737 if (print_public)
738 do_print_public(pw);
Damien Miller95def091999-11-25 00:26:21 +1100739
740 arc4random_stir();
741
Damien Millere39cacc2000-11-29 12:18:44 +1100742 type = key_type_from_name(key_type_name);
743 if (type == KEY_UNSPEC) {
744 fprintf(stderr, "unknown key type %s\n", key_type_name);
745 exit(1);
Damien Millereba71ba2000-04-29 23:57:08 +1000746 }
Damien Miller0bc1bd82000-11-13 22:57:25 +1100747 if (!quiet)
Damien Millere39cacc2000-11-29 12:18:44 +1100748 printf("Generating public/private %s key pair.\n", key_type_name);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100749 private = key_generate(type, bits);
750 if (private == NULL) {
751 fprintf(stderr, "key_generate failed");
752 exit(1);
753 }
754 public = key_from_private(private);
Damien Miller95def091999-11-25 00:26:21 +1100755
756 if (!have_identity)
757 ask_filename(pw, "Enter file in which to save the key");
758
759 /* Create ~/.ssh directory if it doesn\'t already exist. */
760 snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, SSH_USER_DIR);
761 if (strstr(identity_file, dotsshdir) != NULL &&
762 stat(dotsshdir, &st) < 0) {
Damien Millerbe484b52000-07-15 14:14:16 +1000763 if (mkdir(dotsshdir, 0700) < 0)
Damien Miller95def091999-11-25 00:26:21 +1100764 error("Could not create directory '%s'.", dotsshdir);
765 else if (!quiet)
766 printf("Created directory '%s'.\n", dotsshdir);
767 }
768 /* If the file already exists, ask the user to confirm. */
769 if (stat(identity_file, &st) >= 0) {
770 char yesno[3];
771 printf("%s already exists.\n", identity_file);
772 printf("Overwrite (y/n)? ");
773 fflush(stdout);
774 if (fgets(yesno, sizeof(yesno), stdin) == NULL)
775 exit(1);
776 if (yesno[0] != 'y' && yesno[0] != 'Y')
777 exit(1);
778 }
779 /* Ask for a passphrase (twice). */
780 if (identity_passphrase)
781 passphrase1 = xstrdup(identity_passphrase);
782 else if (identity_new_passphrase)
783 passphrase1 = xstrdup(identity_new_passphrase);
784 else {
785passphrase_again:
786 passphrase1 =
787 read_passphrase("Enter passphrase (empty for no passphrase): ", 1);
788 passphrase2 = read_passphrase("Enter same passphrase again: ", 1);
789 if (strcmp(passphrase1, passphrase2) != 0) {
790 /* The passphrases do not match. Clear them and retry. */
791 memset(passphrase1, 0, strlen(passphrase1));
792 memset(passphrase2, 0, strlen(passphrase2));
793 xfree(passphrase1);
794 xfree(passphrase2);
795 printf("Passphrases do not match. Try again.\n");
796 goto passphrase_again;
797 }
798 /* Clear the other copy of the passphrase. */
799 memset(passphrase2, 0, strlen(passphrase2));
800 xfree(passphrase2);
801 }
802
Damien Miller95def091999-11-25 00:26:21 +1100803 if (identity_comment) {
804 strlcpy(comment, identity_comment, sizeof(comment));
805 } else {
Damien Miller4af51302000-04-16 11:18:38 +1000806 /* Create default commend field for the passphrase. */
Damien Miller95def091999-11-25 00:26:21 +1100807 snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname);
808 }
809
810 /* Save the key with the given passphrase and comment. */
Damien Millereba71ba2000-04-29 23:57:08 +1000811 if (!save_private_key(identity_file, passphrase1, private, comment)) {
Damien Miller95def091999-11-25 00:26:21 +1100812 printf("Saving the key failed: %s: %s.\n",
Damien Millereba71ba2000-04-29 23:57:08 +1000813 identity_file, strerror(errno));
Damien Miller95def091999-11-25 00:26:21 +1100814 memset(passphrase1, 0, strlen(passphrase1));
815 xfree(passphrase1);
816 exit(1);
817 }
818 /* Clear the passphrase. */
819 memset(passphrase1, 0, strlen(passphrase1));
820 xfree(passphrase1);
821
822 /* Clear the private key and the random number generator. */
Damien Miller0bc1bd82000-11-13 22:57:25 +1100823 key_free(private);
Damien Miller95def091999-11-25 00:26:21 +1100824 arc4random_stir();
825
826 if (!quiet)
827 printf("Your identification has been saved in %s.\n", identity_file);
828
Damien Miller95def091999-11-25 00:26:21 +1100829 strlcat(identity_file, ".pub", sizeof(identity_file));
830 f = fopen(identity_file, "w");
831 if (!f) {
832 printf("Could not save your public key in %s\n", identity_file);
833 exit(1);
834 }
Damien Millereba71ba2000-04-29 23:57:08 +1000835 if (!key_write(public, f))
836 fprintf(stderr, "write key failed");
837 fprintf(f, " %s\n", comment);
Damien Miller95def091999-11-25 00:26:21 +1100838 fclose(f);
839
840 if (!quiet) {
Damien Millereba71ba2000-04-29 23:57:08 +1000841 printf("Your public key has been saved in %s.\n",
842 identity_file);
Damien Miller95def091999-11-25 00:26:21 +1100843 printf("The key fingerprint is:\n");
Damien Millereba71ba2000-04-29 23:57:08 +1000844 printf("%s %s\n", key_fingerprint(public), comment);
Damien Miller95def091999-11-25 00:26:21 +1100845 }
Damien Millereba71ba2000-04-29 23:57:08 +1000846
847 key_free(public);
Damien Miller95def091999-11-25 00:26:21 +1100848 exit(0);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000849}