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