Tue Apr 10 15:55:42 CEST 2007 Jochen Eisinger <coffee@silcnet.org>
[silc.git] / apps / irssi / src / silc / core / clientutil.c
1 /*
2
3   client.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2006 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; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 #include "module.h"
22
23 #include "net-nonblock.h"
24 #include "net-sendbuffer.h"
25 #include "signals.h"
26 #include "servers.h"
27 #include "commands.h"
28 #include "levels.h"
29 #include "modules.h"
30 #include "rawlog.h"
31 #include "misc.h"
32 #include "settings.h"
33
34 #include "channels-setup.h"
35
36 #include "silc-servers.h"
37 #include "silc-channels.h"
38 #include "silc-queries.h"
39 #include "silc-nicklist.h"
40 #include "window-item-def.h"
41
42 #include "fe-common/core/printtext.h"
43 #include "fe-common/core/keyboard.h"
44 #include "fe-common/silc/module-formats.h"
45
46 #include "core.h"
47
48 #ifdef SILC_PLUGIN
49 void silc_client_print_list(char *list)
50 {
51   char **items;
52   int i=0;
53
54   items = g_strsplit(list, ",", -1);
55   
56   while (items[i] != NULL)
57     printformat_module("fe-common/silc", NULL, NULL,
58                        MSGLEVEL_CRAP, SILCTXT_CONFIG_LIST,
59                        items[i++]);
60
61   g_strfreev(items);
62 }
63 #endif
64
65 /* Lists supported ciphers */
66
67 void silc_client_list_ciphers()
68 {
69   char *ciphers = silc_cipher_get_supported();
70 #ifdef SILC_PLUGIN
71   printformat_module("fe-common/silc", NULL, NULL,
72                      MSGLEVEL_CRAP, SILCTXT_CONFIG_ALGOS,
73                      "cipher");
74   silc_client_print_list(ciphers);
75 #else
76   fprintf(stdout, "%s\n", ciphers);
77 #endif
78   silc_free(ciphers);
79 }
80
81 /* Lists supported hash functions */
82
83 void silc_client_list_hash_funcs()
84 {
85   char *hash = silc_hash_get_supported();
86 #ifdef SILC_PLUGIN
87   printformat_module("fe-common/silc", NULL, NULL,
88                      MSGLEVEL_CRAP, SILCTXT_CONFIG_ALGOS,
89                      "hash");
90   silc_client_print_list(hash);
91 #else
92   fprintf(stdout, "%s\n", hash);
93 #endif
94   silc_free(hash);
95 }
96
97 /* Lists supported hash functions */
98
99 void silc_client_list_hmacs()
100 {
101   char *hash = silc_hmac_get_supported();
102 #ifdef SILC_PLUGIN
103   printformat_module("fe-common/silc", NULL, NULL,
104                      MSGLEVEL_CRAP, SILCTXT_CONFIG_ALGOS,
105                      "hmac");
106   silc_client_print_list(hash);
107 #else
108   fprintf(stdout, "%s\n", hash);
109 #endif
110   silc_free(hash);
111 }
112
113 /* Lists supported PKCS algorithms */
114
115 void silc_client_list_pkcs()
116 {
117   char *pkcs = silc_pkcs_get_supported();
118 #ifdef SILC_PLUGIN
119   printformat_module("fe-common/silc", NULL, NULL,
120                      MSGLEVEL_CRAP, SILCTXT_CONFIG_ALGOS,
121                      "pkcs");
122   silc_client_print_list(pkcs);
123 #else
124   fprintf(stdout, "%s\n", pkcs);
125 #endif
126   silc_free(pkcs);
127 }
128
129 /* This checks stats for various SILC files and directories. First it
130    checks if ~/.silc directory exist and is owned by the correct user. If
131    it doesn't exist, it will create the directory. After that it checks if
132    user's Public and Private key files exists. If they doesn't exist they
133    will be created after return. */
134
135 int silc_client_check_silc_dir()
136 {
137   char filename[256], file_public_key[256], file_private_key[256];
138   char servfilename[256], clientfilename[256], friendsfilename[256];
139   struct stat st;
140   struct passwd *pw;
141
142   SILC_LOG_DEBUG(("Checking ~./silc directory"));
143
144   memset(filename, 0, sizeof(filename));
145   memset(file_public_key, 0, sizeof(file_public_key));
146   memset(file_private_key, 0, sizeof(file_private_key));
147
148   pw = getpwuid(getuid());
149   if (!pw) {
150     fprintf(stderr, "silc: %s\n", strerror(errno));
151     return FALSE;
152   }
153
154   /* We'll take home path from /etc/passwd file to be sure. */
155   snprintf(filename, sizeof(filename) - 1, "%s/", get_irssi_dir());
156   snprintf(servfilename, sizeof(servfilename) - 1, "%s/serverkeys",
157            get_irssi_dir());
158   snprintf(clientfilename, sizeof(clientfilename) - 1, "%s/clientkeys",
159            get_irssi_dir());
160   snprintf(friendsfilename, sizeof(friendsfilename) - 1, "%s/friends",
161            get_irssi_dir());
162
163   /*
164    * Check ~/.silc directory
165    */
166   if ((stat(filename, &st)) == -1) {
167     /* If dir doesn't exist */
168     if (errno == ENOENT) {
169       if (pw->pw_uid == geteuid()) {
170         if ((mkdir(filename, 0755)) == -1) {
171           fprintf(stderr, "Couldn't create `%s' directory\n", filename);
172           return FALSE;
173         }
174       } else {
175         fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
176                 filename);
177         return FALSE;
178       }
179     } else {
180       fprintf(stderr, "%s\n", strerror(errno));
181       return FALSE;
182     }
183   } else {
184
185     /* Check the owner of the dir */
186     if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
187       fprintf(stderr, "You don't seem to own `%s' directory\n",
188               filename);
189       return FALSE;
190     }
191
192 #if 0
193     /* Check the permissions of the dir */
194     if ((st.st_mode & 0777) != 0755) {
195       if ((chmod(filename, 0755)) == -1) {
196         fprintf(stderr, "Permissions for `%s' directory must be 0755\n",
197                 filename);
198         return FALSE;
199       }
200     }
201 #endif
202   }
203
204   /*
205    * Check ~./silc/serverkeys directory
206    */
207   if ((stat(servfilename, &st)) == -1) {
208     /* If dir doesn't exist */
209     if (errno == ENOENT) {
210       if (pw->pw_uid == geteuid()) {
211         if ((mkdir(servfilename, 0755)) == -1) {
212           fprintf(stderr, "Couldn't create `%s' directory\n", servfilename);
213           return FALSE;
214         }
215       } else {
216         fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
217                 servfilename);
218         return FALSE;
219       }
220     } else {
221       fprintf(stderr, "%s\n", strerror(errno));
222       return FALSE;
223     }
224   }
225
226   /*
227    * Check ~./silc/clientkeys directory
228    */
229   if ((stat(clientfilename, &st)) == -1) {
230     /* If dir doesn't exist */
231     if (errno == ENOENT) {
232       if (pw->pw_uid == geteuid()) {
233         if ((mkdir(clientfilename, 0755)) == -1) {
234           fprintf(stderr, "Couldn't create `%s' directory\n", clientfilename);
235           return FALSE;
236         }
237       } else {
238         fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
239                 clientfilename);
240         return FALSE;
241       }
242     } else {
243       fprintf(stderr, "%s\n", strerror(errno));
244       return FALSE;
245     }
246   }
247
248   /*
249    * Check ~./silc/friends directory
250    */
251   if ((stat(friendsfilename, &st)) == -1) {
252     /* If dir doesn't exist */
253     if (errno == ENOENT) {
254       if (pw->pw_uid == geteuid()) {
255         if ((mkdir(friendsfilename, 0755)) == -1) {
256           fprintf(stderr, "Couldn't create `%s' directory\n", friendsfilename);
257           return FALSE;
258         }
259       } else {
260         fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
261                 friendsfilename);
262         return FALSE;
263       }
264     } else {
265       fprintf(stderr, "%s\n", strerror(errno));
266       return FALSE;
267     }
268   }
269
270   /*
271    * Check Public and Private keys
272    */
273   snprintf(file_public_key, sizeof(file_public_key) - 1, "%s%s",
274            filename, SILC_CLIENT_PUBLIC_KEY_NAME);
275   snprintf(file_private_key, sizeof(file_private_key) - 1, "%s%s",
276            filename, SILC_CLIENT_PRIVATE_KEY_NAME);
277
278   if ((stat(file_public_key, &st)) == -1) {
279     /* If file doesn't exist */
280     if (errno == ENOENT) {
281       fprintf(stdout, "Running SILC for the first time\n");
282       silc_create_key_pair(SILC_CLIENT_DEF_PKCS,
283                            SILC_CLIENT_DEF_PKCS_LEN,
284                            file_public_key, file_private_key,
285                            NULL, NULL, NULL, NULL, FALSE);
286       printf("Press <Enter> to continue...\n");
287       getchar();
288     } else {
289       fprintf(stderr, "%s\n", strerror(errno));
290       return FALSE;
291     }
292   }
293
294   /* Check the owner of the public key */
295   if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
296     fprintf(stderr, "You don't seem to own your public key!?\n");
297     return FALSE;
298   }
299
300   if ((stat(file_private_key, &st)) == -1) {
301     /* If file doesn't exist */
302     if (errno == ENOENT) {
303       fprintf(stdout, "Your private key doesn't exist\n");
304       silc_create_key_pair(SILC_CLIENT_DEF_PKCS,
305                            SILC_CLIENT_DEF_PKCS_LEN,
306                            file_public_key, file_private_key,
307                            NULL, NULL, NULL, NULL, FALSE);
308       printf("Press <Enter> to continue...\n");
309       getchar();
310     } else {
311       fprintf(stderr, "%s\n", strerror(errno));
312       return FALSE;
313     }
314   }
315
316   /* Check the owner of the private key */
317   if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
318     fprintf(stderr, "You don't seem to own your private key!?\n");
319     return FALSE;
320   }
321
322   /* Check the permissions for the private key */
323   if ((st.st_mode & 0777) != 0600) {
324     fprintf(stderr, "Wrong permissions in your private key file `%s'!\n"
325             "Trying to change them ... ", file_private_key);
326     if ((chmod(file_private_key, 0600)) == -1) {
327       fprintf(stderr,
328               "Failed to change permissions for private key file!\n"
329               "Permissions for your private key file must be 0600.\n");
330       return FALSE;
331     }
332     fprintf(stderr, "Done.\n\n");
333   }
334
335   return TRUE;
336 }
337
338 /* Loads public and private key from files. */
339
340 int silc_client_load_keys(SilcClient client)
341 {
342   char pub[256], prv[256];
343   struct passwd *pw;
344   bool ret;
345
346   SILC_LOG_DEBUG(("Loading public and private keys"));
347
348   pw = getpwuid(getuid());
349   if (!pw)
350     return FALSE;
351
352   memset(prv, 0, sizeof(prv));
353   snprintf(prv, sizeof(prv) - 1, "%s/%s",
354            get_irssi_dir(), SILC_CLIENT_PRIVATE_KEY_NAME);
355
356   memset(pub, 0, sizeof(pub));
357   snprintf(pub, sizeof(pub) - 1, "%s/%s",
358            get_irssi_dir(), SILC_CLIENT_PUBLIC_KEY_NAME);
359
360   /* Try loading first with "" passphrase, for those that didn't set
361      passphrase for private key, and only if that fails let it prompt
362      for passphrase. */
363   ret = silc_load_key_pair(pub, prv, "", &irssi_pubkey, &irssi_privkey);
364   if (!ret)
365     ret = silc_load_key_pair(pub, prv, NULL, &irssi_pubkey, &irssi_privkey);
366
367   if (!ret)
368     SILC_LOG_ERROR(("Could not load key pair"));
369
370   return ret;
371 }
372
373 #ifdef SILC_PLUGIN
374 extern void irssi_redraw(void); /* dirty, I know */
375
376 void create_key_passphrase(const char *answer, CREATE_KEY_REC *rec)
377 {
378   char priv_key_file[128], pub_key_file[128];
379
380   signal_stop();
381
382   if ((rec->passphrase == NULL) && (answer) && (*answer != '\0')) {
383     rec->passphrase = g_strdup(answer);
384     keyboard_entry_redirect((SIGNAL_FUNC) create_key_passphrase,
385                             format_get_text("fe-common/silc", NULL, NULL,
386                                             NULL, SILCTXT_CONFIG_PASS_ASK2),
387                             ENTRY_REDIRECT_FLAG_HIDDEN, rec);
388     return;
389   }
390
391   if ((answer) && (*answer != '\0') && (rec->passphrase != NULL)) {
392     if (strcmp(answer, rec->passphrase)) {
393       printformat_module("fe-common/silc", NULL, NULL,
394                          MSGLEVEL_CRAP, SILCTXT_CONFIG_PASSMISMATCH);
395       g_free(rec->pkcs);
396       g_free(rec->passphrase);
397       g_free(rec);
398       return;
399     }
400   }
401
402   memset(priv_key_file, 0, sizeof(priv_key_file));
403   memset(pub_key_file, 0, sizeof(pub_key_file));
404   snprintf(priv_key_file, sizeof(priv_key_file) - 1, "%s/%s",
405            get_irssi_dir(), SILC_CLIENT_PRIVATE_KEY_NAME);
406   snprintf(pub_key_file, sizeof(pub_key_file) - 1, "%s/%s",
407            get_irssi_dir(), SILC_CLIENT_PUBLIC_KEY_NAME);
408
409   if (silc_create_key_pair(rec->pkcs, rec->bits, pub_key_file, priv_key_file,
410                        NULL, (rec->passphrase == NULL ? "" : rec->passphrase),
411                        NULL, NULL, FALSE) == TRUE)
412     printformat_module("fe-common/silc", NULL, NULL,
413                        MSGLEVEL_CRAP, SILCTXT_CONFIG_CREATE);
414   else
415     printformat_module("fe-common/silc", NULL, NULL,
416                        MSGLEVEL_CRAP, SILCTXT_CONFIG_CREATE_FAIL);
417
418   g_free(rec->passphrase);
419   g_free(rec->pkcs);
420   g_free(rec);
421   irssi_redraw();
422   
423 }
424
425 void change_private_key_passphrase(const char *answer, CREATE_KEY_REC *rec)
426 {
427   signal_stop();
428
429   if (rec->old == NULL) {
430     rec->old = g_strdup((answer == NULL ? "" : answer));
431     keyboard_entry_redirect((SIGNAL_FUNC) change_private_key_passphrase,
432                             format_get_text("fe-common/silc", NULL, NULL,
433                                             NULL, SILCTXT_CONFIG_PASS_ASK2),
434                             ENTRY_REDIRECT_FLAG_HIDDEN, rec);
435     return;
436   }
437   
438   if ((rec->passphrase == NULL) && (answer) && (*answer != '\0')) {
439     rec->passphrase = g_strdup(answer);
440     keyboard_entry_redirect((SIGNAL_FUNC) change_private_key_passphrase,
441                             format_get_text("fe-common/silc", NULL, NULL,
442                                             NULL, SILCTXT_CONFIG_PASS_ASK3),
443                             ENTRY_REDIRECT_FLAG_HIDDEN, rec);
444     return;
445   }
446
447   if ((answer) && (*answer != '\0') && (rec->passphrase != NULL)) {
448     if (strcmp(answer, rec->passphrase)) {
449       printformat_module("fe-common/silc", NULL, NULL,
450                          MSGLEVEL_CRAP, SILCTXT_CONFIG_PASSMISMATCH);
451       g_free(rec->old);
452       g_free(rec->file);
453       g_free(rec->pkcs);
454       g_free(rec->passphrase);
455       g_free(rec);
456       return;
457     }
458   }
459
460   if (silc_change_private_key_passphrase(rec->file, rec->old,
461                                      (rec->passphrase == NULL ? 
462                                       "" : rec->passphrase)) == TRUE)
463     printformat_module("fe-common/silc", NULL, NULL,
464                        MSGLEVEL_CRAP, SILCTXT_CONFIG_PASSCHANGE);
465   else
466     printformat_module("fe-common/silc", NULL, NULL,
467                        MSGLEVEL_CRAP, SILCTXT_CONFIG_PASSCHANGE_FAIL);
468   g_free(rec->old);
469   g_free(rec->file);
470   g_free(rec->passphrase);
471   g_free(rec->pkcs);
472   g_free(rec);
473
474 }
475 #endif