silc-client: handle key load error correctly
[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 - 2014 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   SilcBool 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   if (!ret) {
367     SILC_LOG_ERROR(("Could not load key pair"));
368     return ret;
369   }
370
371   if (silc_pkcs_private_key_get_len(irssi_privkey) < 4096) {
372     fprintf(stdout,
373             "warning: Your private key %s length is under 4096 bits. It is "
374             "recommended to use at least 4096 bits. Consider generating a "
375             "new key pair.\n", prv);
376     printf("Press <Enter> to continue...\n");
377     getchar();
378   }
379
380   return ret;
381 }
382
383 static SilcBool silc_keyboard_prompt_pending;
384
385 typedef struct {
386   SilcAsyncOperation async_context;
387   SILC_KEYBOARD_PROMPT_PROC user_prompt_proc;
388   void *user_context;
389   SilcBool aborted;
390   SilcBool *immediate_completion;
391 } *SilcKeyboardEntryRedirectContext;
392
393 static void silc_keyboard_entry_redirect_abort(SilcAsyncOperation op,
394                                                void *context)
395 {
396   SilcKeyboardEntryRedirectContext ctx = context;
397
398   /*
399    * Flag ourselves as aborted so the irssi callback doesn't do any real
400    * work here.
401    */
402   ctx->aborted = TRUE;
403
404   /*
405    * Call the user routine to notify it that we are aborting, so that it may
406    * clean up anything that needs cleaning up, e.g. references.  The user
407    * may not reference the SilcAsyncOperation beyond this abort call.  The
408    * recommended procedure is for the user prompt routine to null out its
409    * reference to the SilcAsyncOperation context.  The underlying context
410    * structure will be released when the actual wrappered callback fires,
411    * though the wrappered callback will not call into user code now that
412    * the operation has been aborted.
413    */
414   ctx->user_prompt_proc(NULL, ctx->user_context, KeyboardCompletionAborted);
415
416   /*
417    * Allow new prompt after we've abored despite us leaking Irssi prompt
418    * data.  It's more important to get new prompt up and this abort
419    * guarantees we handle things correctly towards silcclient library by
420    * calling the callback above.
421    */
422   silc_keyboard_prompt_pending = FALSE;
423 }
424
425 static void silc_keyboard_entry_redirect_completion(const char *line,
426                                                     void *context)
427 {
428   SilcKeyboardEntryRedirectContext ctx = context;
429
430   /*
431    * If we are aborted, then don't call the user routine.  Note that we
432    * already notified the user that they were aborted when the abort
433    * call was made in the first place, so the user should not have any
434    * dangling references at this point.
435    *
436    * Otherwise, call the user routine.
437    */
438   if (!ctx->aborted) {
439     ctx->user_prompt_proc(line, ctx->user_context,
440                           KeyboardCompletionSuccess);
441   }
442
443   /*
444    * If there's a flag to set on completion, such that we can detect when the
445    * operation finished immediately instead of being processed as a callback,
446    * then set that now.
447    */
448   if (ctx->immediate_completion)
449     *ctx->immediate_completion = TRUE;
450
451   /*
452    * Clean up our internal context structures.  Note that we are considered
453    * responsible for handling the SilcAsyncOperation release in this model,
454    * unless we were aborted, in which case the abort request has released it.
455    */
456   if (!ctx->aborted)
457     silc_async_free(ctx->async_context);
458
459   silc_free(ctx);
460
461   /*
462    * Mark us as not having a keyboard prompt pending.
463    */
464   silc_keyboard_prompt_pending = FALSE;
465 }
466
467 /* Prompt for user input. */
468 SilcBool silc_keyboard_entry_redirect(SILC_KEYBOARD_PROMPT_PROC prompt_func,
469                                       const char *entry,
470                                       int flags,
471                                       void *data,
472                                       SilcAsyncOperation *async)
473 {
474   SilcKeyboardEntryRedirectContext ctx;
475   SilcBool completed_now;
476
477   /*
478    * Check if we already have a keyboard prompt pending.  This sucks, but
479    * irssi stores the keyboard prompt data in a global, and if we request
480    * a prompt while there is already a prompt in progress, the old prompt
481    * data is leaked.  If irssi gets its act together, this can (and should)
482    * go away.
483    */
484   if (silc_keyboard_prompt_pending) {
485     prompt_func(NULL, data, KeyboardCompletionFailed);
486     return FALSE;
487   }
488
489   /*
490    * Allocate our context blocks.
491    */
492   ctx = (SilcKeyboardEntryRedirectContext)silc_calloc(1, sizeof(*ctx));
493   if (!ctx) {
494     prompt_func(NULL, data, KeyboardCompletionFailed);
495     return FALSE;
496   }
497
498   ctx->async_context = silc_async_alloc(silc_keyboard_entry_redirect_abort,
499                                         NULL, ctx);
500   if (!ctx->async_context) {
501     silc_free(ctx);
502     prompt_func(NULL, data, KeyboardCompletionFailed);
503     return FALSE;
504   }
505
506   /*
507    * Initially, we don't consider ourselves as having finished.
508    */
509   completed_now = FALSE;
510
511   /*
512    * Since irssi can't handle overlapping keyboard prompt requests, block
513    * future requests until we are finished.  N.B. This should really be
514    * handled inside of irssi, but this requires a breaking change to how
515    * keyboard callbacks are processed from an API perspective.  A problem
516    * exists where another user could call a keyboard redirect request
517    * external to silc while we have one pending, and cause ours to get
518    * lost, in which case we will get stuck denying future prompt requests.
519    *
520    * Fortunately, nobody else seems to use keyboard prompt requests, at least
521    * not that I can tell.
522    */
523   silc_keyboard_prompt_pending = TRUE;
524
525   /*
526    * Set up the call to the irssi keyboard entry redirection facility.
527    */
528
529   ctx->user_prompt_proc     = prompt_func;
530   ctx->user_context         = data;
531   ctx->aborted              = FALSE;
532   ctx->immediate_completion = &completed_now;
533
534   keyboard_entry_redirect((SIGNAL_FUNC)silc_keyboard_entry_redirect_completion,
535                           entry, 0, ctx);
536
537   ctx->immediate_completion = NULL;
538
539   /*
540    * If we completed immediately, then there is nothing to return as the async
541    * context has already been released.  In this case we have completed with a
542    * success status, but there is no SilcAsyncOperation context to return.
543    */
544   if (completed_now) {
545     *async = NULL;
546     return TRUE;
547   }
548
549   /*
550    * Otherwise, we must return an async operation context to the caller, and
551    * we must unset the immediate_completion flag as we don't want to be
552    * notified anymore since we're returning out.  Note that this is not safe
553    * if keyboard_entry_redirect can call from a different thread, but we are
554    * assuming that it doesn't as there's already many other things that seem
555    * to make this assumption.
556    */
557   if (async)
558     *async = ctx->async_context;
559
560   /*
561    * All done.  Irssi will invoke the callback on this thread at a later point
562    * in time.
563    */
564   return TRUE;
565 }
566
567 #ifdef SILC_PLUGIN
568 void create_key_passphrase(const char *answer, CREATE_KEY_REC *rec)
569 {
570   char priv_key_file[128], pub_key_file[128];
571
572   signal_stop();
573
574   if ((rec->passphrase == NULL) && (answer) && (*answer != '\0')) {
575     rec->passphrase = g_strdup(answer);
576     keyboard_entry_redirect((SIGNAL_FUNC) create_key_passphrase,
577                             format_get_text("fe-common/silc", NULL, NULL,
578                                             NULL, SILCTXT_CONFIG_PASS_ASK2),
579                             ENTRY_REDIRECT_FLAG_HIDDEN, rec);
580     return;
581   }
582
583   if ((answer) && (*answer != '\0') && (rec->passphrase != NULL)) {
584     if (strcmp(answer, rec->passphrase)) {
585       printformat_module("fe-common/silc", NULL, NULL,
586                          MSGLEVEL_CRAP, SILCTXT_CONFIG_PASSMISMATCH);
587       g_free(rec->pkcs);
588       g_free(rec->passphrase);
589       g_free(rec);
590       return;
591     }
592   }
593
594   memset(priv_key_file, 0, sizeof(priv_key_file));
595   memset(pub_key_file, 0, sizeof(pub_key_file));
596   snprintf(priv_key_file, sizeof(priv_key_file) - 1, "%s/%s",
597            get_irssi_dir(), SILC_CLIENT_PRIVATE_KEY_NAME);
598   snprintf(pub_key_file, sizeof(pub_key_file) - 1, "%s/%s",
599            get_irssi_dir(), SILC_CLIENT_PUBLIC_KEY_NAME);
600
601   if (silc_create_key_pair(rec->pkcs, rec->bits, pub_key_file, priv_key_file,
602                        NULL, (rec->passphrase == NULL ? "" : rec->passphrase),
603                        NULL, NULL, FALSE) == TRUE)
604     printformat_module("fe-common/silc", NULL, NULL,
605                        MSGLEVEL_CRAP, SILCTXT_CONFIG_CREATE);
606   else
607     printformat_module("fe-common/silc", NULL, NULL,
608                        MSGLEVEL_CRAP, SILCTXT_CONFIG_CREATE_FAIL);
609
610   g_free(rec->passphrase);
611   g_free(rec->pkcs);
612   g_free(rec);
613 }
614
615 void change_private_key_passphrase(const char *answer, CREATE_KEY_REC *rec)
616 {
617   signal_stop();
618
619   if (rec->old == NULL) {
620     rec->old = g_strdup((answer == NULL ? "" : answer));
621     keyboard_entry_redirect((SIGNAL_FUNC) change_private_key_passphrase,
622                             format_get_text("fe-common/silc", NULL, NULL,
623                                             NULL, SILCTXT_CONFIG_PASS_ASK2),
624                             ENTRY_REDIRECT_FLAG_HIDDEN, rec);
625     return;
626   }
627
628   if ((rec->passphrase == NULL) && (answer) && (*answer != '\0')) {
629     rec->passphrase = g_strdup(answer);
630     keyboard_entry_redirect((SIGNAL_FUNC) change_private_key_passphrase,
631                             format_get_text("fe-common/silc", NULL, NULL,
632                                             NULL, SILCTXT_CONFIG_PASS_ASK3),
633                             ENTRY_REDIRECT_FLAG_HIDDEN, rec);
634     return;
635   }
636
637   if ((answer) && (*answer != '\0') && (rec->passphrase != NULL)) {
638     if (strcmp(answer, rec->passphrase)) {
639       printformat_module("fe-common/silc", NULL, NULL,
640                          MSGLEVEL_CRAP, SILCTXT_CONFIG_PASSMISMATCH);
641       g_free(rec->old);
642       g_free(rec->file);
643       g_free(rec->pkcs);
644       g_free(rec->passphrase);
645       g_free(rec);
646       return;
647     }
648   }
649
650   if (silc_change_private_key_passphrase(rec->file, rec->old,
651                                      (rec->passphrase == NULL ?
652                                       "" : rec->passphrase)) == TRUE)
653     printformat_module("fe-common/silc", NULL, NULL,
654                        MSGLEVEL_CRAP, SILCTXT_CONFIG_PASSCHANGE);
655   else
656     printformat_module("fe-common/silc", NULL, NULL,
657                        MSGLEVEL_CRAP, SILCTXT_CONFIG_PASSCHANGE_FAIL);
658   g_free(rec->old);
659   g_free(rec->file);
660   g_free(rec->passphrase);
661   g_free(rec->pkcs);
662   g_free(rec);
663
664 }
665 #endif