]> andersk Git - openssh.git/blob - ssh-keygen.c
- Merged OpenBSD CVS changes:
[openssh.git] / ssh-keygen.c
1 /*
2
3 ssh-keygen.c
4
5 Author: Tatu Ylonen <ylo@cs.hut.fi>
6
7 Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
8                    All rights reserved
9
10 Created: Mon Mar 27 02:26:40 1995 ylo
11
12 Identity and host key generation and maintenance.
13
14 */
15
16 #include "includes.h"
17 RCSID("$Id$");
18
19 #include "rsa.h"
20 #include "ssh.h"
21 #include "xmalloc.h"
22
23 /* Generated private key. */
24 RSA *private_key;
25
26 /* Generated public key. */
27 RSA *public_key;
28
29 /* Number of bits in the RSA key.  This value can be changed on the command
30    line. */
31 int bits = 1024;
32
33 /* Flag indicating that we just want to change the passphrase.  This can be
34    set on the command line. */
35 int change_passphrase = 0;
36
37 /* Flag indicating that we just want to change the comment.  This can be set
38    on the command line. */
39 int change_comment = 0;
40
41 int quiet = 0;
42
43 /* This is set to the identity file name if given on the command line. */
44 char *identity_file = NULL;
45
46 /* This is set to the passphrase if given on the command line. */
47 char *identity_passphrase = NULL;
48
49 /* This is set to the new passphrase if given on the command line. */
50 char *identity_new_passphrase = NULL;
51
52 /* This is set to the new comment if given on the command line. */
53 char *identity_comment = NULL;
54
55 /* Perform changing a passphrase.  The argument is the passwd structure
56    for the current user. */
57
58 void
59 do_change_passphrase(struct passwd *pw)
60 {
61   char buf[1024], *comment;
62   char *old_passphrase, *passphrase1, *passphrase2;
63   struct stat st;
64   RSA *private_key;
65
66   /* Read key file name. */
67   if (identity_file != NULL) {
68       strncpy(buf, identity_file, sizeof(buf));
69       buf[sizeof(buf) - 1] = '\0';
70   } else {
71     printf("Enter file in which the key is ($HOME/%s): ", SSH_CLIENT_IDENTITY);
72     fflush(stdout);
73     if (fgets(buf, sizeof(buf), stdin) == NULL)
74       exit(1);
75     if (strchr(buf, '\n'))
76       *strchr(buf, '\n') = 0;
77     if (strcmp(buf, "") == 0)
78       snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_CLIENT_IDENTITY);
79   }
80
81   /* Check if the file exists. */
82   if (stat(buf, &st) < 0)
83     {
84       perror(buf);
85       exit(1);
86     }
87   
88   /* Try to load the public key from the file the verify that it is
89      readable and of the proper format. */
90   public_key = RSA_new();
91   if (!load_public_key(buf, public_key, NULL))
92     {
93       printf("%s is not a valid key file.\n", buf);
94       exit(1);
95     }
96   /* Clear the public key since we are just about to load the whole file. */
97   RSA_free(public_key);
98
99   /* Try to load the file with empty passphrase. */
100   private_key = RSA_new();
101   if (!load_private_key(buf, "", private_key, &comment)) {
102     /* Read passphrase from the user. */
103     if (identity_passphrase)
104       old_passphrase = xstrdup(identity_passphrase);
105     else
106       old_passphrase = read_passphrase("Enter old passphrase: ", 1);
107     /* Try to load using the passphrase. */
108     if (!load_private_key(buf, old_passphrase, private_key, &comment))
109       {
110         memset(old_passphrase, 0, strlen(old_passphrase));
111         xfree(old_passphrase);
112         printf("Bad passphrase.\n");
113         exit(1);
114       }
115     /* Destroy the passphrase. */
116     memset(old_passphrase, 0, strlen(old_passphrase));
117     xfree(old_passphrase);
118   }
119   printf("Key has comment '%s'\n", comment);
120    
121   /* Ask the new passphrase (twice). */
122   if (identity_new_passphrase)
123     {
124       passphrase1 = xstrdup(identity_new_passphrase);
125       passphrase2 = NULL;
126     }
127   else
128     {
129       passphrase1 = 
130         read_passphrase("Enter new passphrase (empty for no passphrase): ", 1);
131       passphrase2 = read_passphrase("Enter same passphrase again: ", 1);
132
133       /* Verify that they are the same. */
134       if (strcmp(passphrase1, passphrase2) != 0)
135         {
136           memset(passphrase1, 0, strlen(passphrase1));
137           memset(passphrase2, 0, strlen(passphrase2));
138           xfree(passphrase1);
139           xfree(passphrase2);
140           printf("Pass phrases do not match.  Try again.\n");
141           exit(1);
142         }
143       /* Destroy the other copy. */
144       memset(passphrase2, 0, strlen(passphrase2));
145       xfree(passphrase2);
146     }
147
148   /* Save the file using the new passphrase. */
149   if (!save_private_key(buf, passphrase1, private_key, comment))
150     {
151       printf("Saving the key failed: %s: %s.\n",
152              buf, strerror(errno));
153       memset(passphrase1, 0, strlen(passphrase1));
154       xfree(passphrase1);
155       RSA_free(private_key);
156       xfree(comment);
157       exit(1);
158     }
159   /* Destroy the passphrase and the copy of the key in memory. */
160   memset(passphrase1, 0, strlen(passphrase1));
161   xfree(passphrase1);
162   RSA_free(private_key); /* Destroys contents */
163   xfree(comment);
164
165   printf("Your identification has been saved with the new passphrase.\n");
166   exit(0);
167 }
168
169 /* Change the comment of a private key file. */
170
171 void
172 do_change_comment(struct passwd *pw)
173 {
174   char buf[1024], new_comment[1024], *comment;
175   RSA *private_key;
176   char *passphrase;
177   struct stat st;
178   FILE *f;
179   char *tmpbuf;
180
181   /* Read key file name. */
182   if (identity_file)
183     {
184       strncpy(buf, identity_file, sizeof(buf));
185       buf[sizeof(buf) - 1] = '\0';
186     }
187   else
188     {
189       printf("Enter file in which the key is ($HOME/%s): ", 
190              SSH_CLIENT_IDENTITY);
191       fflush(stdout);
192       if (fgets(buf, sizeof(buf), stdin) == NULL)
193         exit(1);
194       if (strchr(buf, '\n'))
195         *strchr(buf, '\n') = 0;
196       if (strcmp(buf, "") == 0)
197         snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_CLIENT_IDENTITY);
198     }
199
200   /* Check if the file exists. */
201   if (stat(buf, &st) < 0)
202     {
203       perror(buf);
204       exit(1);
205     }
206   
207   /* Try to load the public key from the file the verify that it is
208      readable and of the proper format. */
209   public_key = RSA_new();
210   if (!load_public_key(buf, public_key, NULL))
211     {
212       printf("%s is not a valid key file.\n", buf);
213       exit(1);
214     }
215
216   private_key = RSA_new();
217   /* Try to load the file with empty passphrase. */
218   if (load_private_key(buf, "", private_key, &comment))
219     passphrase = xstrdup("");
220   else
221     {
222       /* Read passphrase from the user. */
223       if (identity_passphrase)
224         passphrase = xstrdup(identity_passphrase);
225       else
226         if (identity_new_passphrase)
227           passphrase = xstrdup(identity_new_passphrase);
228         else
229           passphrase = read_passphrase("Enter passphrase: ", 1);
230       /* Try to load using the passphrase. */
231       if (!load_private_key(buf, passphrase, private_key, &comment))
232         {
233           memset(passphrase, 0, strlen(passphrase));
234           xfree(passphrase);
235           printf("Bad passphrase.\n");
236           exit(1);
237         }
238     }
239   printf("Key now has comment '%s'\n", comment);
240
241   if (identity_comment)
242     {
243       strncpy(new_comment, identity_comment, sizeof(new_comment));
244       new_comment[sizeof(new_comment) - 1] = '\0';
245     }
246   else
247     {
248       printf("Enter new comment: ");
249       fflush(stdout);
250       if (!fgets(new_comment, sizeof(new_comment), stdin))
251         {
252           memset(passphrase, 0, strlen(passphrase));
253           RSA_free(private_key);
254           exit(1);
255         }
256       
257       /* Remove terminating newline from comment. */
258       if (strchr(new_comment, '\n'))
259         *strchr(new_comment, '\n') = 0;
260     }
261       
262   /* Save the file using the new passphrase. */
263   if (!save_private_key(buf, passphrase, private_key, new_comment))
264     {
265       printf("Saving the key failed: %s: %s.\n",
266              buf, strerror(errno));
267       memset(passphrase, 0, strlen(passphrase));
268       xfree(passphrase);
269       RSA_free(private_key);
270       xfree(comment);
271       exit(1);
272     }
273
274   /* Destroy the passphrase and the private key in memory. */
275   memset(passphrase, 0, strlen(passphrase));
276   xfree(passphrase);
277   RSA_free(private_key);
278
279   /* Save the public key in text format in a file with the same name but
280      .pub appended. */
281   strcat(buf, ".pub");
282   f = fopen(buf, "w");
283   if (!f)
284     {
285       printf("Could not save your public key in %s\n", buf);
286       exit(1);
287     }
288   fprintf(f, "%d ", BN_num_bits(public_key->n));
289   tmpbuf = BN_bn2dec(public_key->e);
290   fprintf(f, "%s ", tmpbuf);
291   free (tmpbuf);
292   tmpbuf = BN_bn2dec(public_key->n);
293   fprintf(f, "%s %s\n", tmpbuf, new_comment);
294   free (tmpbuf);
295   fclose(f);
296
297   xfree(comment);
298
299   printf("The comment in your key file has been changed.\n");
300   exit(0);
301 }
302
303 /* Main program for key management. */
304
305 int
306 main(int ac, char **av)
307 {
308   char buf[16384], buf2[1024], *passphrase1, *passphrase2;
309   struct passwd *pw;
310   char *tmpbuf;
311   int opt;
312   struct stat st;
313   FILE *f;
314   char hostname[MAXHOSTNAMELEN];
315   extern int optind;
316   extern char *optarg;
317
318   /* check if RSA support exists */
319   if (rsa_alive() == 0) {
320     extern char *__progname;
321
322     fprintf(stderr,
323       "%s: no RSA support in libssl and libcrypto.  See ssl(8).\n",
324       __progname);
325     exit(1);
326   }
327
328   /* Get user\'s passwd structure.  We need this for the home directory. */
329   pw = getpwuid(getuid());
330   if (!pw)
331     {
332       printf("You don't exist, go away!\n");
333       exit(1);
334     }
335
336   /* Create ~/.ssh directory if it doesn\'t already exist. */
337   snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_USER_DIR);
338   if (stat(buf, &st) < 0)
339     if (mkdir(buf, 0755) < 0)
340       error("Could not create directory '%s'.", buf);
341
342   /* Parse command line arguments. */
343   while ((opt = getopt(ac, av, "qpcb:f:P:N:C:")) != EOF)
344     {
345       switch (opt)
346         {
347         case 'b':
348           bits = atoi(optarg);
349           if (bits < 512 || bits > 32768)
350             {
351               printf("Bits has bad value.\n");
352               exit(1);
353             }
354           break;
355
356         case 'p':
357           change_passphrase = 1;
358           break;
359
360         case 'c':
361           change_comment = 1;
362           break;
363
364         case 'f':
365           identity_file = optarg;
366           break;
367           
368         case 'P':
369           identity_passphrase = optarg;
370           break;
371
372         case 'N':
373           identity_new_passphrase = optarg;
374           break;
375
376         case 'C':
377           identity_comment = optarg;
378           break;
379
380         case 'q':
381           quiet = 1;
382           break;
383
384         case '?':
385         default:
386           printf("ssh-keygen version %s\n", SSH_VERSION);
387           printf("Usage: %s [-b bits] [-p] [-c] [-f file] [-P pass] [-N new-pass] [-C comment]\n", av[0]);
388           exit(1);
389         }
390     }
391   if (optind < ac)
392     {
393       printf("Too many arguments.\n");
394       exit(1);
395     }
396   if (change_passphrase && change_comment)
397     {
398       printf("Can only have one of -p and -c.\n");
399       exit(1);
400     }
401
402   /* If the user requested to change the passphrase, do it now.  This
403      function never returns. */
404   if (change_passphrase)
405     do_change_passphrase(pw);
406
407   /* If the user requested to change the comment, do it now.  This function
408      never returns. */
409   if (change_comment)
410     do_change_comment(pw);
411
412   arc4random_stir();
413
414   if (quiet)
415     rsa_set_verbose(0);
416
417   /* Generate the rsa key pair. */
418   private_key = RSA_new();
419   public_key = RSA_new();
420   rsa_generate_key(private_key, public_key, bits);
421
422  ask_file_again:
423
424   /* Ask for a file to save the key in. */
425   if (identity_file)
426     {
427       strncpy(buf, identity_file, sizeof(buf));
428       buf[sizeof(buf) - 1] = '\0';
429     }
430   else
431     {
432       printf("Enter file in which to save the key ($HOME/%s): ", 
433              SSH_CLIENT_IDENTITY);
434       fflush(stdout);
435       if (fgets(buf, sizeof(buf), stdin) == NULL)
436         exit(1);
437       if (strchr(buf, '\n'))
438         *strchr(buf, '\n') = 0;
439       if (strcmp(buf, "") == 0)
440         snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_CLIENT_IDENTITY);
441     }
442
443   /* If the file aready exists, ask the user to confirm. */
444   if (stat(buf, &st) >= 0)
445     {
446       printf("%s already exists.\n", buf);
447       printf("Overwrite (y/n)? ");
448       fflush(stdout);
449       if (fgets(buf2, sizeof(buf2), stdin) == NULL)
450         exit(1);
451       if (buf2[0] != 'y' && buf2[0] != 'Y')
452         exit(1);
453     }
454   
455   /* Ask for a passphrase (twice). */
456   if (identity_passphrase)
457     passphrase1 = xstrdup(identity_passphrase);
458   else
459     if (identity_new_passphrase)
460       passphrase1 = xstrdup(identity_new_passphrase);
461     else
462       {
463       passphrase_again:
464         passphrase1 = 
465           read_passphrase("Enter passphrase (empty for no passphrase): ", 1);
466         passphrase2 = read_passphrase("Enter same passphrase again: ", 1);
467         if (strcmp(passphrase1, passphrase2) != 0)
468           {
469             /* The passphrases do not match.  Clear them and retry. */
470             memset(passphrase1, 0, strlen(passphrase1));
471             memset(passphrase2, 0, strlen(passphrase2));
472             xfree(passphrase1);
473             xfree(passphrase2);
474             printf("Passphrases do not match.  Try again.\n");
475             goto passphrase_again;
476           }
477         /* Clear the other copy of the passphrase. */
478         memset(passphrase2, 0, strlen(passphrase2));
479         xfree(passphrase2);
480       }
481
482   /* Create default commend field for the passphrase.  The user can later
483      edit this field. */
484   if (identity_comment)
485     {
486       strlcpy(buf2, identity_comment, sizeof(buf2));
487     }
488   else
489     {
490       if (gethostname(hostname, sizeof(hostname)) < 0)
491         {
492           perror("gethostname");
493           exit(1);
494         }
495       snprintf(buf2, sizeof buf2, "%s@%s", pw->pw_name, hostname);
496     }
497
498   /* Save the key with the given passphrase and comment. */
499   if (!save_private_key(buf, passphrase1, private_key, buf2))
500     {
501       printf("Saving the key failed: %s: %s.\n",
502              buf, strerror(errno));
503       memset(passphrase1, 0, strlen(passphrase1));
504       xfree(passphrase1);
505       goto ask_file_again;
506     }
507   /* Clear the passphrase. */
508   memset(passphrase1, 0, strlen(passphrase1));
509   xfree(passphrase1);
510
511   /* Clear the private key and the random number generator. */
512   RSA_free(private_key);
513   arc4random_stir();
514
515   if (!quiet)
516     printf("Your identification has been saved in %s.\n", buf);
517
518   /* Display the public key on the screen. */
519   if (!quiet) {
520     printf("Your public key is:\n");
521     printf("%d ", BN_num_bits(public_key->n));
522     tmpbuf = BN_bn2dec(public_key->e);
523     printf("%s ", tmpbuf);
524     free(tmpbuf);
525     tmpbuf = BN_bn2dec(public_key->n);
526     printf("%s %s\n", tmpbuf, buf2);
527     free(tmpbuf);
528   }
529
530   /* Save the public key in text format in a file with the same name but
531      .pub appended. */
532   strcat(buf, ".pub");
533   f = fopen(buf, "w");
534   if (!f)
535     {
536       printf("Could not save your public key in %s\n", buf);
537       exit(1);
538     }
539   fprintf(f, "%d ", BN_num_bits(public_key->n));
540   tmpbuf = BN_bn2dec(public_key->e);
541   fprintf(f, "%s ", tmpbuf);
542   free(tmpbuf);
543   tmpbuf = BN_bn2dec(public_key->n);
544   fprintf(f, "%s %s\n", tmpbuf, buf2);
545   free(tmpbuf);
546   fclose(f);
547
548   if (!quiet)
549     printf("Your public key has been saved in %s\n", buf);
550   
551   exit(0);
552 }
This page took 0.081049 seconds and 5 git commands to generate.