Integer type name change.
[silc.git] / apps / silc / clientutil.c
1 /*
2
3   client.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2000 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* $Id$ */
21
22 #include "clientincludes.h"
23
24 /* Routine used to print lines to window. This can split the
25    line neatly if a word would overlap the line. */
26
27 void silc_print_to_window(WINDOW *win, char *message)
28 {
29   int str_len, len;
30
31   str_len = strlen(message);
32  
33   if (str_len > COLS - 1) {
34     /* Split overlapping words to next line */
35     /* XXX In principal this is wrong as this modifies the original
36        string as it replaces the last ' ' with '\n'. This could be done
37        with little more work so that it would not replace anything. */
38     len = COLS - 1;
39     while (1) {
40
41       while (len && message[len] != ' ')
42         len--;
43
44       if (!len)
45         break;
46
47       message[len] = '\n';
48       len += COLS - 1;
49       if (len > str_len)
50         break;
51     }
52   }
53
54   wprintw(win, "%s", message);
55   wrefresh(win);
56 }
57
58 /* Prints message to the screen. This is used to print the messages
59    user is typed and message that came on channels. */
60
61 void silc_print(SilcClient client, char *msg, ...)
62 {
63   va_list vp;
64   char message[2048];
65   SilcClientInternal app = client->application;
66   
67   memset(message, 0, sizeof(message));
68   strncat(message, "\n ", 2);
69
70   va_start(vp, msg);
71   vsprintf(message + 1, msg, vp);
72   va_end(vp);
73   
74   /* Print the message */
75   silc_print_to_window(app->screen->output_win[0], message);
76 }
77
78 /* Returns user's mail path */
79
80 char *silc_get_mail_path()
81 {
82   char pathbuf[MAXPATHLEN];
83   char *path;
84
85 #ifndef _PATH_MAILDIR
86 #define _PATH_MAILDIR "/var/mail"
87 #endif
88   
89   path = getenv("MAIL");
90   if (path) {
91     strncpy(pathbuf, path, strlen(path));
92   } else {
93     strcpy(pathbuf, _PATH_MAILDIR);
94     strcat(pathbuf, "/");
95     strcat(pathbuf, silc_get_username());
96   }
97
98   return strdup(pathbuf);
99 }
100
101 /* gets the number of the user's mails, if possible */
102
103 int silc_get_number_of_emails()
104 {
105   FILE *tl;
106   int num = 0;
107   char *filename;
108   char data[1024];
109   
110   filename = silc_get_mail_path();
111   
112   tl = fopen(filename, "r");
113   if (!tl) {
114     fprintf(stderr, "Couldn't open mail file (%s).\n", filename);
115   } else {
116     while((fscanf(tl, "%s", data)) != EOF) { 
117       if(!strcmp(data, "From:"))
118         num++;
119     }
120     
121     fclose(tl);
122   }
123   
124   return num;
125 }
126
127 /* Returns time til next minute changes. Used to update the clock when
128    needed. */
129
130 int silc_client_time_til_next_min()
131 {
132   time_t curtime;
133   struct tm *min;
134   
135   curtime = time(0);
136   min = localtime(&curtime);
137   
138   return 60 - min->tm_sec;
139 }
140
141 /* Asks yes/no from user on the input line. Returns TRUE on "yes" and
142    FALSE on "no". */
143
144 int silc_client_ask_yes_no(SilcClient client, char *prompt)
145 {
146   SilcClientInternal app = (SilcClientInternal)client->application;
147   char answer[4];
148   int ret;
149
150  again:
151   silc_screen_input_reset(app->screen);
152
153   /* Print prompt */
154   wattroff(app->screen->input_win, A_INVIS);
155   silc_screen_input_print_prompt(app->screen, prompt);
156
157   /* Get string */
158   memset(answer, 0, sizeof(answer));
159   echo();
160   wgetnstr(app->screen->input_win, answer, sizeof(answer));
161   if (!strncasecmp(answer, "yes", strlen(answer)) ||
162       !strncasecmp(answer, "y", strlen(answer))) {
163     ret = TRUE;
164   } else if (!strncasecmp(answer, "no", strlen(answer)) ||
165              !strncasecmp(answer, "n", strlen(answer))) {
166     ret = FALSE;
167   } else {
168     silc_say(client, app->conn, "Type yes or no");
169     goto again;
170   }
171   noecho();
172
173   silc_screen_input_reset(app->screen);
174
175   return ret;
176 }
177
178 /* Lists supported (builtin) ciphers */
179
180 void silc_client_list_ciphers()
181 {
182   char *ciphers = silc_cipher_get_supported();
183   fprintf(stdout, "%s\n", ciphers);
184   silc_free(ciphers);
185 }
186
187 /* Lists supported (builtin) hash functions */
188
189 void silc_client_list_hash_funcs()
190 {
191   char *hash = silc_hash_get_supported();
192   fprintf(stdout, "%s\n", hash);
193   silc_free(hash);
194 }
195
196 /* Lists supported PKCS algorithms */
197
198 void silc_client_list_pkcs()
199 {
200   char *pkcs = silc_pkcs_get_supported();
201   fprintf(stdout, "%s\n", pkcs);
202   silc_free(pkcs);
203 }
204
205 /* Displays input prompt on command line and takes input data from user */
206
207 char *silc_client_get_input(const char *prompt)
208 {
209   char input[2048];
210   int fd;
211
212   fd = open("/dev/tty", O_RDONLY);
213   if (fd < 0) {
214     fprintf(stderr, "silc: %s\n", strerror(errno));
215     return NULL;
216   }
217
218   memset(input, 0, sizeof(input));
219
220   printf("%s", prompt);
221   fflush(stdout);
222
223   if ((read(fd, input, sizeof(input))) < 0) {
224     fprintf(stderr, "silc: %s\n", strerror(errno));
225     return NULL;
226   }
227
228   if (strlen(input) <= 1)
229     return NULL;
230
231   if (strchr(input, '\n'))
232     *strchr(input, '\n') = '\0';
233
234   return strdup(input);
235 }
236
237 /* Displays prompt on command line and takes passphrase with echo 
238    off from user. */
239
240 char *silc_client_get_passphrase(const char *prompt)
241 {
242 #if 0
243   char input[2048];
244   char *ret;
245   int fd;
246   struct termios to;
247   struct termios to_old;
248
249   fd = open("/dev/tty", O_RDONLY);
250   if (fd < 0) {
251     fprintf(stderr, "silc: %s\n", strerror(errno));
252     return NULL;
253   }
254
255   signal(SIGINT, SIG_IGN);
256
257   /* Get terminal info */
258   tcgetattr(fd, &to);
259   to_old = to;
260
261   /* Echo OFF */
262   to.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
263   tcsetattr(fd, TCSANOW, &to);
264
265   memset(input, 0, sizeof(input));
266
267   printf("%s", prompt);
268   fflush(stdout);
269
270   if ((read(fd, input, sizeof(input))) < 0) {
271     fprintf(stderr, "silc: %s\n", strerror(errno));
272     return NULL;
273   }
274
275   if (strlen(input) <= 1) {
276     tcsetattr(fd, TCSANOW, &to_old);
277     return NULL;
278   }
279
280   if (strchr(input, '\n'))
281     *strchr(input, '\n') = '\0';
282
283   /* Restore old terminfo */
284   tcsetattr(fd, TCSANOW, &to_old);
285   signal(SIGINT, SIG_DFL);
286
287   ret = silc_calloc(strlen(input), sizeof(char));
288   memcpy(ret, input, strlen(input));
289   memset(input, 0, sizeof(input));
290   return ret;
291 #else
292   return NULL;
293 #endif
294 }
295
296 /* Returns identifier string for public key generation. */
297
298 char *silc_client_create_identifier()
299 {
300   char *username = NULL, *realname = NULL;
301   char hostname[256], email[256];
302   
303   /* Get realname */
304   realname = silc_get_real_name();
305
306   /* Get hostname */
307   memset(hostname, 0, sizeof(hostname));
308   gethostname(hostname, sizeof(hostname));
309
310   /* Get username (mandatory) */
311   username = silc_get_username();
312   if (!username)
313     return NULL;
314
315   /* Create default email address, whether it is right or not */
316   snprintf(email, sizeof(email), "%s@%s", username, hostname);
317
318   return silc_pkcs_encode_identifier(username, hostname, realname, email,
319                                      NULL, NULL);
320 }
321
322 /* Creates new public key and private key pair. This is used only
323    when user wants to create new key pair from command line. */
324
325 int silc_client_create_key_pair(char *pkcs_name, int bits,
326                                 char *public_key, char *private_key,
327                                 char *identifier, 
328                                 SilcPublicKey *ret_pub_key,
329                                 SilcPrivateKey *ret_prv_key)
330 {
331   SilcPKCS pkcs;
332   SilcPublicKey pub_key;
333   SilcPrivateKey prv_key;
334   SilcRng rng;
335   unsigned char *key;
336   SilcUInt32 key_len;
337   char line[256];
338   char *pkfile = NULL, *prvfile = NULL;
339
340   if (!pkcs_name || !public_key || !private_key)
341     printf("\
342 New pair of keys will be created.  Please, answer to following questions.\n\
343 ");
344
345   if (!pkcs_name) {
346   again_name:
347     pkcs_name = 
348       silc_client_get_input("PKCS name (l to list names) [rsa]: ");
349     if (!pkcs_name)
350       pkcs_name = strdup("rsa");
351
352     if (*pkcs_name == 'l' || *pkcs_name == 'L') {
353       silc_client_list_pkcs();
354       silc_free(pkcs_name);
355       goto again_name;
356     }
357   }
358
359   if (!silc_pkcs_is_supported(pkcs_name)) {
360     fprintf(stderr, "Unknown PKCS `%s'", pkcs_name);
361     return FALSE;
362   }
363
364   if (!bits) {
365     char *length = NULL;
366     length = 
367       silc_client_get_input("Key length in bits [1024]: ");
368     if (!length)
369       bits = 1024;
370     else
371       bits = atoi(length);
372   }
373
374   if (!identifier) {
375     char *def = silc_client_create_identifier();
376
377     memset(line, 0, sizeof(line));
378     if (def)
379       snprintf(line, sizeof(line), "Identifier [%s]: ", def);
380     else
381       snprintf(line, sizeof(line),
382                "Identifier (eg. UN=jon, HN=jon.dummy.com, "
383                "RN=Jon Johnson, E=jon@dummy.com): ");
384
385     while (!identifier) {
386       identifier = silc_client_get_input(line);
387       if (!identifier && def)
388         identifier = strdup(def);
389     }
390
391     if (def)
392       silc_free(def);
393   }
394
395   rng = silc_rng_alloc();
396   silc_rng_init(rng);
397   silc_rng_global_init(rng);
398
399   if (!public_key) {
400     memset(line, 0, sizeof(line));
401     snprintf(line, sizeof(line), "Public key filename [%s] ", 
402              SILC_CLIENT_PUBLIC_KEY_NAME);
403     pkfile = silc_client_get_input(line);
404     if (!pkfile)
405       pkfile = SILC_CLIENT_PUBLIC_KEY_NAME;
406   } else {
407     pkfile = public_key;
408   }
409
410   if (!private_key) {
411     memset(line, 0, sizeof(line));
412     snprintf(line, sizeof(line), "Public key filename [%s] ", 
413              SILC_CLIENT_PRIVATE_KEY_NAME);
414     prvfile = silc_client_get_input(line);
415     if (!prvfile)
416       prvfile = SILC_CLIENT_PRIVATE_KEY_NAME;
417   } else {
418     prvfile = private_key;
419   }
420
421   /* Generate keys */
422   silc_pkcs_alloc(pkcs_name, &pkcs);
423   pkcs->pkcs->init(pkcs->context, bits, rng);
424
425   /* Save public key into file */
426   key = silc_pkcs_get_public_key(pkcs, &key_len);
427   pub_key = silc_pkcs_public_key_alloc(pkcs->pkcs->name, identifier,
428                                        key, key_len);
429   silc_pkcs_save_public_key(pkfile, pub_key, SILC_PKCS_FILE_PEM);
430   if (ret_pub_key)
431     *ret_pub_key = pub_key;
432
433   memset(key, 0, sizeof(key_len));
434   silc_free(key);
435
436   /* Save private key into file */
437   key = silc_pkcs_get_private_key(pkcs, &key_len);
438   prv_key = silc_pkcs_private_key_alloc(pkcs->pkcs->name, key, key_len);
439
440   silc_pkcs_save_private_key(prvfile, prv_key, NULL, SILC_PKCS_FILE_BIN);
441   if (ret_prv_key)
442     *ret_prv_key = prv_key;
443
444   printf("Public key has been saved into `%s'.\n", pkfile);
445   printf("Private key has been saved into `%s'.\n", prvfile);
446   printf("Press <Enter> to continue...\n");
447   getchar();
448
449   memset(key, 0, sizeof(key_len));
450   silc_free(key);
451
452   silc_rng_free(rng);
453   silc_pkcs_free(pkcs);
454
455   return TRUE;
456 }
457
458 /* This checks stats for various SILC files and directories. First it 
459    checks if ~/.silc directory exist and is owned by the correct user. If 
460    it doesn't exist, it will create the directory. After that it checks if
461    user's Public and Private key files exists and that they aren't expired.
462    If they doesn't exist or they are expired, they will be (re)created
463    after return. */
464
465 int silc_client_check_silc_dir()
466 {
467   char filename[256], file_public_key[256], file_private_key[256];
468   char servfilename[256], clientfilename[256];
469   char *identifier;
470   struct stat st;
471   struct passwd *pw;
472   int firstime = FALSE;
473   time_t curtime, modtime;
474
475   SILC_LOG_DEBUG(("Checking ~./silc directory"));
476
477   memset(filename, 0, sizeof(filename));
478   memset(file_public_key, 0, sizeof(file_public_key));
479   memset(file_private_key, 0, sizeof(file_private_key));
480
481   pw = getpwuid(getuid());
482   if (!pw) {
483     fprintf(stderr, "silc: %s\n", strerror(errno));
484     return FALSE;
485   }
486
487   identifier = silc_client_create_identifier();
488
489   /* We'll take home path from /etc/passwd file to be sure. */
490   snprintf(filename, sizeof(filename) - 1, "%s/.silc/", pw->pw_dir);
491   snprintf(servfilename, sizeof(servfilename) - 1, "%s/.silc/serverkeys", 
492            pw->pw_dir);
493   snprintf(clientfilename, sizeof(clientfilename) - 1, "%s/.silc/clientkeys", 
494            pw->pw_dir);
495
496   /*
497    * Check ~/.silc directory
498    */
499   if ((stat(filename, &st)) == -1) {
500     /* If dir doesn't exist */
501     if (errno == ENOENT) {
502       if (pw->pw_uid == geteuid()) {
503         if ((mkdir(filename, 0755)) == -1) {
504           fprintf(stderr, "Couldn't create `%s' directory\n", filename);
505           return FALSE;
506         }
507
508         /* Directory was created. First time running SILC */
509         firstime = TRUE;
510       } else {
511         fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
512                 filename);
513         return FALSE;
514       }
515     } else {
516       fprintf(stderr, "%s\n", strerror(errno));
517       return FALSE;
518     }
519   } else {
520     
521     /* Check the owner of the dir */
522     if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { 
523       fprintf(stderr, "You don't seem to own `%s' directory\n",
524               filename);
525       return FALSE;
526     }
527     
528     /* Check the permissions of the dir */
529     if ((st.st_mode & 0777) != 0755) {
530       if ((chmod(filename, 0755)) == -1) {
531         fprintf(stderr, "Permissions for `%s' directory must be 0755\n", 
532                 filename);
533         return FALSE;
534       }
535     }
536   }
537
538   /*
539    * Check ~./silc/serverkeys directory
540    */
541   if ((stat(servfilename, &st)) == -1) {
542     /* If dir doesn't exist */
543     if (errno == ENOENT) {
544       if (pw->pw_uid == geteuid()) {
545         if ((mkdir(servfilename, 0755)) == -1) {
546           fprintf(stderr, "Couldn't create `%s' directory\n", servfilename);
547           return FALSE;
548         }
549       } else {
550         fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
551                 servfilename);
552         return FALSE;
553       }
554     } else {
555       fprintf(stderr, "%s\n", strerror(errno));
556       return FALSE;
557     }
558   }
559   
560   /*
561    * Check ~./silc/clientkeys directory
562    */
563   if ((stat(clientfilename, &st)) == -1) {
564     /* If dir doesn't exist */
565     if (errno == ENOENT) {
566       if (pw->pw_uid == geteuid()) {
567         if ((mkdir(clientfilename, 0755)) == -1) {
568           fprintf(stderr, "Couldn't create `%s' directory\n", clientfilename);
569           return FALSE;
570         }
571       } else {
572         fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
573                 clientfilename);
574         return FALSE;
575       }
576     } else {
577       fprintf(stderr, "%s\n", strerror(errno));
578       return FALSE;
579     }
580   }
581   
582   /*
583    * Check Public and Private keys
584    */
585   snprintf(file_public_key, sizeof(file_public_key) - 1, "%s%s", 
586            filename, SILC_CLIENT_PUBLIC_KEY_NAME);
587   snprintf(file_private_key, sizeof(file_private_key) - 1, "%s%s", 
588            filename, SILC_CLIENT_PRIVATE_KEY_NAME);
589   
590   /* If running SILC first time */
591   if (firstime) {
592     fprintf(stdout, "Running SILC for the first time\n");
593     silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
594                                 SILC_CLIENT_DEF_PKCS_LEN,
595                                 file_public_key, file_private_key, 
596                                 identifier, NULL, NULL);
597     return TRUE;
598   }
599   
600   if ((stat(file_public_key, &st)) == -1) {
601     /* If file doesn't exist */
602     if (errno == ENOENT) {
603       fprintf(stdout, "Your public key doesn't exist\n");
604       silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
605                                   SILC_CLIENT_DEF_PKCS_LEN,
606                                   file_public_key, 
607                                   file_private_key, identifier, NULL, NULL);
608     } else {
609       fprintf(stderr, "%s\n", strerror(errno));
610       return FALSE;
611     }
612   }
613
614   if ((stat(file_private_key, &st)) == -1) {
615     /* If file doesn't exist */
616     if (errno == ENOENT) {
617       fprintf(stdout, "Your private key doesn't exist\n");
618       silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
619                                   SILC_CLIENT_DEF_PKCS_LEN,
620                                   file_public_key, 
621                                   file_private_key, identifier, NULL, NULL);
622     } else {
623       fprintf(stderr, "%s\n", strerror(errno));
624       return FALSE;
625     }
626   }
627     
628   /* Check the owner of the public key */
629   if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { 
630     fprintf(stderr, "You don't seem to own your public key!?\n");
631     return FALSE;
632   }
633   
634   /* Check the owner of the private key */
635   if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { 
636     fprintf(stderr, "You don't seem to own your private key!?\n");
637     return FALSE;
638   }
639     
640   /* Check the permissions for the private key */
641   if ((st.st_mode & 0777) != 0600) {
642     fprintf(stderr, "Wrong permissions in your private key file `%s'!\n"
643             "Trying to change them ... ", file_private_key);
644     if ((chmod(file_private_key, 0600)) == -1) {
645       fprintf(stderr,
646               "Failed to change permissions for private key file!\n" 
647               "Permissions for your private key file must be 0600.\n");
648       return FALSE;
649     }
650     fprintf(stderr, "Done.\n\n");
651   }
652
653   /* See if the key has expired. */
654   modtime = st.st_mtime;        /* last modified */
655   curtime = time(0) - modtime;
656     
657   /* 86400 is seconds in a day. */
658   if (curtime >= (86400 * SILC_CLIENT_KEY_EXPIRES)) {
659     fprintf(stdout, 
660             "--------------------------------------------------\n"
661             "Your private key has expired and needs to be\n" 
662             "recreated.  This will be done automatically now.\n"
663             "Your new key will expire in %d days from today.\n"
664             "--------------------------------------------------\n",
665             SILC_CLIENT_KEY_EXPIRES);
666
667     silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
668                                 SILC_CLIENT_DEF_PKCS_LEN,
669                                 file_public_key, 
670                                 file_private_key, identifier, NULL, NULL);
671   }
672   
673   if (identifier)
674     silc_free(identifier);
675
676   return TRUE;
677 }
678
679 /* Loads public and private key from files. */
680
681 int silc_client_load_keys(SilcClient client)
682 {
683   char filename[256];
684   struct passwd *pw;
685
686   SILC_LOG_DEBUG(("Loading public and private keys"));
687
688   pw = getpwuid(getuid());
689   if (!pw)
690     return FALSE;
691
692   memset(filename, 0, sizeof(filename));
693   snprintf(filename, sizeof(filename) - 1, "%s/.silc/%s", 
694            pw->pw_dir, SILC_CLIENT_PRIVATE_KEY_NAME);
695
696   if (silc_pkcs_load_private_key(filename, &client->private_key,
697                                  SILC_PKCS_FILE_BIN) == FALSE)
698     if (silc_pkcs_load_private_key(filename, &client->private_key,
699                                    SILC_PKCS_FILE_PEM) == FALSE)
700       return FALSE;
701
702   memset(filename, 0, sizeof(filename));
703   snprintf(filename, sizeof(filename) - 1, "%s/.silc/%s", 
704            pw->pw_dir, SILC_CLIENT_PUBLIC_KEY_NAME);
705
706   if (silc_pkcs_load_public_key(filename, &client->public_key,
707                                 SILC_PKCS_FILE_PEM) == FALSE)
708     if (silc_pkcs_load_public_key(filename, &client->public_key,
709                                   SILC_PKCS_FILE_BIN) == FALSE)
710       return FALSE;
711
712   return TRUE;
713 }
714
715 /* Dumps the public key on screen. Used from the command line option. */
716
717 int silc_client_show_key(char *keyfile)
718 {
719   SilcPublicKey public_key;
720   SilcPublicKeyIdentifier ident;
721   char *fingerprint;
722   unsigned char *pk;
723   SilcUInt32 pk_len;
724   SilcPKCS pkcs;
725   int key_len = 0;
726
727   if (silc_pkcs_load_public_key(keyfile, &public_key,
728                                 SILC_PKCS_FILE_PEM) == FALSE)
729     if (silc_pkcs_load_public_key(keyfile, &public_key,
730                                   SILC_PKCS_FILE_BIN) == FALSE) {
731       fprintf(stderr, "Could not load public key file `%s'\n", keyfile);
732       return FALSE;
733     }
734
735   ident = silc_pkcs_decode_identifier(public_key->identifier);
736
737   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
738   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
739
740   if (silc_pkcs_alloc(public_key->name, &pkcs)) {
741     key_len = silc_pkcs_public_key_set(pkcs, public_key);
742     silc_pkcs_free(pkcs);
743   }
744
745   printf("Public key file    : %s\n", keyfile);
746   printf("Algorithm          : %s\n", public_key->name);
747   if (key_len)
748     printf("Key length (bits)  : %d\n", key_len);
749   if (ident->realname)
750     printf("Real name          : %s\n", ident->realname);
751   if (ident->username)
752     printf("Username           : %s\n", ident->username);
753   if (ident->host)
754     printf("Hostname           : %s\n", ident->host);
755   if (ident->email)
756     printf("Email              : %s\n", ident->email);
757   if (ident->org)
758     printf("Organization       : %s\n", ident->org);
759   if (ident->country)
760     printf("Country            : %s\n", ident->country);
761   printf("Fingerprint (SHA1) : %s\n", fingerprint); 
762
763   fflush(stdout);
764
765   silc_free(fingerprint);
766   silc_free(pk);
767   silc_pkcs_public_key_free(public_key);
768   silc_pkcs_free_identifier(ident);
769
770   return TRUE;
771 }