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