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