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