Added support for verifying incoming public keys from user.
[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 /*
21  * $Id$
22  * $Log$
23  * Revision 1.4  2000/07/10 05:40:05  priikone
24  *      Added support for verifying incoming public keys from user.
25  *      Shows fingerprint of the public key now plus other changes.
26  *
27  * Revision 1.3  2000/07/07 06:53:45  priikone
28  *      Added support for server public key verification.
29  *
30  * Revision 1.2  2000/07/05 06:11:00  priikone
31  *      Added ~./silc directory checking, autoloading of keys and
32  *      tweaked the key pair generation function.
33  *
34  * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
35  *      Imported from internal CVS/Added Log headers.
36  *
37  *
38  */
39
40 #include "clientincludes.h"
41
42 /* Internal routine used to print lines to window. This can split the
43    line neatly if a word would overlap the line. */
44
45 static void silc_print_to_window(WINDOW *win, char *message)
46 {
47   int str_len, len;
48
49   str_len = strlen(message);
50  
51   if (str_len > COLS - 1) {
52     /* Split overlapping words to next line */
53     /* XXX In principal this is wrong as this modifies the original
54        string as it replaces the last ' ' with '\n'. This could be done
55        with little more work so that it would not replace anything. */
56     len = COLS - 1;
57     while (1) {
58
59       while (len && message[len] != ' ')
60         len--;
61
62       if (!len)
63         break;
64
65       message[len] = '\n';
66       len += COLS - 1;
67       if (len > str_len)
68         break;
69     }
70   }
71
72   wprintw(win, "%s", message);
73   wrefresh(win);
74 }
75
76 /* Prints a message with three star (*) sign before the actual message
77    on the current output window. This is used to print command outputs
78    and error messages. */
79 /* XXX Change to accept SilcClientWindow and use output window 
80    from there (the pointer to the output window must be added to the
81    SilcClientWindow object. */
82
83 void silc_say(SilcClient client, char *msg, ...)
84 {
85   va_list vp;
86   char message[1024];
87   
88   memset(message, 0, sizeof(message));
89   strncat(message, "\n***  ", 5);
90
91   va_start(vp, msg);
92   vsprintf(message + 5, msg, vp);
93   va_end(vp);
94   
95   /* Print the message */
96   silc_print_to_window(client->screen->output_win[0], message);
97 }
98
99 /* Prints message to the screen. This is used to print the messages
100    user is typed and message that came on channels. */
101
102 void silc_print(SilcClient client, char *msg, ...)
103 {
104   va_list vp;
105   char message[1024];
106   
107   memset(message, 0, sizeof(message));
108   strncat(message, "\n ", 2);
109
110   va_start(vp, msg);
111   vsprintf(message + 1, msg, vp);
112   va_end(vp);
113   
114   /* Print the message */
115   silc_print_to_window(client->screen->output_win[0], message);
116 }
117
118 /* Returns user's mail path */
119
120 char *silc_get_mail_path()
121 {
122   char pathbuf[MAXPATHLEN];
123   char *path;
124   
125   if ((path = (char *)getenv("MAIL")) != 0) {
126     strncpy(pathbuf, path, strlen(path));
127   } else {
128     strcpy(pathbuf, _PATH_MAILDIR);
129     strcat(pathbuf, "/");
130     strcat(pathbuf, silc_get_username());
131   }
132
133   return strdup(pathbuf);
134 }
135
136 /* gets the number of the user's mails, if possible */
137
138 int silc_get_number_of_emails()
139 {
140   FILE *tl;
141   int num = 0;
142   char *filename;
143   char data[1024];
144   
145   filename = silc_get_mail_path();
146   
147   tl = fopen(filename, "r");
148   if (!tl) {
149     fprintf(stderr, "Couldn't open mail file (%s).\n", filename);
150   } else {
151     while((fscanf(tl, "%s", data)) != EOF) { 
152       if(!strcmp(data, "Subject:"))
153         num++;
154     }
155     
156     fclose(tl);
157   }
158   
159   return num;
160 }
161
162 /* Returns the username of the user. If the global variable LOGNAME
163    does not exists we will get the name from the password file. */
164
165 char *silc_get_username()
166 {
167   char *logname = NULL;
168   
169   logname = strdup(getenv("LOGNAME"));
170   if (!logname) {
171     logname = getlogin();
172     if (!logname) {
173       struct passwd *pw;
174
175       pw = getpwuid(getuid());
176       if (!pw) {
177         fprintf(stderr, "silc_get_username: %s\n", strerror(errno));
178         return NULL;
179       }
180       
181       logname = strdup(pw->pw_name);
182     }
183   }
184   
185   return logname;
186 }                          
187
188 /* Returns the real name of ther user. */
189
190 char *silc_get_real_name()
191 {
192   char *realname = NULL;
193   struct passwd *pw;
194     
195   pw = getpwuid(getuid());
196   if (!pw) {
197     fprintf(stderr, "silc_get_username: %s\n", strerror(errno));
198     return NULL;
199   }
200
201   if (strchr(pw->pw_gecos, ','))
202     *strchr(pw->pw_gecos, ',') = 0;
203
204   realname = strdup(pw->pw_gecos);
205
206   return realname;
207 }
208
209 /* Returns time til next minute changes. Used to update the clock when
210    needed. */
211
212 int silc_client_time_til_next_min()
213 {
214   time_t curtime;
215   struct tm *min;
216   
217   curtime = time(0);
218   min = localtime(&curtime);
219   
220   return 60 - min->tm_sec;
221 }
222
223 /* Asks passphrase from user on the input line. */
224
225 char *silc_client_ask_passphrase(SilcClient client)
226 {
227   char pass1[256], pass2[256];
228   char *ret;
229   int try = 3;
230
231   while(try) {
232
233     /* Print prompt */
234     wattroff(client->screen->input_win, A_INVIS);
235     silc_screen_input_print_prompt(client->screen, "Passphrase: ");
236     wattron(client->screen->input_win, A_INVIS);
237     
238     /* Get string */
239     memset(pass1, 0, sizeof(pass1));
240     wgetnstr(client->screen->input_win, pass1, sizeof(pass1));
241     
242     /* Print retype prompt */
243     wattroff(client->screen->input_win, A_INVIS);
244     silc_screen_input_print_prompt(client->screen, "Retype passphrase: ");
245     wattron(client->screen->input_win, A_INVIS);
246     
247     /* Get string */
248     memset(pass2, 0, sizeof(pass2));
249     wgetnstr(client->screen->input_win, pass2, sizeof(pass2));
250
251     if (!strncmp(pass1, pass2, strlen(pass2)))
252       break;
253
254     try--;
255   }
256
257   ret = silc_calloc(strlen(pass1), sizeof(char));
258   memcpy(ret, pass1, strlen(pass1));
259
260   memset(pass1, 0, sizeof(pass1));
261   memset(pass2, 0, sizeof(pass2));
262
263   wattroff(client->screen->input_win, A_INVIS);
264   silc_screen_input_reset(client->screen);
265
266   return ret;
267 }
268
269 /* Asks yes/no from user on the input line. Returns TRUE on "yes" and
270    FALSE on "no". */
271
272 int silc_client_ask_yes_no(SilcClient client, char *prompt)
273 {
274   char answer[4];
275   int ret;
276
277  again:
278   silc_screen_input_reset(client->screen);
279
280   /* Print prompt */
281   wattroff(client->screen->input_win, A_INVIS);
282   silc_screen_input_print_prompt(client->screen, prompt);
283
284   /* Get string */
285   memset(answer, 0, sizeof(answer));
286   echo();
287   wgetnstr(client->screen->input_win, answer, sizeof(answer));
288   if (!strncasecmp(answer, "yes", strlen(answer)) ||
289       !strncasecmp(answer, "y", strlen(answer))) {
290     ret = TRUE;
291   } else if (!strncasecmp(answer, "no", strlen(answer)) ||
292              !strncasecmp(answer, "n", strlen(answer))) {
293     ret = FALSE;
294   } else {
295     silc_say(client, "Type yes or no");
296     goto again;
297   }
298   noecho();
299
300   silc_screen_input_reset(client->screen);
301
302   return ret;
303 }
304
305 /* Lists supported (builtin) ciphers */
306
307 void silc_client_list_ciphers()
308 {
309
310 }
311
312 /* Lists supported (builtin) hash functions */
313
314 void silc_client_list_hash_funcs()
315 {
316
317 }
318
319 /* Lists supported PKCS algorithms */
320
321 void silc_client_list_pkcs()
322 {
323
324 }
325
326 /* Displays input prompt on command line and takes input data from user */
327
328 char *silc_client_get_input(const char *prompt)
329 {
330   char input[2048];
331   int fd;
332
333   fd = open("/dev/tty", O_RDONLY);
334   if (fd < 0) {
335     fprintf(stderr, "silc: %s\n", strerror(errno));
336     return NULL;
337   }
338
339   memset(input, 0, sizeof(input));
340
341   printf("%s", prompt);
342   fflush(stdout);
343
344   if ((read(fd, input, sizeof(input))) < 0) {
345     fprintf(stderr, "silc: %s\n", strerror(errno));
346     return NULL;
347   }
348
349   if (strlen(input) <= 1)
350     return NULL;
351
352   if (strchr(input, '\n'))
353     *strchr(input, '\n') = '\0';
354
355   return strdup(input);
356 }
357
358 /* Displays prompt on command line and takes passphrase with echo 
359    off from user. */
360
361 char *silc_client_get_passphrase(const char *prompt)
362 {
363 #if 0
364   char input[2048];
365   char *ret;
366   int fd;
367   struct termios to;
368   struct termios to_old;
369
370   fd = open("/dev/tty", O_RDONLY);
371   if (fd < 0) {
372     fprintf(stderr, "silc: %s\n", strerror(errno));
373     return NULL;
374   }
375
376   signal(SIGINT, SIG_IGN);
377
378   /* Get terminal info */
379   tcgetattr(fd, &to);
380   to_old = to;
381
382   /* Echo OFF */
383   to.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
384   tcsetattr(fd, TCSANOW, &to);
385
386   memset(input, 0, sizeof(input));
387
388   printf("%s", prompt);
389   fflush(stdout);
390
391   if ((read(fd, input, sizeof(input))) < 0) {
392     fprintf(stderr, "silc: %s\n", strerror(errno));
393     return NULL;
394   }
395
396   if (strlen(input) <= 1) {
397     tcsetattr(fd, TCSANOW, &to_old);
398     return NULL;
399   }
400
401   if (strchr(input, '\n'))
402     *strchr(input, '\n') = '\0';
403
404   /* Restore old terminfo */
405   tcsetattr(fd, TCSANOW, &to_old);
406   signal(SIGINT, SIG_DFL);
407
408   ret = silc_calloc(strlen(input), sizeof(char));
409   memcpy(ret, input, strlen(input));
410   memset(input, 0, sizeof(input));
411   return ret;
412 #else
413   return NULL;
414 #endif
415 }
416
417 /* Returns identifier string for public key generation. */
418
419 char *silc_client_create_identifier()
420 {
421   char *username = NULL, *realname = NULL;
422   char hostname[256], email[256];
423   
424   /* Get realname */
425   realname = silc_get_real_name();
426
427   /* Get hostname */
428   memset(hostname, 0, sizeof(hostname));
429   gethostname(hostname, sizeof(hostname));
430
431   /* Get username (mandatory) */
432   username = silc_get_username();
433   if (!username)
434     return NULL;
435
436   /* Create default email address, whether it is right or not */
437   snprintf(email, sizeof(email), "%s@%s", username, hostname);
438
439   return silc_pkcs_encode_identifier(username, hostname, realname, email,
440                                      NULL, NULL);
441 }
442
443 /* Creates new public key and private key pair. This is used only
444    when user wants to create new key pair from command line. */
445
446 int silc_client_create_key_pair(char *pkcs_name, int bits,
447                                 char *public_key, char *private_key,
448                                 char *identifier, 
449                                 SilcPublicKey *ret_pub_key,
450                                 SilcPrivateKey *ret_prv_key)
451 {
452   SilcPKCS pkcs;
453   SilcPublicKey pub_key;
454   SilcPrivateKey prv_key;
455   SilcRng rng;
456   unsigned char *key;
457   unsigned int key_len;
458   char *pkfile = NULL, *prvfile = NULL;
459
460   if (!pkcs_name || !public_key || !private_key)
461     printf("\
462 New pair of keys will be created.  Please, answer to following questions.\n\
463 ");
464
465   if (!pkcs_name) {
466   again_name:
467     pkcs_name = 
468       silc_client_get_input("PKCS name (l to list names) [rsa]: ");
469     if (!pkcs_name)
470       pkcs_name = strdup("rsa");
471
472     if (*pkcs_name == 'l' || *pkcs_name == 'L') {
473       silc_client_list_pkcs();
474       silc_free(pkcs_name);
475       goto again_name;
476     }
477   }
478
479   if (!silc_pkcs_is_supported(pkcs_name)) {
480     fprintf(stderr, "Unsupported PKCS `%s'", pkcs_name);
481     return FALSE;
482   }
483
484   if (!bits) {
485     char *length = NULL;
486     length = 
487       silc_client_get_input("Key length in bits [1024]: ");
488     if (!length)
489       bits = 1024;
490     else
491       bits = atoi(length);
492   }
493
494   if (!identifier) {
495     char *def = silc_client_create_identifier();
496
497     if (identifier)
498       snprintf(def, sizeof(def), "Public key identifier [%s]: ", def);
499     else
500       snprintf(def, sizeof(def),
501                "Public key identifier (eg. UN=priikone, HN=poseidon.pspt.fi, "
502                "RN=Pekka Riikonen, E=priikone@poseidon.pspt.fi): ");
503
504   again_ident:
505     identifier = silc_client_get_input(def);
506     if (!identifier)
507       goto again_ident;
508
509     if (def)
510       silc_free(def);
511   }
512
513   rng = silc_rng_alloc();
514   silc_rng_init(rng);
515   silc_math_primegen_init();
516
517   if (!public_key) {
518   again_pk:
519     pkfile = silc_client_get_input("Public key filename: ");
520     if (!pkfile) {
521       printf("Public key filename must be defined\n");
522       goto again_pk;
523     }
524   } else {
525     pkfile = public_key;
526   }
527
528   if (!private_key) {
529   again_prv:
530     prvfile = silc_client_get_input("Private key filename: ");
531     if (!prvfile) {
532       printf("Private key filename must be defined\n");
533       goto again_prv;
534     }
535   } else {
536     prvfile = private_key;
537   }
538
539   /* Generate keys */
540   silc_pkcs_alloc(pkcs_name, &pkcs);
541   pkcs->pkcs->init(pkcs->context, bits, rng);
542
543   /* Save public key into file */
544   key = silc_pkcs_get_public_key(pkcs, &key_len);
545   pub_key = silc_pkcs_public_key_alloc(pkcs->pkcs->name, identifier,
546                                        key, key_len);
547   silc_pkcs_save_public_key(pkfile, pub_key, SILC_PKCS_FILE_PEM);
548   if (ret_pub_key)
549     *ret_pub_key = pub_key;
550
551   memset(key, 0, sizeof(key_len));
552   silc_free(key);
553
554   /* Save private key into file */
555   key = silc_pkcs_get_private_key(pkcs, &key_len);
556   prv_key = silc_pkcs_private_key_alloc(pkcs->pkcs->name, key, key_len);
557
558   silc_pkcs_save_private_key(prvfile, prv_key, NULL, SILC_PKCS_FILE_BIN);
559   if (ret_prv_key)
560     *ret_prv_key = prv_key;
561
562   printf("Public key has been save into `%s'.\n", pkfile);
563   printf("Private key has been saved into `%s'.\n", prvfile);
564   printf("Press <Enter> to continue...\n");
565   getchar();
566
567   memset(key, 0, sizeof(key_len));
568   silc_free(key);
569
570   silc_math_primegen_uninit();
571   silc_rng_free(rng);
572   silc_pkcs_free(pkcs);
573
574   return TRUE;
575 }
576
577 /* This checks stats for various SILC files and directories. First it 
578    checks if ~/.silc directory exist and is owned by the correct user. If 
579    it doesn't exist, it will create the directory. After that it checks if
580    user's Public and Private key files exists and that they aren't expired.
581    If they doesn't exist or they are expired, they will be (re)created
582    after return. */
583
584 int silc_client_check_silc_dir()
585 {
586   char filename[256], file_public_key[256], file_private_key[256];
587   char servfilename[256];
588   char *identifier;
589   struct stat st;
590   struct passwd *pw;
591   int firstime = FALSE;
592   time_t curtime, modtime;
593
594   SILC_LOG_DEBUG(("Checking ~./silc directory"));
595
596   memset(filename, 0, sizeof(filename));
597   memset(file_public_key, 0, sizeof(file_public_key));
598   memset(file_private_key, 0, sizeof(file_private_key));
599
600   pw = getpwuid(getuid());
601   if (!pw) {
602     fprintf(stderr, "silc: %s\n", strerror(errno));
603     return FALSE;
604   }
605
606   identifier = silc_client_create_identifier();
607
608   /* We'll take home path from /etc/passwd file to be sure. */
609   snprintf(filename, sizeof(filename) - 1, "%s/.silc/", pw->pw_dir);
610   snprintf(servfilename, sizeof(servfilename) - 1, "%s/.silc/serverkeys", 
611            pw->pw_dir);
612
613   /*
614    * Check ~/.silc directory
615    */
616   if ((stat(filename, &st)) == -1) {
617     /* If dir doesn't exist */
618     if (errno == ENOENT) {
619       if (pw->pw_uid == geteuid()) {
620         if ((mkdir(filename, 0755)) == -1) {
621           fprintf(stderr, "Couldn't create `%s' directory\n", filename);
622           return FALSE;
623         }
624
625         /* Directory was created. First time running SILC */
626         firstime = TRUE;
627       } else {
628         fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
629                 filename);
630         return FALSE;
631       }
632     } else {
633       fprintf(stderr, "%s\n", strerror(errno));
634       return FALSE;
635     }
636   } else {
637     
638     /* Check the owner of the dir */
639     if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { 
640       fprintf(stderr, "You don't seem to own `%s' directory\n",
641               filename);
642       return FALSE;
643     }
644     
645     /* Check the permissions of the dir */
646     if ((st.st_mode & 0777) != 0755) {
647       if ((chmod(filename, 0755)) == -1) {
648         fprintf(stderr, "Permissions for `%s' directory must be 0755\n", 
649                 filename);
650         return FALSE;
651       }
652     }
653   }
654
655   /*
656    * Check ~./silc/serverkeys directory
657    */
658   if ((stat(servfilename, &st)) == -1) {
659     /* If dir doesn't exist */
660     if (errno == ENOENT) {
661       if (pw->pw_uid == geteuid()) {
662         if ((mkdir(servfilename, 0755)) == -1) {
663           fprintf(stderr, "Couldn't create `%s' directory\n", servfilename);
664           return FALSE;
665         }
666       } else {
667         fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
668                 servfilename);
669         return FALSE;
670       }
671     } else {
672       fprintf(stderr, "%s\n", strerror(errno));
673       return FALSE;
674     }
675   }
676   
677   /*
678    * Check Public and Private keys
679    */
680   snprintf(file_public_key, sizeof(file_public_key) - 1, "%s%s", 
681            filename, SILC_CLIENT_PUBLIC_KEY_NAME);
682   snprintf(file_private_key, sizeof(file_private_key) - 1, "%s%s", 
683            filename, SILC_CLIENT_PRIVATE_KEY_NAME);
684   
685   /* If running SILC first time */
686   if (firstime) {
687     fprintf(stdout, "Running SILC for the first time\n");
688     silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
689                                 SILC_CLIENT_DEF_PKCS_LEN,
690                                 file_public_key, file_private_key, 
691                                 identifier, NULL, NULL);
692     return TRUE;
693   }
694   
695   if ((stat(file_public_key, &st)) == -1) {
696     /* If file doesn't exist */
697     if (errno == ENOENT) {
698       fprintf(stdout, "Your public key doesn't exist\n");
699       silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
700                                   SILC_CLIENT_DEF_PKCS_LEN,
701                                   file_public_key, 
702                                   file_private_key, identifier, NULL, NULL);
703     } else {
704       fprintf(stderr, "%s\n", strerror(errno));
705       return FALSE;
706     }
707   }
708
709   if ((stat(file_private_key, &st)) == -1) {
710     /* If file doesn't exist */
711     if (errno == ENOENT) {
712       fprintf(stdout, "Your private key doesn't exist\n");
713       silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
714                                   SILC_CLIENT_DEF_PKCS_LEN,
715                                   file_public_key, 
716                                   file_private_key, identifier, NULL, NULL);
717     } else {
718       fprintf(stderr, "%s\n", strerror(errno));
719       return FALSE;
720     }
721   }
722     
723   /* Check the owner of the public key */
724   if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { 
725     fprintf(stderr, "You don't seem to own your public key!?\n");
726     return FALSE;
727   }
728   
729   /* Check the owner of the private key */
730   if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { 
731     fprintf(stderr, "You don't seem to own your private key!?\n");
732     return FALSE;
733   }
734     
735   /* Check the permissions for the private key */
736   if ((st.st_mode & 0777) != 0600) {
737     fprintf(stderr, "Wrong permissions in your private key file `%s'!\n"
738             "Trying to change them ... ", file_private_key);
739     if ((chmod(file_private_key, 0600)) == -1) {
740       fprintf(stderr,
741               "Failed to change permissions for private key file!\n" 
742               "Permissions for your private key file must be 0600.\n");
743       return FALSE;
744     }
745     fprintf(stderr, "Done.\n\n");
746   }
747
748   /* See if the key has expired. */
749   modtime = st.st_mtime;        /* last modified */
750   curtime = time(0) - modtime;
751     
752   /* 86400 is seconds in a day. */
753   if (curtime >= (86400 * SILC_CLIENT_KEY_EXPIRES)) {
754     fprintf(stdout, 
755             "--------------------------------------------------\n"
756             "Your private key has expired and needs to be\n" 
757             "recreated.  This will be done automatically now.\n"
758             "Your new key will expire in %d days from today.\n"
759             "--------------------------------------------------\n",
760             SILC_CLIENT_KEY_EXPIRES);
761
762     silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
763                                 SILC_CLIENT_DEF_PKCS_LEN,
764                                 file_public_key, 
765                                 file_private_key, identifier, NULL, NULL);
766   }
767   
768   if (identifier)
769     silc_free(identifier);
770
771   return TRUE;
772 }
773
774 /* Loads public and private key from files. */
775
776 int silc_client_load_keys(SilcClient client)
777 {
778   char filename[256];
779   struct passwd *pw;
780
781   SILC_LOG_DEBUG(("Loading public and private keys"));
782
783   pw = getpwuid(getuid());
784   if (!pw)
785     return FALSE;
786
787   memset(filename, 0, sizeof(filename));
788   snprintf(filename, sizeof(filename) - 1, "%s/.silc/%s", 
789            pw->pw_dir, SILC_CLIENT_PRIVATE_KEY_NAME);
790
791   if (silc_pkcs_load_private_key(filename, &client->private_key,
792                                  SILC_PKCS_FILE_BIN) == FALSE)
793     if (silc_pkcs_load_private_key(filename, &client->private_key,
794                                    SILC_PKCS_FILE_PEM) == FALSE)
795       return FALSE;
796
797   memset(filename, 0, sizeof(filename));
798   snprintf(filename, sizeof(filename) - 1, "%s/.silc/%s", 
799            pw->pw_dir, SILC_CLIENT_PUBLIC_KEY_NAME);
800
801   if (silc_pkcs_load_public_key(filename, &client->public_key,
802                                 SILC_PKCS_FILE_PEM) == FALSE)
803     if (silc_pkcs_load_public_key(filename, &client->public_key,
804                                   SILC_PKCS_FILE_BIN) == FALSE)
805       return FALSE;
806
807   return TRUE;
808 }
809
810 /* Verifies received public key. If user decides to trust the key it is
811    saved as trusted server key for later use. If user does not trust the
812    key this returns FALSE. */
813
814 int silc_client_verify_server_key(SilcClient client, 
815                                   SilcSocketConnection sock,
816                                   unsigned char *pk, unsigned int pk_len,
817                                   SilcSKEPKType pk_type)
818 {
819   char filename[256];
820   char file[256];
821   char *hostname, *fingerprint;
822   struct passwd *pw;
823   struct stat st;
824
825   hostname = sock->hostname ? sock->hostname : sock->ip;
826
827   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
828     silc_say(client, "We don't support server %s key type", hostname);
829     return FALSE;
830   }
831
832   pw = getpwuid(getuid());
833   if (!pw)
834     return FALSE;
835
836   memset(filename, 0, sizeof(filename));
837   memset(file, 0, sizeof(file));
838   snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname,
839            sock->port);
840   snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s", 
841            pw->pw_dir, file);
842
843   /* Check wheter this key already exists */
844   if (stat(filename, &st) < 0) {
845
846     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
847     silc_say(client, "Received server %s public key", hostname);
848     silc_say(client, "Fingerprint for the server %s key is", hostname);
849     silc_say(client, "%s", fingerprint);
850     silc_free(fingerprint);
851
852     /* Ask user to verify the key and save it */
853     if (silc_client_ask_yes_no(client, 
854        "Would you like to accept the key (y/n)? "))
855       {
856         /* Save the key for future checking */
857         silc_pkcs_save_public_key_data(filename, pk, pk_len, 
858                                        SILC_PKCS_FILE_PEM);
859         return TRUE;
860       }
861   } else {
862     /* The key already exists, verify it. */
863     SilcPublicKey public_key;
864     unsigned char *encpk;
865     unsigned int encpk_len;
866
867     /* Load the key file */
868     if (!silc_pkcs_load_public_key(filename, &public_key, 
869                                    SILC_PKCS_FILE_PEM))
870       if (!silc_pkcs_load_public_key(filename, &public_key, 
871                                      SILC_PKCS_FILE_BIN)) {
872         fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
873         silc_say(client, "Received server %s public key", hostname);
874         silc_say(client, "Fingerprint for the server %s key is", hostname);
875         silc_say(client, "%s", fingerprint);
876         silc_free(fingerprint);
877         silc_say(client, "Could not load your local copy of the server %s key",
878                  hostname);
879         if (silc_client_ask_yes_no(client, 
880            "Would you like to accept the key anyway (y/n)? "))
881           {
882             /* Save the key for future checking */
883             unlink(filename);
884             silc_pkcs_save_public_key_data(filename, pk, pk_len,
885                                            SILC_PKCS_FILE_PEM);
886             return TRUE;
887           }
888         
889         return FALSE;
890       }
891   
892     /* Encode the key data */
893     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
894     if (!encpk) {
895       fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
896       silc_say(client, "Received server %s public key", hostname);
897       silc_say(client, "Fingerprint for the server %s key is", hostname);
898       silc_say(client, "%s", fingerprint);
899       silc_free(fingerprint);
900       silc_say(client, "Your local copy of the server %s key is malformed",
901                hostname);
902       if (silc_client_ask_yes_no(client, 
903          "Would you like to accept the key anyway (y/n)? "))
904         {
905           /* Save the key for future checking */
906           unlink(filename);
907           silc_pkcs_save_public_key_data(filename, pk, pk_len,
908                                          SILC_PKCS_FILE_PEM);
909           return TRUE;
910         }
911
912       return FALSE;
913     }
914
915     if (memcmp(encpk, pk, encpk_len)) {
916       fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
917       silc_say(client, "Received server %s public key", hostname);
918       silc_say(client, "Fingerprint for the server %s key is", hostname);
919       silc_say(client, "%s", fingerprint);
920       silc_free(fingerprint);
921       silc_say(client, "Server %s key does not match with your local copy",
922                hostname);
923       silc_say(client, "It is possible that the key has expired or changed");
924       silc_say(client, "It is also possible that some one is performing "
925                        "man-in-the-middle attack");
926       
927       /* Ask user to verify the key and save it */
928       if (silc_client_ask_yes_no(client, 
929          "Would you like to accept the key anyway (y/n)? "))
930         {
931           /* Save the key for future checking */
932           unlink(filename);
933           silc_pkcs_save_public_key_data(filename, pk, pk_len,
934                                          SILC_PKCS_FILE_PEM);
935           return TRUE;
936         }
937
938       silc_say(client, "Will not accept server %s key", hostname);
939       return FALSE;
940     }
941
942     /* Local copy matched */
943     return TRUE;
944   }
945
946   silc_say(client, "Will not accept server %s key", hostname);
947   return FALSE;
948 }