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