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