blob: dbb46ac90453f5dace6bec65348d85e839e947d6 [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 Lindstrom5fc62702001-03-09 18:19:24 +000015RCSID("$OpenBSD: ssh-keygen.c,v 1.46 2001/03/09 03:14:39 deraadt 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>
Damien Millereba71ba2000-04-29 23:57:08 +100019
Damien Millerd4a8b7e1999-10-27 13:42:43 +100020#include "xmalloc.h"
Damien Millereba71ba2000-04-29 23:57:08 +100021#include "key.h"
Damien Millereba71ba2000-04-29 23:57:08 +100022#include "authfile.h"
23#include "uuencode.h"
Damien Miller874d77b2000-10-14 16:23:11 +110024#include "buffer.h"
25#include "bufaux.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000026#include "pathnames.h"
27#include "log.h"
28#include "readpass.h"
Damien Miller874d77b2000-10-14 16:23:11 +110029
Damien Millereba71ba2000-04-29 23:57:08 +100030/* Number of bits in the RSA/DSA key. This value can be changed on the command line. */
Damien Millerd4a8b7e1999-10-27 13:42:43 +100031int bits = 1024;
32
Damien Miller5428f641999-11-25 11:54:57 +110033/*
34 * Flag indicating that we just want to change the passphrase. This can be
35 * set on the command line.
36 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +100037int change_passphrase = 0;
38
Damien Miller5428f641999-11-25 11:54:57 +110039/*
40 * Flag indicating that we just want to change the comment. This can be set
41 * on the command line.
42 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +100043int change_comment = 0;
44
45int quiet = 0;
46
Damien Miller10f6f6b1999-11-17 17:29:08 +110047/* Flag indicating that we just want to see the key fingerprint */
48int print_fingerprint = 0;
49
Damien Miller431f66b1999-11-21 18:31:57 +110050/* The identity file name, given on the command line or entered by the user. */
51char identity_file[1024];
52int have_identity = 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100053
54/* This is set to the passphrase if given on the command line. */
55char *identity_passphrase = NULL;
56
57/* This is set to the new passphrase if given on the command line. */
58char *identity_new_passphrase = NULL;
59
60/* This is set to the new comment if given on the command line. */
61char *identity_comment = NULL;
62
Damien Millereba71ba2000-04-29 23:57:08 +100063/* Dump public key file in format used by real and the original SSH 2 */
64int convert_to_ssh2 = 0;
65int convert_from_ssh2 = 0;
66int print_public = 0;
Damien Miller0bc1bd82000-11-13 22:57:25 +110067
Damien Millere39cacc2000-11-29 12:18:44 +110068/* default to RSA for SSH-1 */
69char *key_type_name = "rsa1";
Damien Millereba71ba2000-04-29 23:57:08 +100070
Damien Miller431f66b1999-11-21 18:31:57 +110071/* argv0 */
Damien Miller95def091999-11-25 00:26:21 +110072#ifdef HAVE___PROGNAME
Damien Miller431f66b1999-11-21 18:31:57 +110073extern char *__progname;
Ben Lindstrom49a79c02000-11-17 03:47:20 +000074#else
75char *__progname;
76#endif
Damien Millerd4a8b7e1999-10-27 13:42:43 +100077
Damien Millereba71ba2000-04-29 23:57:08 +100078char hostname[MAXHOSTNAMELEN];
79
Damien Miller431f66b1999-11-21 18:31:57 +110080void
81ask_filename(struct passwd *pw, const char *prompt)
Damien Millerd4a8b7e1999-10-27 13:42:43 +100082{
Damien Miller95def091999-11-25 00:26:21 +110083 char buf[1024];
Damien Millere39cacc2000-11-29 12:18:44 +110084 char *name = NULL;
85
86 switch (key_type_from_name(key_type_name)) {
87 case KEY_RSA1:
Ben Lindstrom226cfa02001-01-22 05:34:40 +000088 name = _PATH_SSH_CLIENT_IDENTITY;
Damien Millere39cacc2000-11-29 12:18:44 +110089 break;
90 case KEY_DSA:
Ben Lindstrom226cfa02001-01-22 05:34:40 +000091 name = _PATH_SSH_CLIENT_ID_DSA;
Damien Millere39cacc2000-11-29 12:18:44 +110092 break;
93 case KEY_RSA:
Ben Lindstrom226cfa02001-01-22 05:34:40 +000094 name = _PATH_SSH_CLIENT_ID_RSA;
Damien Millere39cacc2000-11-29 12:18:44 +110095 break;
96 default:
97 fprintf(stderr, "bad key type");
98 exit(1);
99 break;
100 }
101 snprintf(identity_file, sizeof(identity_file), "%s/%s", pw->pw_dir, name);
Ben Lindstrom3deda8b2000-12-22 20:27:43 +0000102 fprintf(stderr, "%s (%s): ", prompt, identity_file);
103 fflush(stderr);
Damien Miller95def091999-11-25 00:26:21 +1100104 if (fgets(buf, sizeof(buf), stdin) == NULL)
105 exit(1);
106 if (strchr(buf, '\n'))
107 *strchr(buf, '\n') = 0;
108 if (strcmp(buf, "") != 0)
109 strlcpy(identity_file, buf, sizeof(identity_file));
110 have_identity = 1;
Damien Miller10f6f6b1999-11-17 17:29:08 +1100111}
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000112
Damien Millereba71ba2000-04-29 23:57:08 +1000113int
114try_load_key(char *filename, Key *k)
115{
116 int success = 1;
117 if (!load_private_key(filename, "", k, NULL)) {
118 char *pass = read_passphrase("Enter passphrase: ", 1);
119 if (!load_private_key(filename, pass, k, NULL)) {
120 success = 0;
121 }
122 memset(pass, 0, strlen(pass));
123 xfree(pass);
124 }
125 return success;
126}
127
Damien Miller874d77b2000-10-14 16:23:11 +1100128#define SSH_COM_PUBLIC_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----"
129#define SSH_COM_PUBLIC_END "---- END SSH2 PUBLIC KEY ----"
130#define SSH_COM_PRIVATE_BEGIN "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----"
Kevin Stevesef4eea92001-02-05 12:42:17 +0000131#define SSH_COM_PRIVATE_KEY_MAGIC 0x3f6ff9eb
Damien Millereba71ba2000-04-29 23:57:08 +1000132
133void
134do_convert_to_ssh2(struct passwd *pw)
135{
136 Key *k;
137 int len;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000138 u_char *blob;
Damien Millereba71ba2000-04-29 23:57:08 +1000139 struct stat st;
140
141 if (!have_identity)
142 ask_filename(pw, "Enter file in which the key is");
143 if (stat(identity_file, &st) < 0) {
144 perror(identity_file);
145 exit(1);
146 }
Damien Miller0bc1bd82000-11-13 22:57:25 +1100147 k = key_new(KEY_UNSPEC);
Damien Millereba71ba2000-04-29 23:57:08 +1000148 if (!try_load_key(identity_file, k)) {
149 fprintf(stderr, "load failed\n");
150 exit(1);
151 }
Damien Miller0bc1bd82000-11-13 22:57:25 +1100152 key_to_blob(k, &blob, &len);
Damien Miller874d77b2000-10-14 16:23:11 +1100153 fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN);
Damien Millereba71ba2000-04-29 23:57:08 +1000154 fprintf(stdout,
Damien Miller874d77b2000-10-14 16:23:11 +1100155 "Comment: \"%d-bit %s, converted from OpenSSH by %s@%s\"\n",
156 key_size(k), key_type(k),
Damien Millereba71ba2000-04-29 23:57:08 +1000157 pw->pw_name, hostname);
158 dump_base64(stdout, blob, len);
Damien Miller874d77b2000-10-14 16:23:11 +1100159 fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END);
Damien Millereba71ba2000-04-29 23:57:08 +1000160 key_free(k);
161 xfree(blob);
162 exit(0);
163}
164
165void
Damien Miller874d77b2000-10-14 16:23:11 +1100166buffer_get_bignum_bits(Buffer *b, BIGNUM *value)
167{
168 int bits = buffer_get_int(b);
169 int bytes = (bits + 7) / 8;
170 if (buffer_len(b) < bytes)
171 fatal("buffer_get_bignum_bits: input buffer too small");
Ben Lindstrom46c16222000-12-22 01:43:59 +0000172 BN_bin2bn((u_char *)buffer_ptr(b), bytes, value);
Damien Miller874d77b2000-10-14 16:23:11 +1100173 buffer_consume(b, bytes);
174}
175
176Key *
177do_convert_private_ssh2_from_blob(char *blob, int blen)
178{
179 Buffer b;
180 DSA *dsa;
181 Key *key = NULL;
182 int ignore, magic, rlen;
183 char *type, *cipher;
184
185 buffer_init(&b);
186 buffer_append(&b, blob, blen);
187
188 magic = buffer_get_int(&b);
189 if (magic != SSH_COM_PRIVATE_KEY_MAGIC) {
190 error("bad magic 0x%x != 0x%x", magic, SSH_COM_PRIVATE_KEY_MAGIC);
191 buffer_free(&b);
192 return NULL;
193 }
194 ignore = buffer_get_int(&b);
195 type = buffer_get_string(&b, NULL);
196 cipher = buffer_get_string(&b, NULL);
197 ignore = buffer_get_int(&b);
198 ignore = buffer_get_int(&b);
199 ignore = buffer_get_int(&b);
200 xfree(type);
201
202 if (strcmp(cipher, "none") != 0) {
203 error("unsupported cipher %s", cipher);
204 xfree(cipher);
205 buffer_free(&b);
206 return NULL;
207 }
208 xfree(cipher);
209
210 key = key_new(KEY_DSA);
211 dsa = key->dsa;
212 dsa->priv_key = BN_new();
213 if (dsa->priv_key == NULL) {
214 error("alloc priv_key failed");
215 key_free(key);
216 return NULL;
217 }
218 buffer_get_bignum_bits(&b, dsa->p);
219 buffer_get_bignum_bits(&b, dsa->g);
220 buffer_get_bignum_bits(&b, dsa->q);
221 buffer_get_bignum_bits(&b, dsa->pub_key);
222 buffer_get_bignum_bits(&b, dsa->priv_key);
223 rlen = buffer_len(&b);
224 if(rlen != 0)
225 error("do_convert_private_ssh2_from_blob: remaining bytes in key blob %d", rlen);
226 buffer_free(&b);
227 return key;
228}
229
230void
Damien Millereba71ba2000-04-29 23:57:08 +1000231do_convert_from_ssh2(struct passwd *pw)
232{
233 Key *k;
234 int blen;
235 char line[1024], *p;
236 char blob[8096];
237 char encoded[8096];
238 struct stat st;
Damien Miller874d77b2000-10-14 16:23:11 +1100239 int escaped = 0, private = 0, ok;
Damien Millereba71ba2000-04-29 23:57:08 +1000240 FILE *fp;
241
242 if (!have_identity)
243 ask_filename(pw, "Enter file in which the key is");
244 if (stat(identity_file, &st) < 0) {
245 perror(identity_file);
246 exit(1);
247 }
248 fp = fopen(identity_file, "r");
249 if (fp == NULL) {
250 perror(identity_file);
251 exit(1);
252 }
253 encoded[0] = '\0';
254 while (fgets(line, sizeof(line), fp)) {
Damien Miller30c3d422000-05-09 11:02:59 +1000255 if (!(p = strchr(line, '\n'))) {
256 fprintf(stderr, "input line too long.\n");
257 exit(1);
258 }
259 if (p > line && p[-1] == '\\')
260 escaped++;
Damien Millereba71ba2000-04-29 23:57:08 +1000261 if (strncmp(line, "----", 4) == 0 ||
262 strstr(line, ": ") != NULL) {
Damien Miller874d77b2000-10-14 16:23:11 +1100263 if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL)
264 private = 1;
Damien Millereba71ba2000-04-29 23:57:08 +1000265 fprintf(stderr, "ignore: %s", line);
266 continue;
267 }
Damien Miller30c3d422000-05-09 11:02:59 +1000268 if (escaped) {
269 escaped--;
270 fprintf(stderr, "escaped: %s", line);
271 continue;
Damien Millereba71ba2000-04-29 23:57:08 +1000272 }
273 *p = '\0';
274 strlcat(encoded, line, sizeof(encoded));
275 }
Ben Lindstrom46c16222000-12-22 01:43:59 +0000276 blen = uudecode(encoded, (u_char *)blob, sizeof(blob));
Damien Millereba71ba2000-04-29 23:57:08 +1000277 if (blen < 0) {
278 fprintf(stderr, "uudecode failed.\n");
279 exit(1);
280 }
Damien Miller874d77b2000-10-14 16:23:11 +1100281 k = private ?
282 do_convert_private_ssh2_from_blob(blob, blen) :
Damien Miller0bc1bd82000-11-13 22:57:25 +1100283 key_from_blob(blob, blen);
Damien Miller874d77b2000-10-14 16:23:11 +1100284 if (k == NULL) {
285 fprintf(stderr, "decode blob failed.\n");
286 exit(1);
287 }
288 ok = private ?
289 PEM_write_DSAPrivateKey(stdout, k->dsa, NULL, NULL, 0, NULL, NULL) :
290 key_write(k, stdout);
291 if (!ok) {
292 fprintf(stderr, "key write failed");
293 exit(1);
294 }
Damien Millereba71ba2000-04-29 23:57:08 +1000295 key_free(k);
296 fprintf(stdout, "\n");
297 fclose(fp);
298 exit(0);
299}
300
301void
302do_print_public(struct passwd *pw)
303{
304 Key *k;
Damien Millereba71ba2000-04-29 23:57:08 +1000305 struct stat st;
306
307 if (!have_identity)
308 ask_filename(pw, "Enter file in which the key is");
309 if (stat(identity_file, &st) < 0) {
310 perror(identity_file);
311 exit(1);
312 }
Damien Miller0bc1bd82000-11-13 22:57:25 +1100313 k = key_new(KEY_UNSPEC);
Damien Millereba71ba2000-04-29 23:57:08 +1000314 if (!try_load_key(identity_file, k)) {
315 fprintf(stderr, "load failed\n");
316 exit(1);
317 }
Damien Millereba71ba2000-04-29 23:57:08 +1000318 if (!key_write(k, stdout))
319 fprintf(stderr, "key_write failed");
320 key_free(k);
Damien Millereba71ba2000-04-29 23:57:08 +1000321 fprintf(stdout, "\n");
322 exit(0);
323}
324
Damien Miller10f6f6b1999-11-17 17:29:08 +1100325void
326do_fingerprint(struct passwd *pw)
327{
Damien Millerad833b32000-08-23 10:46:23 +1000328
Damien Miller98c7ad62000-03-09 21:27:49 +1100329 FILE *f;
Damien Millereba71ba2000-04-29 23:57:08 +1000330 Key *public;
Damien Miller98c7ad62000-03-09 21:27:49 +1100331 char *comment = NULL, *cp, *ep, line[16*1024];
Damien Miller0bc1bd82000-11-13 22:57:25 +1100332 int i, skip = 0, num = 1, invalid = 1, success = 0;
Damien Miller95def091999-11-25 00:26:21 +1100333 struct stat st;
Damien Miller10f6f6b1999-11-17 17:29:08 +1100334
Damien Miller95def091999-11-25 00:26:21 +1100335 if (!have_identity)
336 ask_filename(pw, "Enter file in which the key is");
337 if (stat(identity_file, &st) < 0) {
338 perror(identity_file);
339 exit(1);
340 }
Damien Miller0bc1bd82000-11-13 22:57:25 +1100341 public = key_new(KEY_RSA1);
Damien Millereba71ba2000-04-29 23:57:08 +1000342 if (load_public_key(identity_file, public, &comment)) {
Damien Miller0bc1bd82000-11-13 22:57:25 +1100343 success = 1;
344 } else {
Damien Millereba71ba2000-04-29 23:57:08 +1000345 key_free(public);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100346 public = key_new(KEY_UNSPEC);
347 if (try_load_public_key(identity_file, public, &comment))
348 success = 1;
349 else
Ben Lindstrom2941f112000-12-29 16:50:13 +0000350 debug("try_load_public_key KEY_UNSPEC failed");
Damien Miller0bc1bd82000-11-13 22:57:25 +1100351 }
352 if (success) {
353 printf("%d %s %s\n", key_size(public), key_fingerprint(public), comment);
354 key_free(public);
355 xfree(comment);
Damien Miller98c7ad62000-03-09 21:27:49 +1100356 exit(0);
357 }
Damien Miller98c7ad62000-03-09 21:27:49 +1100358
359 f = fopen(identity_file, "r");
360 if (f != NULL) {
Damien Miller98c7ad62000-03-09 21:27:49 +1100361 while (fgets(line, sizeof(line), f)) {
362 i = strlen(line) - 1;
363 if (line[i] != '\n') {
364 error("line %d too long: %.40s...", num, line);
365 skip = 1;
366 continue;
Damien Miller95def091999-11-25 00:26:21 +1100367 }
Damien Miller98c7ad62000-03-09 21:27:49 +1100368 num++;
369 if (skip) {
370 skip = 0;
371 continue;
372 }
373 line[i] = '\0';
374
375 /* Skip leading whitespace, empty and comment lines. */
376 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
377 ;
378 if (!*cp || *cp == '\n' || *cp == '#')
379 continue ;
380 i = strtol(cp, &ep, 10);
381 if (i == 0 || ep == NULL || (*ep != ' ' && *ep != '\t')) {
382 int quoted = 0;
383 comment = cp;
384 for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
385 if (*cp == '\\' && cp[1] == '"')
386 cp++; /* Skip both */
387 else if (*cp == '"')
388 quoted = !quoted;
389 }
390 if (!*cp)
391 continue;
392 *cp++ = '\0';
393 }
394 ep = cp;
Ben Lindstrom2941f112000-12-29 16:50:13 +0000395 public = key_new(KEY_RSA1);
396 if (key_read(public, &cp) != 1) {
397 cp = ep;
398 key_free(public);
399 public = key_new(KEY_UNSPEC);
400 if (key_read(public, &cp) != 1) {
401 key_free(public);
402 continue;
403 }
Damien Miller98c7ad62000-03-09 21:27:49 +1100404 }
Ben Lindstrom2941f112000-12-29 16:50:13 +0000405 comment = *cp ? cp : comment;
406 printf("%d %s %s\n", key_size(public),
407 key_fingerprint(public),
408 comment ? comment : "no comment");
409 invalid = 0;
Damien Miller95def091999-11-25 00:26:21 +1100410 }
Damien Miller98c7ad62000-03-09 21:27:49 +1100411 fclose(f);
Damien Miller95def091999-11-25 00:26:21 +1100412 }
Damien Millerad833b32000-08-23 10:46:23 +1000413 key_free(public);
Damien Miller98c7ad62000-03-09 21:27:49 +1100414 if (invalid) {
415 printf("%s is not a valid key file.\n", identity_file);
416 exit(1);
417 }
Damien Miller95def091999-11-25 00:26:21 +1100418 exit(0);
Damien Miller10f6f6b1999-11-17 17:29:08 +1100419}
420
Damien Miller95def091999-11-25 00:26:21 +1100421/*
422 * Perform changing a passphrase. The argument is the passwd structure
423 * for the current user.
424 */
Damien Miller10f6f6b1999-11-17 17:29:08 +1100425void
426do_change_passphrase(struct passwd *pw)
427{
Damien Miller95def091999-11-25 00:26:21 +1100428 char *comment;
429 char *old_passphrase, *passphrase1, *passphrase2;
430 struct stat st;
Damien Millereba71ba2000-04-29 23:57:08 +1000431 Key *private;
432 Key *public;
Damien Miller0bc1bd82000-11-13 22:57:25 +1100433 int type = KEY_RSA1;
Damien Miller10f6f6b1999-11-17 17:29:08 +1100434
Damien Miller95def091999-11-25 00:26:21 +1100435 if (!have_identity)
436 ask_filename(pw, "Enter file in which the key is");
Damien Miller95def091999-11-25 00:26:21 +1100437 if (stat(identity_file, &st) < 0) {
438 perror(identity_file);
439 exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000440 }
Damien Miller0bc1bd82000-11-13 22:57:25 +1100441 public = key_new(type);
442 if (!load_public_key(identity_file, public, NULL)) {
443 type = KEY_UNSPEC;
444 } else {
Damien Millereba71ba2000-04-29 23:57:08 +1000445 /* Clear the public key since we are just about to load the whole file. */
446 key_free(public);
Damien Miller95def091999-11-25 00:26:21 +1100447 }
Damien Miller95def091999-11-25 00:26:21 +1100448 /* Try to load the file with empty passphrase. */
Damien Millereba71ba2000-04-29 23:57:08 +1000449 private = key_new(type);
450 if (!load_private_key(identity_file, "", private, &comment)) {
Damien Miller95def091999-11-25 00:26:21 +1100451 if (identity_passphrase)
452 old_passphrase = xstrdup(identity_passphrase);
453 else
454 old_passphrase = read_passphrase("Enter old passphrase: ", 1);
Damien Millereba71ba2000-04-29 23:57:08 +1000455 if (!load_private_key(identity_file, old_passphrase, private, &comment)) {
Damien Miller95def091999-11-25 00:26:21 +1100456 memset(old_passphrase, 0, strlen(old_passphrase));
457 xfree(old_passphrase);
458 printf("Bad passphrase.\n");
459 exit(1);
460 }
Damien Miller95def091999-11-25 00:26:21 +1100461 memset(old_passphrase, 0, strlen(old_passphrase));
462 xfree(old_passphrase);
463 }
464 printf("Key has comment '%s'\n", comment);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000465
Damien Miller95def091999-11-25 00:26:21 +1100466 /* Ask the new passphrase (twice). */
467 if (identity_new_passphrase) {
468 passphrase1 = xstrdup(identity_new_passphrase);
469 passphrase2 = NULL;
470 } else {
471 passphrase1 =
472 read_passphrase("Enter new passphrase (empty for no passphrase): ", 1);
473 passphrase2 = read_passphrase("Enter same passphrase again: ", 1);
474
475 /* Verify that they are the same. */
476 if (strcmp(passphrase1, passphrase2) != 0) {
477 memset(passphrase1, 0, strlen(passphrase1));
478 memset(passphrase2, 0, strlen(passphrase2));
479 xfree(passphrase1);
480 xfree(passphrase2);
481 printf("Pass phrases do not match. Try again.\n");
482 exit(1);
483 }
484 /* Destroy the other copy. */
485 memset(passphrase2, 0, strlen(passphrase2));
486 xfree(passphrase2);
487 }
488
489 /* Save the file using the new passphrase. */
Damien Millereba71ba2000-04-29 23:57:08 +1000490 if (!save_private_key(identity_file, passphrase1, private, comment)) {
Damien Miller95def091999-11-25 00:26:21 +1100491 printf("Saving the key failed: %s: %s.\n",
492 identity_file, strerror(errno));
493 memset(passphrase1, 0, strlen(passphrase1));
494 xfree(passphrase1);
Damien Millereba71ba2000-04-29 23:57:08 +1000495 key_free(private);
Damien Miller95def091999-11-25 00:26:21 +1100496 xfree(comment);
497 exit(1);
498 }
499 /* Destroy the passphrase and the copy of the key in memory. */
500 memset(passphrase1, 0, strlen(passphrase1));
501 xfree(passphrase1);
Damien Millereba71ba2000-04-29 23:57:08 +1000502 key_free(private); /* Destroys contents */
Damien Miller95def091999-11-25 00:26:21 +1100503 xfree(comment);
504
505 printf("Your identification has been saved with the new passphrase.\n");
506 exit(0);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000507}
508
Damien Miller95def091999-11-25 00:26:21 +1100509/*
510 * Change the comment of a private key file.
511 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000512void
513do_change_comment(struct passwd *pw)
514{
Ben Lindstrom5fc62702001-03-09 18:19:24 +0000515 char new_comment[1024], *comment, *passphrase;
516 Key *private, *public;
Damien Miller95def091999-11-25 00:26:21 +1100517 struct stat st;
518 FILE *f;
Ben Lindstrom5fc62702001-03-09 18:19:24 +0000519 int fd;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000520
Damien Miller95def091999-11-25 00:26:21 +1100521 if (!have_identity)
522 ask_filename(pw, "Enter file in which the key is");
Damien Miller95def091999-11-25 00:26:21 +1100523 if (stat(identity_file, &st) < 0) {
524 perror(identity_file);
525 exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000526 }
Damien Miller5428f641999-11-25 11:54:57 +1100527 /*
528 * Try to load the public key from the file the verify that it is
529 * readable and of the proper format.
530 */
Damien Miller0bc1bd82000-11-13 22:57:25 +1100531 public = key_new(KEY_RSA1);
Damien Millereba71ba2000-04-29 23:57:08 +1000532 if (!load_public_key(identity_file, public, NULL)) {
Damien Miller95def091999-11-25 00:26:21 +1100533 printf("%s is not a valid key file.\n", identity_file);
Ben Lindstromd8a90212001-02-15 03:08:27 +0000534 printf("Comments are only supported in RSA1 keys\n");
Damien Miller95def091999-11-25 00:26:21 +1100535 exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000536 }
Damien Miller5428f641999-11-25 11:54:57 +1100537
Damien Miller0bc1bd82000-11-13 22:57:25 +1100538 private = key_new(KEY_RSA1);
Damien Millereba71ba2000-04-29 23:57:08 +1000539 if (load_private_key(identity_file, "", private, &comment))
Damien Miller95def091999-11-25 00:26:21 +1100540 passphrase = xstrdup("");
541 else {
Damien Miller95def091999-11-25 00:26:21 +1100542 if (identity_passphrase)
543 passphrase = xstrdup(identity_passphrase);
544 else if (identity_new_passphrase)
545 passphrase = xstrdup(identity_new_passphrase);
546 else
547 passphrase = read_passphrase("Enter passphrase: ", 1);
548 /* Try to load using the passphrase. */
Damien Millereba71ba2000-04-29 23:57:08 +1000549 if (!load_private_key(identity_file, passphrase, private, &comment)) {
Damien Miller95def091999-11-25 00:26:21 +1100550 memset(passphrase, 0, strlen(passphrase));
551 xfree(passphrase);
552 printf("Bad passphrase.\n");
553 exit(1);
554 }
555 }
556 printf("Key now has comment '%s'\n", comment);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000557
Damien Miller95def091999-11-25 00:26:21 +1100558 if (identity_comment) {
559 strlcpy(new_comment, identity_comment, sizeof(new_comment));
560 } else {
561 printf("Enter new comment: ");
562 fflush(stdout);
563 if (!fgets(new_comment, sizeof(new_comment), stdin)) {
564 memset(passphrase, 0, strlen(passphrase));
Damien Millereba71ba2000-04-29 23:57:08 +1000565 key_free(private);
Damien Miller95def091999-11-25 00:26:21 +1100566 exit(1);
567 }
Damien Miller95def091999-11-25 00:26:21 +1100568 if (strchr(new_comment, '\n'))
569 *strchr(new_comment, '\n') = 0;
570 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000571
Damien Miller95def091999-11-25 00:26:21 +1100572 /* Save the file using the new passphrase. */
Damien Millereba71ba2000-04-29 23:57:08 +1000573 if (!save_private_key(identity_file, passphrase, private, new_comment)) {
Damien Miller95def091999-11-25 00:26:21 +1100574 printf("Saving the key failed: %s: %s.\n",
575 identity_file, strerror(errno));
576 memset(passphrase, 0, strlen(passphrase));
577 xfree(passphrase);
Damien Millereba71ba2000-04-29 23:57:08 +1000578 key_free(private);
Damien Miller95def091999-11-25 00:26:21 +1100579 xfree(comment);
580 exit(1);
581 }
Damien Miller95def091999-11-25 00:26:21 +1100582 memset(passphrase, 0, strlen(passphrase));
583 xfree(passphrase);
Damien Millereba71ba2000-04-29 23:57:08 +1000584 key_free(private);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000585
Damien Miller95def091999-11-25 00:26:21 +1100586 strlcat(identity_file, ".pub", sizeof(identity_file));
Ben Lindstrom5fc62702001-03-09 18:19:24 +0000587 fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
588 if (fd == -1) {
Damien Miller95def091999-11-25 00:26:21 +1100589 printf("Could not save your public key in %s\n", identity_file);
590 exit(1);
591 }
Ben Lindstrom5fc62702001-03-09 18:19:24 +0000592 f = fdopen(fd, "w");
593 if (f == NULL) {
594 printf("fdopen %s failed", identity_file);
595 exit(1);
596 }
Damien Millereba71ba2000-04-29 23:57:08 +1000597 if (!key_write(public, f))
598 fprintf(stderr, "write key failed");
599 key_free(public);
600 fprintf(f, " %s\n", new_comment);
Damien Miller95def091999-11-25 00:26:21 +1100601 fclose(f);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000602
Damien Miller95def091999-11-25 00:26:21 +1100603 xfree(comment);
604
605 printf("The comment in your key file has been changed.\n");
606 exit(0);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000607}
608
Damien Miller431f66b1999-11-21 18:31:57 +1100609void
610usage(void)
611{
Ben Lindstrom7ab51172001-03-05 05:13:38 +0000612 printf("Usage: %s [-lpqxXyc] [-t type] [-b bits] [-f file] [-C comment] "
Ben Lindstromb7c92322001-03-05 05:10:52 +0000613 "[-N new-pass] [-P pass]\n", __progname);
Damien Miller95def091999-11-25 00:26:21 +1100614 exit(1);
Damien Miller431f66b1999-11-21 18:31:57 +1100615}
616
Damien Miller95def091999-11-25 00:26:21 +1100617/*
618 * Main program for key management.
619 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000620int
621main(int ac, char **av)
622{
Damien Miller95def091999-11-25 00:26:21 +1100623 char dotsshdir[16 * 1024], comment[1024], *passphrase1, *passphrase2;
Ben Lindstrom5fc62702001-03-09 18:19:24 +0000624 Key *private, *public;
Damien Miller95def091999-11-25 00:26:21 +1100625 struct passwd *pw;
Ben Lindstrom5fc62702001-03-09 18:19:24 +0000626 int opt, type, fd;
Damien Miller95def091999-11-25 00:26:21 +1100627 struct stat st;
628 FILE *f;
Damien Miller0bc1bd82000-11-13 22:57:25 +1100629
Damien Miller95def091999-11-25 00:26:21 +1100630 extern int optind;
631 extern char *optarg;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000632
Ben Lindstrom49a79c02000-11-17 03:47:20 +0000633 __progname = get_progname(av[0]);
Damien Millerf9b625c2000-07-09 22:42:32 +1000634 init_rng();
635
Damien Millerd3a18572000-06-07 19:55:44 +1000636 SSLeay_add_all_algorithms();
Damien Millereba71ba2000-04-29 23:57:08 +1000637
Damien Miller5428f641999-11-25 11:54:57 +1100638 /* we need this for the home * directory. */
Damien Miller95def091999-11-25 00:26:21 +1100639 pw = getpwuid(getuid());
640 if (!pw) {
641 printf("You don't exist, go away!\n");
642 exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000643 }
Damien Millereba71ba2000-04-29 23:57:08 +1000644 if (gethostname(hostname, sizeof(hostname)) < 0) {
645 perror("gethostname");
646 exit(1);
647 }
Damien Miller5428f641999-11-25 11:54:57 +1100648
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000649 while ((opt = getopt(ac, av, "dqpclRxXyb:f:t:P:N:C:")) != -1) {
Damien Miller95def091999-11-25 00:26:21 +1100650 switch (opt) {
651 case 'b':
652 bits = atoi(optarg);
653 if (bits < 512 || bits > 32768) {
654 printf("Bits has bad value.\n");
655 exit(1);
656 }
657 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000658
Damien Miller95def091999-11-25 00:26:21 +1100659 case 'l':
660 print_fingerprint = 1;
661 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000662
Damien Miller95def091999-11-25 00:26:21 +1100663 case 'p':
664 change_passphrase = 1;
665 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000666
Damien Miller95def091999-11-25 00:26:21 +1100667 case 'c':
668 change_comment = 1;
669 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000670
Damien Miller95def091999-11-25 00:26:21 +1100671 case 'f':
672 strlcpy(identity_file, optarg, sizeof(identity_file));
673 have_identity = 1;
674 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000675
Damien Miller95def091999-11-25 00:26:21 +1100676 case 'P':
677 identity_passphrase = optarg;
678 break;
679
680 case 'N':
681 identity_new_passphrase = optarg;
682 break;
683
684 case 'C':
685 identity_comment = optarg;
686 break;
687
688 case 'q':
689 quiet = 1;
690 break;
691
Damien Millereba71ba2000-04-29 23:57:08 +1000692 case 'R':
Damien Miller0bc1bd82000-11-13 22:57:25 +1100693 /* unused */
694 exit(0);
Damien Millereba71ba2000-04-29 23:57:08 +1000695 break;
696
697 case 'x':
698 convert_to_ssh2 = 1;
699 break;
700
701 case 'X':
702 convert_from_ssh2 = 1;
703 break;
704
705 case 'y':
706 print_public = 1;
707 break;
708
709 case 'd':
Damien Miller0bc1bd82000-11-13 22:57:25 +1100710 key_type_name = "dsa";
Damien Millereba71ba2000-04-29 23:57:08 +1000711 break;
712
Damien Miller0bc1bd82000-11-13 22:57:25 +1100713 case 't':
714 key_type_name = optarg;
Damien Miller0bc1bd82000-11-13 22:57:25 +1100715 break;
716
Damien Miller95def091999-11-25 00:26:21 +1100717 case '?':
718 default:
719 usage();
720 }
721 }
722 if (optind < ac) {
723 printf("Too many arguments.\n");
724 usage();
725 }
726 if (change_passphrase && change_comment) {
727 printf("Can only have one of -p and -c.\n");
728 usage();
729 }
730 if (print_fingerprint)
731 do_fingerprint(pw);
Damien Miller95def091999-11-25 00:26:21 +1100732 if (change_passphrase)
733 do_change_passphrase(pw);
Damien Miller95def091999-11-25 00:26:21 +1100734 if (change_comment)
735 do_change_comment(pw);
Damien Millereba71ba2000-04-29 23:57:08 +1000736 if (convert_to_ssh2)
737 do_convert_to_ssh2(pw);
738 if (convert_from_ssh2)
739 do_convert_from_ssh2(pw);
740 if (print_public)
741 do_print_public(pw);
Damien Miller95def091999-11-25 00:26:21 +1100742
743 arc4random_stir();
744
Damien Millere39cacc2000-11-29 12:18:44 +1100745 type = key_type_from_name(key_type_name);
746 if (type == KEY_UNSPEC) {
747 fprintf(stderr, "unknown key type %s\n", key_type_name);
748 exit(1);
Damien Millereba71ba2000-04-29 23:57:08 +1000749 }
Damien Miller0bc1bd82000-11-13 22:57:25 +1100750 if (!quiet)
Damien Millere39cacc2000-11-29 12:18:44 +1100751 printf("Generating public/private %s key pair.\n", key_type_name);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100752 private = key_generate(type, bits);
753 if (private == NULL) {
754 fprintf(stderr, "key_generate failed");
755 exit(1);
756 }
757 public = key_from_private(private);
Damien Miller95def091999-11-25 00:26:21 +1100758
759 if (!have_identity)
760 ask_filename(pw, "Enter file in which to save the key");
761
762 /* Create ~/.ssh directory if it doesn\'t already exist. */
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000763 snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, _PATH_SSH_USER_DIR);
Damien Miller95def091999-11-25 00:26:21 +1100764 if (strstr(identity_file, dotsshdir) != NULL &&
765 stat(dotsshdir, &st) < 0) {
Damien Millerbe484b52000-07-15 14:14:16 +1000766 if (mkdir(dotsshdir, 0700) < 0)
Damien Miller95def091999-11-25 00:26:21 +1100767 error("Could not create directory '%s'.", dotsshdir);
768 else if (!quiet)
769 printf("Created directory '%s'.\n", dotsshdir);
770 }
771 /* If the file already exists, ask the user to confirm. */
772 if (stat(identity_file, &st) >= 0) {
773 char yesno[3];
774 printf("%s already exists.\n", identity_file);
775 printf("Overwrite (y/n)? ");
776 fflush(stdout);
777 if (fgets(yesno, sizeof(yesno), stdin) == NULL)
778 exit(1);
779 if (yesno[0] != 'y' && yesno[0] != 'Y')
780 exit(1);
781 }
782 /* Ask for a passphrase (twice). */
783 if (identity_passphrase)
784 passphrase1 = xstrdup(identity_passphrase);
785 else if (identity_new_passphrase)
786 passphrase1 = xstrdup(identity_new_passphrase);
787 else {
788passphrase_again:
789 passphrase1 =
790 read_passphrase("Enter passphrase (empty for no passphrase): ", 1);
791 passphrase2 = read_passphrase("Enter same passphrase again: ", 1);
792 if (strcmp(passphrase1, passphrase2) != 0) {
793 /* The passphrases do not match. Clear them and retry. */
794 memset(passphrase1, 0, strlen(passphrase1));
795 memset(passphrase2, 0, strlen(passphrase2));
796 xfree(passphrase1);
797 xfree(passphrase2);
798 printf("Passphrases do not match. Try again.\n");
799 goto passphrase_again;
800 }
801 /* Clear the other copy of the passphrase. */
802 memset(passphrase2, 0, strlen(passphrase2));
803 xfree(passphrase2);
804 }
805
Damien Miller95def091999-11-25 00:26:21 +1100806 if (identity_comment) {
807 strlcpy(comment, identity_comment, sizeof(comment));
808 } else {
Damien Miller4af51302000-04-16 11:18:38 +1000809 /* Create default commend field for the passphrase. */
Damien Miller95def091999-11-25 00:26:21 +1100810 snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname);
811 }
812
813 /* Save the key with the given passphrase and comment. */
Damien Millereba71ba2000-04-29 23:57:08 +1000814 if (!save_private_key(identity_file, passphrase1, private, comment)) {
Damien Miller95def091999-11-25 00:26:21 +1100815 printf("Saving the key failed: %s: %s.\n",
Damien Millereba71ba2000-04-29 23:57:08 +1000816 identity_file, strerror(errno));
Damien Miller95def091999-11-25 00:26:21 +1100817 memset(passphrase1, 0, strlen(passphrase1));
818 xfree(passphrase1);
819 exit(1);
820 }
821 /* Clear the passphrase. */
822 memset(passphrase1, 0, strlen(passphrase1));
823 xfree(passphrase1);
824
825 /* Clear the private key and the random number generator. */
Damien Miller0bc1bd82000-11-13 22:57:25 +1100826 key_free(private);
Damien Miller95def091999-11-25 00:26:21 +1100827 arc4random_stir();
828
829 if (!quiet)
830 printf("Your identification has been saved in %s.\n", identity_file);
831
Damien Miller95def091999-11-25 00:26:21 +1100832 strlcat(identity_file, ".pub", sizeof(identity_file));
Ben Lindstrom5fc62702001-03-09 18:19:24 +0000833 fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
834 if (fd == -1) {
Damien Miller95def091999-11-25 00:26:21 +1100835 printf("Could not save your public key in %s\n", identity_file);
836 exit(1);
837 }
Ben Lindstrom5fc62702001-03-09 18:19:24 +0000838 f = fdopen(fd, "w");
839 if (f == NULL) {
840 printf("fdopen %s failed", identity_file);
841 exit(1);
842 }
Damien Millereba71ba2000-04-29 23:57:08 +1000843 if (!key_write(public, f))
844 fprintf(stderr, "write key failed");
845 fprintf(f, " %s\n", comment);
Damien Miller95def091999-11-25 00:26:21 +1100846 fclose(f);
847
848 if (!quiet) {
Damien Millereba71ba2000-04-29 23:57:08 +1000849 printf("Your public key has been saved in %s.\n",
850 identity_file);
Damien Miller95def091999-11-25 00:26:21 +1100851 printf("The key fingerprint is:\n");
Damien Millereba71ba2000-04-29 23:57:08 +1000852 printf("%s %s\n", key_fingerprint(public), comment);
Damien Miller95def091999-11-25 00:26:21 +1100853 }
Damien Millereba71ba2000-04-29 23:57:08 +1000854
855 key_free(public);
Damien Miller95def091999-11-25 00:26:21 +1100856 exit(0);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000857}