Moved silc_client_ch[u]mode[_char] to client library from silc/.
[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_math_primegen_init();
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_math_primegen_uninit();
494   silc_rng_free(rng);
495   silc_pkcs_free(pkcs);
496
497   return TRUE;
498 }
499
500 /* This checks stats for various SILC files and directories. First it 
501    checks if ~/.silc directory exist and is owned by the correct user. If 
502    it doesn't exist, it will create the directory. After that it checks if
503    user's Public and Private key files exists and that they aren't expired.
504    If they doesn't exist or they are expired, they will be (re)created
505    after return. */
506
507 int silc_client_check_silc_dir()
508 {
509   char filename[256], file_public_key[256], file_private_key[256];
510   char servfilename[256];
511   char *identifier;
512   struct stat st;
513   struct passwd *pw;
514   int firstime = FALSE;
515   time_t curtime, modtime;
516
517   SILC_LOG_DEBUG(("Checking ~./silc directory"));
518
519   memset(filename, 0, sizeof(filename));
520   memset(file_public_key, 0, sizeof(file_public_key));
521   memset(file_private_key, 0, sizeof(file_private_key));
522
523   pw = getpwuid(getuid());
524   if (!pw) {
525     fprintf(stderr, "silc: %s\n", strerror(errno));
526     return FALSE;
527   }
528
529   identifier = silc_client_create_identifier();
530
531   /* We'll take home path from /etc/passwd file to be sure. */
532   snprintf(filename, sizeof(filename) - 1, "%s/.silc/", pw->pw_dir);
533   snprintf(servfilename, sizeof(servfilename) - 1, "%s/.silc/serverkeys", 
534            pw->pw_dir);
535
536   /*
537    * Check ~/.silc directory
538    */
539   if ((stat(filename, &st)) == -1) {
540     /* If dir doesn't exist */
541     if (errno == ENOENT) {
542       if (pw->pw_uid == geteuid()) {
543         if ((mkdir(filename, 0755)) == -1) {
544           fprintf(stderr, "Couldn't create `%s' directory\n", filename);
545           return FALSE;
546         }
547
548         /* Directory was created. First time running SILC */
549         firstime = TRUE;
550       } else {
551         fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
552                 filename);
553         return FALSE;
554       }
555     } else {
556       fprintf(stderr, "%s\n", strerror(errno));
557       return FALSE;
558     }
559   } else {
560     
561     /* Check the owner of the dir */
562     if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { 
563       fprintf(stderr, "You don't seem to own `%s' directory\n",
564               filename);
565       return FALSE;
566     }
567     
568     /* Check the permissions of the dir */
569     if ((st.st_mode & 0777) != 0755) {
570       if ((chmod(filename, 0755)) == -1) {
571         fprintf(stderr, "Permissions for `%s' directory must be 0755\n", 
572                 filename);
573         return FALSE;
574       }
575     }
576   }
577
578   /*
579    * Check ~./silc/serverkeys directory
580    */
581   if ((stat(servfilename, &st)) == -1) {
582     /* If dir doesn't exist */
583     if (errno == ENOENT) {
584       if (pw->pw_uid == geteuid()) {
585         if ((mkdir(servfilename, 0755)) == -1) {
586           fprintf(stderr, "Couldn't create `%s' directory\n", servfilename);
587           return FALSE;
588         }
589       } else {
590         fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
591                 servfilename);
592         return FALSE;
593       }
594     } else {
595       fprintf(stderr, "%s\n", strerror(errno));
596       return FALSE;
597     }
598   }
599   
600   /*
601    * Check Public and Private keys
602    */
603   snprintf(file_public_key, sizeof(file_public_key) - 1, "%s%s", 
604            filename, SILC_CLIENT_PUBLIC_KEY_NAME);
605   snprintf(file_private_key, sizeof(file_private_key) - 1, "%s%s", 
606            filename, SILC_CLIENT_PRIVATE_KEY_NAME);
607   
608   /* If running SILC first time */
609   if (firstime) {
610     fprintf(stdout, "Running SILC for the first time\n");
611     silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
612                                 SILC_CLIENT_DEF_PKCS_LEN,
613                                 file_public_key, file_private_key, 
614                                 identifier, NULL, NULL);
615     return TRUE;
616   }
617   
618   if ((stat(file_public_key, &st)) == -1) {
619     /* If file doesn't exist */
620     if (errno == ENOENT) {
621       fprintf(stdout, "Your public key doesn't exist\n");
622       silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
623                                   SILC_CLIENT_DEF_PKCS_LEN,
624                                   file_public_key, 
625                                   file_private_key, identifier, NULL, NULL);
626     } else {
627       fprintf(stderr, "%s\n", strerror(errno));
628       return FALSE;
629     }
630   }
631
632   if ((stat(file_private_key, &st)) == -1) {
633     /* If file doesn't exist */
634     if (errno == ENOENT) {
635       fprintf(stdout, "Your private key doesn't exist\n");
636       silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
637                                   SILC_CLIENT_DEF_PKCS_LEN,
638                                   file_public_key, 
639                                   file_private_key, identifier, NULL, NULL);
640     } else {
641       fprintf(stderr, "%s\n", strerror(errno));
642       return FALSE;
643     }
644   }
645     
646   /* Check the owner of the public key */
647   if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { 
648     fprintf(stderr, "You don't seem to own your public key!?\n");
649     return FALSE;
650   }
651   
652   /* Check the owner of the private key */
653   if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { 
654     fprintf(stderr, "You don't seem to own your private key!?\n");
655     return FALSE;
656   }
657     
658   /* Check the permissions for the private key */
659   if ((st.st_mode & 0777) != 0600) {
660     fprintf(stderr, "Wrong permissions in your private key file `%s'!\n"
661             "Trying to change them ... ", file_private_key);
662     if ((chmod(file_private_key, 0600)) == -1) {
663       fprintf(stderr,
664               "Failed to change permissions for private key file!\n" 
665               "Permissions for your private key file must be 0600.\n");
666       return FALSE;
667     }
668     fprintf(stderr, "Done.\n\n");
669   }
670
671   /* See if the key has expired. */
672   modtime = st.st_mtime;        /* last modified */
673   curtime = time(0) - modtime;
674     
675   /* 86400 is seconds in a day. */
676   if (curtime >= (86400 * SILC_CLIENT_KEY_EXPIRES)) {
677     fprintf(stdout, 
678             "--------------------------------------------------\n"
679             "Your private key has expired and needs to be\n" 
680             "recreated.  This will be done automatically now.\n"
681             "Your new key will expire in %d days from today.\n"
682             "--------------------------------------------------\n",
683             SILC_CLIENT_KEY_EXPIRES);
684
685     silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
686                                 SILC_CLIENT_DEF_PKCS_LEN,
687                                 file_public_key, 
688                                 file_private_key, identifier, NULL, NULL);
689   }
690   
691   if (identifier)
692     silc_free(identifier);
693
694   return TRUE;
695 }
696
697 /* Loads public and private key from files. */
698
699 int silc_client_load_keys(SilcClient client)
700 {
701   char filename[256];
702   struct passwd *pw;
703
704   SILC_LOG_DEBUG(("Loading public and private keys"));
705
706   pw = getpwuid(getuid());
707   if (!pw)
708     return FALSE;
709
710   memset(filename, 0, sizeof(filename));
711   snprintf(filename, sizeof(filename) - 1, "%s/.silc/%s", 
712            pw->pw_dir, SILC_CLIENT_PRIVATE_KEY_NAME);
713
714   if (silc_pkcs_load_private_key(filename, &client->private_key,
715                                  SILC_PKCS_FILE_BIN) == FALSE)
716     if (silc_pkcs_load_private_key(filename, &client->private_key,
717                                    SILC_PKCS_FILE_PEM) == 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,
725                                 SILC_PKCS_FILE_PEM) == FALSE)
726     if (silc_pkcs_load_public_key(filename, &client->public_key,
727                                   SILC_PKCS_FILE_BIN) == FALSE)
728       return FALSE;
729
730   return TRUE;
731 }