blob: b3074e8def92f25ec79d31f2eea6dbbf30d8754a [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 Lindstromd0fca422001-03-26 13:44:06 +000015RCSID("$OpenBSD: ssh-keygen.c,v 1.52 2001/03/26 08:07:09 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>
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;
Ben Lindstrom8fd372b2001-03-12 03:02:17 +000049int print_bubblebabble = 0;
Damien Miller10f6f6b1999-11-17 17:29:08 +110050
Damien Miller431f66b1999-11-21 18:31:57 +110051/* The identity file name, given on the command line or entered by the user. */
52char identity_file[1024];
53int have_identity = 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100054
55/* This is set to the passphrase if given on the command line. */
56char *identity_passphrase = NULL;
57
58/* This is set to the new passphrase if given on the command line. */
59char *identity_new_passphrase = NULL;
60
61/* This is set to the new comment if given on the command line. */
62char *identity_comment = NULL;
63
Damien Millereba71ba2000-04-29 23:57:08 +100064/* Dump public key file in format used by real and the original SSH 2 */
65int convert_to_ssh2 = 0;
66int convert_from_ssh2 = 0;
67int print_public = 0;
Damien Miller0bc1bd82000-11-13 22:57:25 +110068
Damien Millere39cacc2000-11-29 12:18:44 +110069/* default to RSA for SSH-1 */
70char *key_type_name = "rsa1";
Damien Millereba71ba2000-04-29 23:57:08 +100071
Damien Miller431f66b1999-11-21 18:31:57 +110072/* argv0 */
Damien Miller95def091999-11-25 00:26:21 +110073#ifdef HAVE___PROGNAME
Damien Miller431f66b1999-11-21 18:31:57 +110074extern char *__progname;
Ben Lindstrom49a79c02000-11-17 03:47:20 +000075#else
76char *__progname;
77#endif
Damien Millerd4a8b7e1999-10-27 13:42:43 +100078
Damien Millereba71ba2000-04-29 23:57:08 +100079char hostname[MAXHOSTNAMELEN];
80
Damien Miller431f66b1999-11-21 18:31:57 +110081void
82ask_filename(struct passwd *pw, const char *prompt)
Damien Millerd4a8b7e1999-10-27 13:42:43 +100083{
Damien Miller95def091999-11-25 00:26:21 +110084 char buf[1024];
Damien Millere39cacc2000-11-29 12:18:44 +110085 char *name = NULL;
86
87 switch (key_type_from_name(key_type_name)) {
88 case KEY_RSA1:
Ben Lindstrom226cfa02001-01-22 05:34:40 +000089 name = _PATH_SSH_CLIENT_IDENTITY;
Damien Millere39cacc2000-11-29 12:18:44 +110090 break;
91 case KEY_DSA:
Ben Lindstrom226cfa02001-01-22 05:34:40 +000092 name = _PATH_SSH_CLIENT_ID_DSA;
Damien Millere39cacc2000-11-29 12:18:44 +110093 break;
94 case KEY_RSA:
Ben Lindstrom226cfa02001-01-22 05:34:40 +000095 name = _PATH_SSH_CLIENT_ID_RSA;
Damien Millere39cacc2000-11-29 12:18:44 +110096 break;
97 default:
98 fprintf(stderr, "bad key type");
99 exit(1);
100 break;
101 }
102 snprintf(identity_file, sizeof(identity_file), "%s/%s", pw->pw_dir, name);
Ben Lindstrom3deda8b2000-12-22 20:27:43 +0000103 fprintf(stderr, "%s (%s): ", prompt, identity_file);
104 fflush(stderr);
Damien Miller95def091999-11-25 00:26:21 +1100105 if (fgets(buf, sizeof(buf), stdin) == NULL)
106 exit(1);
107 if (strchr(buf, '\n'))
108 *strchr(buf, '\n') = 0;
109 if (strcmp(buf, "") != 0)
110 strlcpy(identity_file, buf, sizeof(identity_file));
111 have_identity = 1;
Damien Miller10f6f6b1999-11-17 17:29:08 +1100112}
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000113
Ben Lindstromd0fca422001-03-26 13:44:06 +0000114Key *
115try_load_pem_key(char *filename)
Damien Millereba71ba2000-04-29 23:57:08 +1000116{
Ben Lindstromd0fca422001-03-26 13:44:06 +0000117 char *pass;
118 Key *prv;
119
120 prv = key_load_private(filename, "", NULL);
121 if (prv == NULL) {
122 pass = read_passphrase("Enter passphrase: ", 1);
123 prv = key_load_private(filename, pass, NULL);
Damien Millereba71ba2000-04-29 23:57:08 +1000124 memset(pass, 0, strlen(pass));
125 xfree(pass);
126 }
Ben Lindstromd0fca422001-03-26 13:44:06 +0000127 return prv;
Damien Millereba71ba2000-04-29 23:57:08 +1000128}
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 ----"
Kevin Stevesef4eea92001-02-05 12:42:17 +0000133#define SSH_COM_PRIVATE_KEY_MAGIC 0x3f6ff9eb
Damien Millereba71ba2000-04-29 23:57:08 +1000134
135void
136do_convert_to_ssh2(struct passwd *pw)
137{
Ben Lindstromd0fca422001-03-26 13:44:06 +0000138 Key *prv;
Damien Millereba71ba2000-04-29 23:57:08 +1000139 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 }
Ben Lindstromd0fca422001-03-26 13:44:06 +0000149 prv = try_load_pem_key(identity_file);
150 if (prv == NULL) {
Damien Millereba71ba2000-04-29 23:57:08 +1000151 fprintf(stderr, "load failed\n");
152 exit(1);
153 }
Ben Lindstromd0fca422001-03-26 13:44:06 +0000154 key_to_blob(prv, &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",
Ben Lindstromd0fca422001-03-26 13:44:06 +0000158 key_size(prv), key_type(prv),
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);
Ben Lindstromd0fca422001-03-26 13:44:06 +0000162 key_free(prv);
Damien Millereba71ba2000-04-29 23:57:08 +1000163 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{
Ben Lindstromd0fca422001-03-26 13:44:06 +0000306 Key *prv;
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 }
Ben Lindstromd0fca422001-03-26 13:44:06 +0000315 prv = try_load_pem_key(identity_file);
316 if (prv == NULL) {
Damien Millereba71ba2000-04-29 23:57:08 +1000317 fprintf(stderr, "load failed\n");
318 exit(1);
319 }
Ben Lindstromd0fca422001-03-26 13:44:06 +0000320 if (!key_write(prv, stdout))
Damien Millereba71ba2000-04-29 23:57:08 +1000321 fprintf(stderr, "key_write failed");
Ben Lindstromd0fca422001-03-26 13:44:06 +0000322 key_free(prv);
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 Miller98c7ad62000-03-09 21:27:49 +1100330 FILE *f;
Damien Millereba71ba2000-04-29 23:57:08 +1000331 Key *public;
Ben Lindstrom8fd372b2001-03-12 03:02:17 +0000332 char *comment = NULL, *cp, *ep, line[16*1024], *fp;
Ben Lindstromd0fca422001-03-26 13:44:06 +0000333 int i, skip = 0, num = 1, invalid = 1, rep, fptype;
Damien Miller95def091999-11-25 00:26:21 +1100334 struct stat st;
Damien Miller10f6f6b1999-11-17 17:29:08 +1100335
Ben Lindstromd0fca422001-03-26 13:44:06 +0000336 fptype = print_bubblebabble ? SSH_FP_SHA1 : SSH_FP_MD5;
337 rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_HEX;
Ben Lindstrom8fd372b2001-03-12 03:02:17 +0000338
Damien Miller95def091999-11-25 00:26:21 +1100339 if (!have_identity)
340 ask_filename(pw, "Enter file in which the key is");
341 if (stat(identity_file, &st) < 0) {
342 perror(identity_file);
343 exit(1);
344 }
Ben Lindstromd0fca422001-03-26 13:44:06 +0000345 public = key_load_public(identity_file, &comment);
346 if (public != NULL) {
347 fp = key_fingerprint(public, fptype, rep);
348 printf("%d %s %s\n", key_size(public), fp, comment);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100349 key_free(public);
350 xfree(comment);
Ben Lindstrom8fd372b2001-03-12 03:02:17 +0000351 xfree(fp);
Damien Miller98c7ad62000-03-09 21:27:49 +1100352 exit(0);
353 }
Ben Lindstromd0fca422001-03-26 13:44:06 +0000354 if (comment)
355 xfree(comment);
Damien Miller98c7ad62000-03-09 21:27:49 +1100356
357 f = fopen(identity_file, "r");
358 if (f != NULL) {
Damien Miller98c7ad62000-03-09 21:27:49 +1100359 while (fgets(line, sizeof(line), f)) {
360 i = strlen(line) - 1;
361 if (line[i] != '\n') {
362 error("line %d too long: %.40s...", num, line);
363 skip = 1;
364 continue;
Damien Miller95def091999-11-25 00:26:21 +1100365 }
Damien Miller98c7ad62000-03-09 21:27:49 +1100366 num++;
367 if (skip) {
368 skip = 0;
369 continue;
370 }
371 line[i] = '\0';
372
373 /* Skip leading whitespace, empty and comment lines. */
374 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
375 ;
376 if (!*cp || *cp == '\n' || *cp == '#')
377 continue ;
378 i = strtol(cp, &ep, 10);
379 if (i == 0 || ep == NULL || (*ep != ' ' && *ep != '\t')) {
380 int quoted = 0;
381 comment = cp;
382 for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
383 if (*cp == '\\' && cp[1] == '"')
384 cp++; /* Skip both */
385 else if (*cp == '"')
386 quoted = !quoted;
387 }
388 if (!*cp)
389 continue;
390 *cp++ = '\0';
391 }
392 ep = cp;
Ben Lindstrom2941f112000-12-29 16:50:13 +0000393 public = key_new(KEY_RSA1);
394 if (key_read(public, &cp) != 1) {
395 cp = ep;
396 key_free(public);
397 public = key_new(KEY_UNSPEC);
398 if (key_read(public, &cp) != 1) {
399 key_free(public);
400 continue;
401 }
Damien Miller98c7ad62000-03-09 21:27:49 +1100402 }
Ben Lindstrom2941f112000-12-29 16:50:13 +0000403 comment = *cp ? cp : comment;
Ben Lindstromd0fca422001-03-26 13:44:06 +0000404 fp = key_fingerprint(public, fptype, rep);
Ben Lindstrom8fd372b2001-03-12 03:02:17 +0000405 printf("%d %s %s\n", key_size(public), fp,
Ben Lindstrom2941f112000-12-29 16:50:13 +0000406 comment ? comment : "no comment");
Ben Lindstrom8fd372b2001-03-12 03:02:17 +0000407 xfree(fp);
Ben Lindstromd0fca422001-03-26 13:44:06 +0000408 key_free(public);
Ben Lindstrom2941f112000-12-29 16:50:13 +0000409 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 Miller98c7ad62000-03-09 21:27:49 +1100413 if (invalid) {
414 printf("%s is not a valid key file.\n", identity_file);
415 exit(1);
416 }
Damien Miller95def091999-11-25 00:26:21 +1100417 exit(0);
Damien Miller10f6f6b1999-11-17 17:29:08 +1100418}
419
Damien Miller95def091999-11-25 00:26:21 +1100420/*
421 * Perform changing a passphrase. The argument is the passwd structure
422 * for the current user.
423 */
Damien Miller10f6f6b1999-11-17 17:29:08 +1100424void
425do_change_passphrase(struct passwd *pw)
426{
Damien Miller95def091999-11-25 00:26:21 +1100427 char *comment;
428 char *old_passphrase, *passphrase1, *passphrase2;
429 struct stat st;
Damien Millereba71ba2000-04-29 23:57:08 +1000430 Key *private;
Damien Miller10f6f6b1999-11-17 17:29:08 +1100431
Damien Miller95def091999-11-25 00:26:21 +1100432 if (!have_identity)
433 ask_filename(pw, "Enter file in which the key is");
Damien Miller95def091999-11-25 00:26:21 +1100434 if (stat(identity_file, &st) < 0) {
435 perror(identity_file);
436 exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000437 }
Damien Miller95def091999-11-25 00:26:21 +1100438 /* Try to load the file with empty passphrase. */
Ben Lindstromd0fca422001-03-26 13:44:06 +0000439 private = key_load_private(identity_file, "", &comment);
440 if (private == NULL) {
Damien Miller95def091999-11-25 00:26:21 +1100441 if (identity_passphrase)
442 old_passphrase = xstrdup(identity_passphrase);
443 else
444 old_passphrase = read_passphrase("Enter old passphrase: ", 1);
Ben Lindstromd0fca422001-03-26 13:44:06 +0000445 private = key_load_private(identity_file, old_passphrase , &comment);
446 memset(old_passphrase, 0, strlen(old_passphrase));
447 xfree(old_passphrase);
448 if (private == NULL) {
Damien Miller95def091999-11-25 00:26:21 +1100449 printf("Bad passphrase.\n");
450 exit(1);
451 }
Damien Miller95def091999-11-25 00:26:21 +1100452 }
453 printf("Key has comment '%s'\n", comment);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000454
Damien Miller95def091999-11-25 00:26:21 +1100455 /* Ask the new passphrase (twice). */
456 if (identity_new_passphrase) {
457 passphrase1 = xstrdup(identity_new_passphrase);
458 passphrase2 = NULL;
459 } else {
460 passphrase1 =
461 read_passphrase("Enter new passphrase (empty for no passphrase): ", 1);
462 passphrase2 = read_passphrase("Enter same passphrase again: ", 1);
463
464 /* Verify that they are the same. */
465 if (strcmp(passphrase1, passphrase2) != 0) {
466 memset(passphrase1, 0, strlen(passphrase1));
467 memset(passphrase2, 0, strlen(passphrase2));
468 xfree(passphrase1);
469 xfree(passphrase2);
470 printf("Pass phrases do not match. Try again.\n");
471 exit(1);
472 }
473 /* Destroy the other copy. */
474 memset(passphrase2, 0, strlen(passphrase2));
475 xfree(passphrase2);
476 }
477
478 /* Save the file using the new passphrase. */
Ben Lindstromd0fca422001-03-26 13:44:06 +0000479 if (!key_save_private(private, identity_file, passphrase1, comment)) {
Damien Miller95def091999-11-25 00:26:21 +1100480 printf("Saving the key failed: %s: %s.\n",
481 identity_file, strerror(errno));
482 memset(passphrase1, 0, strlen(passphrase1));
483 xfree(passphrase1);
Damien Millereba71ba2000-04-29 23:57:08 +1000484 key_free(private);
Damien Miller95def091999-11-25 00:26:21 +1100485 xfree(comment);
486 exit(1);
487 }
488 /* Destroy the passphrase and the copy of the key in memory. */
489 memset(passphrase1, 0, strlen(passphrase1));
490 xfree(passphrase1);
Damien Millereba71ba2000-04-29 23:57:08 +1000491 key_free(private); /* Destroys contents */
Damien Miller95def091999-11-25 00:26:21 +1100492 xfree(comment);
493
494 printf("Your identification has been saved with the new passphrase.\n");
495 exit(0);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000496}
497
Damien Miller95def091999-11-25 00:26:21 +1100498/*
499 * Change the comment of a private key file.
500 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000501void
502do_change_comment(struct passwd *pw)
503{
Ben Lindstrom5fc62702001-03-09 18:19:24 +0000504 char new_comment[1024], *comment, *passphrase;
Ben Lindstromd0fca422001-03-26 13:44:06 +0000505 Key *private;
506 Key *public;
Damien Miller95def091999-11-25 00:26:21 +1100507 struct stat st;
508 FILE *f;
Ben Lindstrom5fc62702001-03-09 18:19:24 +0000509 int fd;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000510
Damien Miller95def091999-11-25 00:26:21 +1100511 if (!have_identity)
512 ask_filename(pw, "Enter file in which the key is");
Damien Miller95def091999-11-25 00:26:21 +1100513 if (stat(identity_file, &st) < 0) {
514 perror(identity_file);
515 exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000516 }
Ben Lindstromd0fca422001-03-26 13:44:06 +0000517 private = key_load_private(identity_file, "", &comment);
518 if (private == NULL) {
Damien Miller95def091999-11-25 00:26:21 +1100519 if (identity_passphrase)
520 passphrase = xstrdup(identity_passphrase);
521 else if (identity_new_passphrase)
522 passphrase = xstrdup(identity_new_passphrase);
523 else
524 passphrase = read_passphrase("Enter passphrase: ", 1);
525 /* Try to load using the passphrase. */
Ben Lindstromd0fca422001-03-26 13:44:06 +0000526 private = key_load_private(identity_file, passphrase, &comment);
527 if (private == NULL) {
Damien Miller95def091999-11-25 00:26:21 +1100528 memset(passphrase, 0, strlen(passphrase));
529 xfree(passphrase);
530 printf("Bad passphrase.\n");
531 exit(1);
532 }
Ben Lindstromd0fca422001-03-26 13:44:06 +0000533 } else {
534 passphrase = xstrdup("");
Damien Miller95def091999-11-25 00:26:21 +1100535 }
Ben Lindstromd0fca422001-03-26 13:44:06 +0000536 if (private->type != KEY_RSA1) {
537 fprintf(stderr, "Comments are only supported for RSA1 keys.\n");
538 key_free(private);
539 exit(1);
540 }
Damien Miller95def091999-11-25 00:26:21 +1100541 printf("Key now has comment '%s'\n", comment);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000542
Damien Miller95def091999-11-25 00:26:21 +1100543 if (identity_comment) {
544 strlcpy(new_comment, identity_comment, sizeof(new_comment));
545 } else {
546 printf("Enter new comment: ");
547 fflush(stdout);
548 if (!fgets(new_comment, sizeof(new_comment), stdin)) {
549 memset(passphrase, 0, strlen(passphrase));
Damien Millereba71ba2000-04-29 23:57:08 +1000550 key_free(private);
Damien Miller95def091999-11-25 00:26:21 +1100551 exit(1);
552 }
Damien Miller95def091999-11-25 00:26:21 +1100553 if (strchr(new_comment, '\n'))
554 *strchr(new_comment, '\n') = 0;
555 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000556
Damien Miller95def091999-11-25 00:26:21 +1100557 /* Save the file using the new passphrase. */
Ben Lindstromd0fca422001-03-26 13:44:06 +0000558 if (!key_save_private(private, identity_file, passphrase, new_comment)) {
Damien Miller95def091999-11-25 00:26:21 +1100559 printf("Saving the key failed: %s: %s.\n",
560 identity_file, strerror(errno));
561 memset(passphrase, 0, strlen(passphrase));
562 xfree(passphrase);
Damien Millereba71ba2000-04-29 23:57:08 +1000563 key_free(private);
Damien Miller95def091999-11-25 00:26:21 +1100564 xfree(comment);
565 exit(1);
566 }
Damien Miller95def091999-11-25 00:26:21 +1100567 memset(passphrase, 0, strlen(passphrase));
568 xfree(passphrase);
Ben Lindstromd0fca422001-03-26 13:44:06 +0000569 public = key_from_private(private);
Damien Millereba71ba2000-04-29 23:57:08 +1000570 key_free(private);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000571
Damien Miller95def091999-11-25 00:26:21 +1100572 strlcat(identity_file, ".pub", sizeof(identity_file));
Ben Lindstrom5fc62702001-03-09 18:19:24 +0000573 fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
574 if (fd == -1) {
Damien Miller95def091999-11-25 00:26:21 +1100575 printf("Could not save your public key in %s\n", identity_file);
576 exit(1);
577 }
Ben Lindstrom5fc62702001-03-09 18:19:24 +0000578 f = fdopen(fd, "w");
579 if (f == NULL) {
580 printf("fdopen %s failed", identity_file);
581 exit(1);
582 }
Damien Millereba71ba2000-04-29 23:57:08 +1000583 if (!key_write(public, f))
584 fprintf(stderr, "write key failed");
585 key_free(public);
586 fprintf(f, " %s\n", new_comment);
Damien Miller95def091999-11-25 00:26:21 +1100587 fclose(f);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000588
Damien Miller95def091999-11-25 00:26:21 +1100589 xfree(comment);
590
591 printf("The comment in your key file has been changed.\n");
592 exit(0);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000593}
594
Damien Miller431f66b1999-11-21 18:31:57 +1100595void
596usage(void)
597{
Ben Lindstromf1107f52001-03-22 02:05:32 +0000598 printf("Usage: %s [-lBpqxXyc] [-t type] [-b bits] [-f file] [-C comment] "
Ben Lindstromb7c92322001-03-05 05:10:52 +0000599 "[-N new-pass] [-P pass]\n", __progname);
Damien Miller95def091999-11-25 00:26:21 +1100600 exit(1);
Damien Miller431f66b1999-11-21 18:31:57 +1100601}
602
Damien Miller95def091999-11-25 00:26:21 +1100603/*
604 * Main program for key management.
605 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000606int
607main(int ac, char **av)
608{
Damien Miller95def091999-11-25 00:26:21 +1100609 char dotsshdir[16 * 1024], comment[1024], *passphrase1, *passphrase2;
Ben Lindstrom5fc62702001-03-09 18:19:24 +0000610 Key *private, *public;
Damien Miller95def091999-11-25 00:26:21 +1100611 struct passwd *pw;
Ben Lindstrom5fc62702001-03-09 18:19:24 +0000612 int opt, type, fd;
Damien Miller95def091999-11-25 00:26:21 +1100613 struct stat st;
614 FILE *f;
Damien Miller0bc1bd82000-11-13 22:57:25 +1100615
Damien Miller95def091999-11-25 00:26:21 +1100616 extern int optind;
617 extern char *optarg;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000618
Ben Lindstrom49a79c02000-11-17 03:47:20 +0000619 __progname = get_progname(av[0]);
Damien Millerf9b625c2000-07-09 22:42:32 +1000620 init_rng();
Damien Miller60bc5172001-03-19 09:38:15 +1100621 seed_rng();
Damien Millerf9b625c2000-07-09 22:42:32 +1000622
Damien Millerd3a18572000-06-07 19:55:44 +1000623 SSLeay_add_all_algorithms();
Damien Millereba71ba2000-04-29 23:57:08 +1000624
Damien Miller5428f641999-11-25 11:54:57 +1100625 /* we need this for the home * directory. */
Damien Miller95def091999-11-25 00:26:21 +1100626 pw = getpwuid(getuid());
627 if (!pw) {
628 printf("You don't exist, go away!\n");
629 exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000630 }
Damien Millereba71ba2000-04-29 23:57:08 +1000631 if (gethostname(hostname, sizeof(hostname)) < 0) {
632 perror("gethostname");
633 exit(1);
634 }
Damien Miller5428f641999-11-25 11:54:57 +1100635
Ben Lindstrom8fd372b2001-03-12 03:02:17 +0000636 while ((opt = getopt(ac, av, "dqpclBRxXyb:f:t:P:N:C:")) != -1) {
Damien Miller95def091999-11-25 00:26:21 +1100637 switch (opt) {
638 case 'b':
639 bits = atoi(optarg);
640 if (bits < 512 || bits > 32768) {
641 printf("Bits has bad value.\n");
642 exit(1);
643 }
644 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000645
Damien Miller95def091999-11-25 00:26:21 +1100646 case 'l':
647 print_fingerprint = 1;
648 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000649
Ben Lindstrom8fd372b2001-03-12 03:02:17 +0000650 case 'B':
651 print_bubblebabble = 1;
652 break;
653
Damien Miller95def091999-11-25 00:26:21 +1100654 case 'p':
655 change_passphrase = 1;
656 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000657
Damien Miller95def091999-11-25 00:26:21 +1100658 case 'c':
659 change_comment = 1;
660 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000661
Damien Miller95def091999-11-25 00:26:21 +1100662 case 'f':
663 strlcpy(identity_file, optarg, sizeof(identity_file));
664 have_identity = 1;
665 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000666
Damien Miller95def091999-11-25 00:26:21 +1100667 case 'P':
668 identity_passphrase = optarg;
669 break;
670
671 case 'N':
672 identity_new_passphrase = optarg;
673 break;
674
675 case 'C':
676 identity_comment = optarg;
677 break;
678
679 case 'q':
680 quiet = 1;
681 break;
682
Damien Millereba71ba2000-04-29 23:57:08 +1000683 case 'R':
Damien Miller0bc1bd82000-11-13 22:57:25 +1100684 /* unused */
685 exit(0);
Damien Millereba71ba2000-04-29 23:57:08 +1000686 break;
687
688 case 'x':
689 convert_to_ssh2 = 1;
690 break;
691
692 case 'X':
693 convert_from_ssh2 = 1;
694 break;
695
696 case 'y':
697 print_public = 1;
698 break;
699
700 case 'd':
Damien Miller0bc1bd82000-11-13 22:57:25 +1100701 key_type_name = "dsa";
Damien Millereba71ba2000-04-29 23:57:08 +1000702 break;
703
Damien Miller0bc1bd82000-11-13 22:57:25 +1100704 case 't':
705 key_type_name = optarg;
Damien Miller0bc1bd82000-11-13 22:57:25 +1100706 break;
707
Damien Miller95def091999-11-25 00:26:21 +1100708 case '?':
709 default:
710 usage();
711 }
712 }
713 if (optind < ac) {
714 printf("Too many arguments.\n");
715 usage();
716 }
717 if (change_passphrase && change_comment) {
718 printf("Can only have one of -p and -c.\n");
719 usage();
720 }
Ben Lindstrom8fd372b2001-03-12 03:02:17 +0000721 if (print_fingerprint || print_bubblebabble)
Damien Miller95def091999-11-25 00:26:21 +1100722 do_fingerprint(pw);
Damien Miller95def091999-11-25 00:26:21 +1100723 if (change_passphrase)
724 do_change_passphrase(pw);
Damien Miller95def091999-11-25 00:26:21 +1100725 if (change_comment)
726 do_change_comment(pw);
Damien Millereba71ba2000-04-29 23:57:08 +1000727 if (convert_to_ssh2)
728 do_convert_to_ssh2(pw);
729 if (convert_from_ssh2)
730 do_convert_from_ssh2(pw);
731 if (print_public)
732 do_print_public(pw);
Damien Miller95def091999-11-25 00:26:21 +1100733
734 arc4random_stir();
735
Damien Millere39cacc2000-11-29 12:18:44 +1100736 type = key_type_from_name(key_type_name);
737 if (type == KEY_UNSPEC) {
738 fprintf(stderr, "unknown key type %s\n", key_type_name);
739 exit(1);
Damien Millereba71ba2000-04-29 23:57:08 +1000740 }
Damien Miller0bc1bd82000-11-13 22:57:25 +1100741 if (!quiet)
Damien Millere39cacc2000-11-29 12:18:44 +1100742 printf("Generating public/private %s key pair.\n", key_type_name);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100743 private = key_generate(type, bits);
744 if (private == NULL) {
745 fprintf(stderr, "key_generate failed");
746 exit(1);
747 }
748 public = key_from_private(private);
Damien Miller95def091999-11-25 00:26:21 +1100749
750 if (!have_identity)
751 ask_filename(pw, "Enter file in which to save the key");
752
753 /* Create ~/.ssh directory if it doesn\'t already exist. */
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000754 snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, _PATH_SSH_USER_DIR);
Damien Miller95def091999-11-25 00:26:21 +1100755 if (strstr(identity_file, dotsshdir) != NULL &&
756 stat(dotsshdir, &st) < 0) {
Damien Millerbe484b52000-07-15 14:14:16 +1000757 if (mkdir(dotsshdir, 0700) < 0)
Damien Miller95def091999-11-25 00:26:21 +1100758 error("Could not create directory '%s'.", dotsshdir);
759 else if (!quiet)
760 printf("Created directory '%s'.\n", dotsshdir);
761 }
762 /* If the file already exists, ask the user to confirm. */
763 if (stat(identity_file, &st) >= 0) {
764 char yesno[3];
765 printf("%s already exists.\n", identity_file);
766 printf("Overwrite (y/n)? ");
767 fflush(stdout);
768 if (fgets(yesno, sizeof(yesno), stdin) == NULL)
769 exit(1);
770 if (yesno[0] != 'y' && yesno[0] != 'Y')
771 exit(1);
772 }
773 /* Ask for a passphrase (twice). */
774 if (identity_passphrase)
775 passphrase1 = xstrdup(identity_passphrase);
776 else if (identity_new_passphrase)
777 passphrase1 = xstrdup(identity_new_passphrase);
778 else {
779passphrase_again:
780 passphrase1 =
781 read_passphrase("Enter passphrase (empty for no passphrase): ", 1);
782 passphrase2 = read_passphrase("Enter same passphrase again: ", 1);
783 if (strcmp(passphrase1, passphrase2) != 0) {
784 /* The passphrases do not match. Clear them and retry. */
785 memset(passphrase1, 0, strlen(passphrase1));
786 memset(passphrase2, 0, strlen(passphrase2));
787 xfree(passphrase1);
788 xfree(passphrase2);
789 printf("Passphrases do not match. Try again.\n");
790 goto passphrase_again;
791 }
792 /* Clear the other copy of the passphrase. */
793 memset(passphrase2, 0, strlen(passphrase2));
794 xfree(passphrase2);
795 }
796
Damien Miller95def091999-11-25 00:26:21 +1100797 if (identity_comment) {
798 strlcpy(comment, identity_comment, sizeof(comment));
799 } else {
Damien Miller4af51302000-04-16 11:18:38 +1000800 /* Create default commend field for the passphrase. */
Damien Miller95def091999-11-25 00:26:21 +1100801 snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname);
802 }
803
804 /* Save the key with the given passphrase and comment. */
Ben Lindstromd0fca422001-03-26 13:44:06 +0000805 if (!key_save_private(private, identity_file, passphrase1, comment)) {
Damien Miller95def091999-11-25 00:26:21 +1100806 printf("Saving the key failed: %s: %s.\n",
Damien Millereba71ba2000-04-29 23:57:08 +1000807 identity_file, strerror(errno));
Damien Miller95def091999-11-25 00:26:21 +1100808 memset(passphrase1, 0, strlen(passphrase1));
809 xfree(passphrase1);
810 exit(1);
811 }
812 /* Clear the passphrase. */
813 memset(passphrase1, 0, strlen(passphrase1));
814 xfree(passphrase1);
815
816 /* Clear the private key and the random number generator. */
Damien Miller0bc1bd82000-11-13 22:57:25 +1100817 key_free(private);
Damien Miller95def091999-11-25 00:26:21 +1100818 arc4random_stir();
819
820 if (!quiet)
821 printf("Your identification has been saved in %s.\n", identity_file);
822
Damien Miller95def091999-11-25 00:26:21 +1100823 strlcat(identity_file, ".pub", sizeof(identity_file));
Ben Lindstrom5fc62702001-03-09 18:19:24 +0000824 fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
825 if (fd == -1) {
Damien Miller95def091999-11-25 00:26:21 +1100826 printf("Could not save your public key in %s\n", identity_file);
827 exit(1);
828 }
Ben Lindstrom5fc62702001-03-09 18:19:24 +0000829 f = fdopen(fd, "w");
830 if (f == NULL) {
831 printf("fdopen %s failed", identity_file);
832 exit(1);
833 }
Damien Millereba71ba2000-04-29 23:57:08 +1000834 if (!key_write(public, f))
835 fprintf(stderr, "write key failed");
836 fprintf(f, " %s\n", comment);
Damien Miller95def091999-11-25 00:26:21 +1100837 fclose(f);
838
839 if (!quiet) {
Ben Lindstromcfccef92001-03-13 04:57:58 +0000840 char *fp = key_fingerprint(public, SSH_FP_MD5, SSH_FP_HEX);
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");
Ben Lindstromcfccef92001-03-13 04:57:58 +0000844 printf("%s %s\n", fp, comment);
845 xfree(fp);
Damien Miller95def091999-11-25 00:26:21 +1100846 }
Damien Millereba71ba2000-04-29 23:57:08 +1000847
848 key_free(public);
Damien Miller95def091999-11-25 00:26:21 +1100849 exit(0);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000850}