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