updates.
[silc.git] / apps / irssi / src / silc / core / 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 "module.h"
23
24 #include "net-nonblock.h"
25 #include "net-sendbuffer.h"
26 #include "signals.h"
27 #include "servers.h"
28 #include "commands.h"
29 #include "levels.h"
30 #include "modules.h"
31 #include "rawlog.h"
32 #include "misc.h"
33 #include "settings.h"
34 #include "curses.h"
35
36 #include "servers-setup.h"
37
38 #include "silc-servers.h"
39 #include "silc-channels.h"
40 #include "silc-queries.h"
41 #include "window-item-def.h"
42
43 #include "fe-common/core/printtext.h"
44
45 /* Asks yes/no from user on the input line. Returns TRUE on "yes" and
46    FALSE on "no". */
47
48 void silc_client_ask_yes_no(char *prompt, SIGNAL_FUNC func)
49 {
50   keyboard_entry_redirect(func, prompt, 0, NULL);
51 }
52
53 /* Lists supported (builtin) ciphers */
54
55 void silc_client_list_ciphers()
56 {
57   char *ciphers = silc_cipher_get_supported();
58   fprintf(stdout, "%s\n", ciphers);
59   silc_free(ciphers);
60 }
61
62 /* Lists supported (builtin) hash functions */
63
64 void silc_client_list_hash_funcs()
65 {
66   char *hash = silc_hash_get_supported();
67   fprintf(stdout, "%s\n", hash);
68   silc_free(hash);
69 }
70
71 /* Lists supported PKCS algorithms */
72
73 void silc_client_list_pkcs()
74 {
75   char *pkcs = silc_pkcs_get_supported();
76   fprintf(stdout, "%s\n", pkcs);
77   silc_free(pkcs);
78 }
79
80 /* Displays input prompt on command line and takes input data from user */
81
82 void silc_client_get_input(char *prompt, SIGNAL_FUNC func)
83 {
84   keyboard_entry_redirect(func, prompt, 0, NULL);
85 }
86
87 /* Returns identifier string for public key generation. */
88
89 char *silc_client_create_identifier()
90 {
91   char *username = NULL, *realname = NULL;
92   char hostname[256], email[256];
93   
94   /* Get realname */
95   realname = silc_get_real_name();
96
97   /* Get hostname */
98   memset(hostname, 0, sizeof(hostname));
99   gethostname(hostname, sizeof(hostname));
100
101   /* Get username (mandatory) */
102   username = silc_get_username();
103   if (!username)
104     return NULL;
105
106   /* Create default email address, whether it is right or not */
107   snprintf(email, sizeof(email), "%s@%s", username, hostname);
108
109   return silc_pkcs_encode_identifier(username, hostname, realname, email,
110                                      NULL, NULL);
111 }
112
113 /* Creates new public key and private key pair. This is used only
114    when user wants to create new key pair from command line. */
115
116 int silc_client_create_key_pair(char *pkcs_name, int bits,
117                                 char *public_key, char *private_key,
118                                 char *identifier, 
119                                 SilcPublicKey *ret_pub_key,
120                                 SilcPrivateKey *ret_prv_key)
121 {
122   SilcPKCS pkcs;
123   SilcPublicKey pub_key;
124   SilcPrivateKey prv_key;
125   SilcRng rng;
126   unsigned char *key;
127   uint32 key_len;
128   char line[256];
129   char *pkfile = NULL, *prvfile = NULL;
130
131   if (!pkcs_name || !public_key || !private_key)
132     printf("\
133 New pair of keys will be created.  Please, answer to following questions.\n\
134 ");
135
136   if (!pkcs_name) {
137   again_name:
138     pkcs_name = 
139       silc_client_get_input("PKCS name (l to list names) [rsa]: ");
140     if (!pkcs_name)
141       pkcs_name = strdup("rsa");
142
143     if (*pkcs_name == 'l' || *pkcs_name == 'L') {
144       silc_client_list_pkcs();
145       silc_free(pkcs_name);
146       goto again_name;
147     }
148   }
149
150   if (!silc_pkcs_is_supported(pkcs_name)) {
151     fprintf(stderr, "Unknown PKCS `%s'", pkcs_name);
152     return FALSE;
153   }
154
155   if (!bits) {
156     char *length = NULL;
157     length = 
158       silc_client_get_input("Key length in bits [1024]: ");
159     if (!length)
160       bits = 1024;
161     else
162       bits = atoi(length);
163   }
164
165   if (!identifier) {
166     char *def = silc_client_create_identifier();
167
168     memset(line, 0, sizeof(line));
169     if (def)
170       snprintf(line, sizeof(line), "Identifier [%s]: ", def);
171     else
172       snprintf(line, sizeof(line),
173                "Identifier (eg. UN=jon, HN=jon.dummy.com, "
174                "RN=Jon Johnson, E=jon@dummy.com): ");
175
176     while (!identifier) {
177       identifier = silc_client_get_input(line);
178       if (!identifier && def)
179         identifier = strdup(def);
180     }
181
182     if (def)
183       silc_free(def);
184   }
185
186   rng = silc_rng_alloc();
187   silc_rng_init(rng);
188   silc_rng_global_init(rng);
189
190   if (!public_key) {
191     memset(line, 0, sizeof(line));
192     snprintf(line, sizeof(line), "Public key filename [%s] ", 
193              SILC_CLIENT_PUBLIC_KEY_NAME);
194     pkfile = silc_client_get_input(line);
195     if (!pkfile)
196       pkfile = SILC_CLIENT_PUBLIC_KEY_NAME;
197   } else {
198     pkfile = public_key;
199   }
200
201   if (!private_key) {
202     memset(line, 0, sizeof(line));
203     snprintf(line, sizeof(line), "Public key filename [%s] ", 
204              SILC_CLIENT_PRIVATE_KEY_NAME);
205     prvfile = silc_client_get_input(line);
206     if (!prvfile)
207       prvfile = SILC_CLIENT_PRIVATE_KEY_NAME;
208   } else {
209     prvfile = private_key;
210   }
211
212   /* Generate keys */
213   silc_pkcs_alloc(pkcs_name, &pkcs);
214   pkcs->pkcs->init(pkcs->context, bits, rng);
215
216   /* Save public key into file */
217   key = silc_pkcs_get_public_key(pkcs, &key_len);
218   pub_key = silc_pkcs_public_key_alloc(pkcs->pkcs->name, identifier,
219                                        key, key_len);
220   silc_pkcs_save_public_key(pkfile, pub_key, SILC_PKCS_FILE_PEM);
221   if (ret_pub_key)
222     *ret_pub_key = pub_key;
223
224   memset(key, 0, sizeof(key_len));
225   silc_free(key);
226
227   /* Save private key into file */
228   key = silc_pkcs_get_private_key(pkcs, &key_len);
229   prv_key = silc_pkcs_private_key_alloc(pkcs->pkcs->name, key, key_len);
230
231   silc_pkcs_save_private_key(prvfile, prv_key, NULL, SILC_PKCS_FILE_BIN);
232   if (ret_prv_key)
233     *ret_prv_key = prv_key;
234
235   printf("Public key has been saved into `%s'.\n", pkfile);
236   printf("Private key has been saved into `%s'.\n", prvfile);
237   printf("Press <Enter> to continue...\n");
238   getchar();
239
240   memset(key, 0, sizeof(key_len));
241   silc_free(key);
242
243   silc_rng_free(rng);
244   silc_pkcs_free(pkcs);
245
246   return TRUE;
247 }
248
249 /* This checks stats for various SILC files and directories. First it 
250    checks if ~/.silc directory exist and is owned by the correct user. If 
251    it doesn't exist, it will create the directory. After that it checks if
252    user's Public and Private key files exists and that they aren't expired.
253    If they doesn't exist or they are expired, they will be (re)created
254    after return. */
255
256 int silc_client_check_silc_dir()
257 {
258   char filename[256], file_public_key[256], file_private_key[256];
259   char servfilename[256], clientfilename[256];
260   char *identifier;
261   struct stat st;
262   struct passwd *pw;
263   int firstime = FALSE;
264   time_t curtime, modtime;
265
266   SILC_LOG_DEBUG(("Checking ~./silc directory"));
267
268   memset(filename, 0, sizeof(filename));
269   memset(file_public_key, 0, sizeof(file_public_key));
270   memset(file_private_key, 0, sizeof(file_private_key));
271
272   pw = getpwuid(getuid());
273   if (!pw) {
274     fprintf(stderr, "silc: %s\n", strerror(errno));
275     return FALSE;
276   }
277
278   identifier = silc_client_create_identifier();
279
280   /* We'll take home path from /etc/passwd file to be sure. */
281   snprintf(filename, sizeof(filename) - 1, "%s/.silc/", pw->pw_dir);
282   snprintf(servfilename, sizeof(servfilename) - 1, "%s/.silc/serverkeys", 
283            pw->pw_dir);
284   snprintf(clientfilename, sizeof(clientfilename) - 1, "%s/.silc/clientkeys", 
285            pw->pw_dir);
286
287   /*
288    * Check ~/.silc directory
289    */
290   if ((stat(filename, &st)) == -1) {
291     /* If dir doesn't exist */
292     if (errno == ENOENT) {
293       if (pw->pw_uid == geteuid()) {
294         if ((mkdir(filename, 0755)) == -1) {
295           fprintf(stderr, "Couldn't create `%s' directory\n", filename);
296           return FALSE;
297         }
298
299         /* Directory was created. First time running SILC */
300         firstime = TRUE;
301       } else {
302         fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
303                 filename);
304         return FALSE;
305       }
306     } else {
307       fprintf(stderr, "%s\n", strerror(errno));
308       return FALSE;
309     }
310   } else {
311     
312     /* Check the owner of the dir */
313     if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { 
314       fprintf(stderr, "You don't seem to own `%s' directory\n",
315               filename);
316       return FALSE;
317     }
318     
319     /* Check the permissions of the dir */
320     if ((st.st_mode & 0777) != 0755) {
321       if ((chmod(filename, 0755)) == -1) {
322         fprintf(stderr, "Permissions for `%s' directory must be 0755\n", 
323                 filename);
324         return FALSE;
325       }
326     }
327   }
328
329   /*
330    * Check ~./silc/serverkeys directory
331    */
332   if ((stat(servfilename, &st)) == -1) {
333     /* If dir doesn't exist */
334     if (errno == ENOENT) {
335       if (pw->pw_uid == geteuid()) {
336         if ((mkdir(servfilename, 0755)) == -1) {
337           fprintf(stderr, "Couldn't create `%s' directory\n", servfilename);
338           return FALSE;
339         }
340       } else {
341         fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
342                 servfilename);
343         return FALSE;
344       }
345     } else {
346       fprintf(stderr, "%s\n", strerror(errno));
347       return FALSE;
348     }
349   }
350   
351   /*
352    * Check ~./silc/clientkeys directory
353    */
354   if ((stat(clientfilename, &st)) == -1) {
355     /* If dir doesn't exist */
356     if (errno == ENOENT) {
357       if (pw->pw_uid == geteuid()) {
358         if ((mkdir(clientfilename, 0755)) == -1) {
359           fprintf(stderr, "Couldn't create `%s' directory\n", clientfilename);
360           return FALSE;
361         }
362       } else {
363         fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
364                 clientfilename);
365         return FALSE;
366       }
367     } else {
368       fprintf(stderr, "%s\n", strerror(errno));
369       return FALSE;
370     }
371   }
372   
373   /*
374    * Check Public and Private keys
375    */
376   snprintf(file_public_key, sizeof(file_public_key) - 1, "%s%s", 
377            filename, SILC_CLIENT_PUBLIC_KEY_NAME);
378   snprintf(file_private_key, sizeof(file_private_key) - 1, "%s%s", 
379            filename, SILC_CLIENT_PRIVATE_KEY_NAME);
380   
381   /* If running SILC first time */
382   if (firstime) {
383     fprintf(stdout, "Running SILC for the first time\n");
384     silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
385                                 SILC_CLIENT_DEF_PKCS_LEN,
386                                 file_public_key, file_private_key, 
387                                 identifier, NULL, NULL);
388     return TRUE;
389   }
390   
391   if ((stat(file_public_key, &st)) == -1) {
392     /* If file doesn't exist */
393     if (errno == ENOENT) {
394       fprintf(stdout, "Your public key doesn't exist\n");
395       silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
396                                   SILC_CLIENT_DEF_PKCS_LEN,
397                                   file_public_key, 
398                                   file_private_key, identifier, NULL, NULL);
399     } else {
400       fprintf(stderr, "%s\n", strerror(errno));
401       return FALSE;
402     }
403   }
404
405   if ((stat(file_private_key, &st)) == -1) {
406     /* If file doesn't exist */
407     if (errno == ENOENT) {
408       fprintf(stdout, "Your private key doesn't exist\n");
409       silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
410                                   SILC_CLIENT_DEF_PKCS_LEN,
411                                   file_public_key, 
412                                   file_private_key, identifier, NULL, NULL);
413     } else {
414       fprintf(stderr, "%s\n", strerror(errno));
415       return FALSE;
416     }
417   }
418     
419   /* Check the owner of the public key */
420   if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { 
421     fprintf(stderr, "You don't seem to own your public key!?\n");
422     return FALSE;
423   }
424   
425   /* Check the owner of the private key */
426   if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { 
427     fprintf(stderr, "You don't seem to own your private key!?\n");
428     return FALSE;
429   }
430     
431   /* Check the permissions for the private key */
432   if ((st.st_mode & 0777) != 0600) {
433     fprintf(stderr, "Wrong permissions in your private key file `%s'!\n"
434             "Trying to change them ... ", file_private_key);
435     if ((chmod(file_private_key, 0600)) == -1) {
436       fprintf(stderr,
437               "Failed to change permissions for private key file!\n" 
438               "Permissions for your private key file must be 0600.\n");
439       return FALSE;
440     }
441     fprintf(stderr, "Done.\n\n");
442   }
443
444   /* See if the key has expired. */
445   modtime = st.st_mtime;        /* last modified */
446   curtime = time(0) - modtime;
447     
448   /* 86400 is seconds in a day. */
449   if (curtime >= (86400 * SILC_CLIENT_KEY_EXPIRES)) {
450     fprintf(stdout, 
451             "--------------------------------------------------\n"
452             "Your private key has expired and needs to be\n" 
453             "recreated.  This will be done automatically now.\n"
454             "Your new key will expire in %d days from today.\n"
455             "--------------------------------------------------\n",
456             SILC_CLIENT_KEY_EXPIRES);
457
458     silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
459                                 SILC_CLIENT_DEF_PKCS_LEN,
460                                 file_public_key, 
461                                 file_private_key, identifier, NULL, NULL);
462   }
463   
464   if (identifier)
465     silc_free(identifier);
466
467   return TRUE;
468 }
469
470 /* Loads public and private key from files. */
471
472 int silc_client_load_keys(SilcClient client)
473 {
474   char filename[256];
475   struct passwd *pw;
476
477   SILC_LOG_DEBUG(("Loading public and private keys"));
478
479   pw = getpwuid(getuid());
480   if (!pw)
481     return FALSE;
482
483   memset(filename, 0, sizeof(filename));
484   snprintf(filename, sizeof(filename) - 1, "%s/.silc/%s", 
485            pw->pw_dir, SILC_CLIENT_PRIVATE_KEY_NAME);
486
487   if (silc_pkcs_load_private_key(filename, &client->private_key,
488                                  SILC_PKCS_FILE_BIN) == FALSE)
489     if (silc_pkcs_load_private_key(filename, &client->private_key,
490                                    SILC_PKCS_FILE_PEM) == FALSE)
491       return FALSE;
492
493   memset(filename, 0, sizeof(filename));
494   snprintf(filename, sizeof(filename) - 1, "%s/.silc/%s", 
495            pw->pw_dir, SILC_CLIENT_PUBLIC_KEY_NAME);
496
497   if (silc_pkcs_load_public_key(filename, &client->public_key,
498                                 SILC_PKCS_FILE_PEM) == FALSE)
499     if (silc_pkcs_load_public_key(filename, &client->public_key,
500                                   SILC_PKCS_FILE_BIN) == FALSE)
501       return FALSE;
502
503   return TRUE;
504 }
505
506 /* Dumps the public key on screen. Used from the command line option. */
507
508 int silc_client_show_key(char *keyfile)
509 {
510   SilcPublicKey public_key;
511   SilcPublicKeyIdentifier ident;
512   char *fingerprint;
513   unsigned char *pk;
514   uint32 pk_len;
515   SilcPKCS pkcs;
516   int key_len = 0;
517
518   if (silc_pkcs_load_public_key(keyfile, &public_key,
519                                 SILC_PKCS_FILE_PEM) == FALSE)
520     if (silc_pkcs_load_public_key(keyfile, &public_key,
521                                   SILC_PKCS_FILE_BIN) == FALSE) {
522       fprintf(stderr, "Could not load public key file `%s'\n", keyfile);
523       return FALSE;
524     }
525
526   ident = silc_pkcs_decode_identifier(public_key->identifier);
527
528   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
529   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
530
531   if (silc_pkcs_alloc(public_key->name, &pkcs)) {
532     key_len = silc_pkcs_public_key_set(pkcs, public_key);
533     silc_pkcs_free(pkcs);
534   }
535
536   printf("Public key file    : %s\n", keyfile);
537   printf("Algorithm          : %s\n", public_key->name);
538   if (key_len)
539     printf("Key length (bits)  : %d\n", key_len);
540   if (ident->realname)
541     printf("Real name          : %s\n", ident->realname);
542   if (ident->username)
543     printf("Username           : %s\n", ident->username);
544   if (ident->host)
545     printf("Hostname           : %s\n", ident->host);
546   if (ident->email)
547     printf("Email              : %s\n", ident->email);
548   if (ident->org)
549     printf("Organization       : %s\n", ident->org);
550   if (ident->country)
551     printf("Country            : %s\n", ident->country);
552   printf("Fingerprint (SHA1) : %s\n", fingerprint); 
553
554   fflush(stdout);
555
556   silc_free(fingerprint);
557   silc_free(pk);
558   silc_pkcs_public_key_free(public_key);
559   silc_pkcs_free_identifier(ident);
560
561   return TRUE;
562 }