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