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