5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2006 Pekka Riikonen
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.
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.
23 #include "net-nonblock.h"
24 #include "net-sendbuffer.h"
34 #include "channels-setup.h"
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"
42 #include "fe-common/core/printtext.h"
43 #include "fe-common/core/keyboard.h"
44 #include "fe-common/silc/module-formats.h"
49 void silc_client_print_list(char *list)
54 items = g_strsplit(list, ",", -1);
56 while (items[i] != NULL)
57 printformat_module("fe-common/silc", NULL, NULL,
58 MSGLEVEL_CRAP, SILCTXT_CONFIG_LIST,
65 /* Lists supported ciphers */
67 void silc_client_list_ciphers()
69 char *ciphers = silc_cipher_get_supported();
71 printformat_module("fe-common/silc", NULL, NULL,
72 MSGLEVEL_CRAP, SILCTXT_CONFIG_ALGOS,
74 silc_client_print_list(ciphers);
76 fprintf(stdout, "%s\n", ciphers);
81 /* Lists supported hash functions */
83 void silc_client_list_hash_funcs()
85 char *hash = silc_hash_get_supported();
87 printformat_module("fe-common/silc", NULL, NULL,
88 MSGLEVEL_CRAP, SILCTXT_CONFIG_ALGOS,
90 silc_client_print_list(hash);
92 fprintf(stdout, "%s\n", hash);
97 /* Lists supported hash functions */
99 void silc_client_list_hmacs()
101 char *hash = silc_hmac_get_supported();
103 printformat_module("fe-common/silc", NULL, NULL,
104 MSGLEVEL_CRAP, SILCTXT_CONFIG_ALGOS,
106 silc_client_print_list(hash);
108 fprintf(stdout, "%s\n", hash);
113 /* Lists supported PKCS algorithms */
115 void silc_client_list_pkcs()
117 char *pkcs = silc_pkcs_get_supported();
119 printformat_module("fe-common/silc", NULL, NULL,
120 MSGLEVEL_CRAP, SILCTXT_CONFIG_ALGOS,
122 silc_client_print_list(pkcs);
124 fprintf(stdout, "%s\n", pkcs);
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. */
135 int silc_client_check_silc_dir()
137 char filename[256], file_public_key[256], file_private_key[256];
138 char servfilename[256], clientfilename[256], friendsfilename[256];
142 SILC_LOG_DEBUG(("Checking ~./silc directory"));
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));
148 pw = getpwuid(getuid());
150 fprintf(stderr, "silc: %s\n", strerror(errno));
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",
158 snprintf(clientfilename, sizeof(clientfilename) - 1, "%s/clientkeys",
160 snprintf(friendsfilename, sizeof(friendsfilename) - 1, "%s/friends",
164 * Check ~/.silc directory
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);
175 fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
180 fprintf(stderr, "%s\n", strerror(errno));
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",
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",
205 * Check ~./silc/serverkeys directory
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);
216 fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
221 fprintf(stderr, "%s\n", strerror(errno));
227 * Check ~./silc/clientkeys directory
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);
238 fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
243 fprintf(stderr, "%s\n", strerror(errno));
249 * Check ~./silc/friends directory
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);
260 fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
265 fprintf(stderr, "%s\n", strerror(errno));
271 * Check Public and Private keys
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);
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");
289 fprintf(stderr, "%s\n", strerror(errno));
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");
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");
311 fprintf(stderr, "%s\n", strerror(errno));
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");
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) {
328 "Failed to change permissions for private key file!\n"
329 "Permissions for your private key file must be 0600.\n");
332 fprintf(stderr, "Done.\n\n");
338 /* Loads public and private key from files. */
340 int silc_client_load_keys(SilcClient client)
342 char pub[256], prv[256];
346 SILC_LOG_DEBUG(("Loading public and private keys"));
348 pw = getpwuid(getuid());
352 memset(prv, 0, sizeof(prv));
353 snprintf(prv, sizeof(prv) - 1, "%s/%s",
354 get_irssi_dir(), SILC_CLIENT_PRIVATE_KEY_NAME);
356 memset(pub, 0, sizeof(pub));
357 snprintf(pub, sizeof(pub) - 1, "%s/%s",
358 get_irssi_dir(), SILC_CLIENT_PUBLIC_KEY_NAME);
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
363 ret = silc_load_key_pair(pub, prv, "", &irssi_pubkey, &irssi_privkey);
365 ret = silc_load_key_pair(pub, prv, NULL, &irssi_pubkey, &irssi_privkey);
368 SILC_LOG_ERROR(("Could not load key pair"));
373 static bool silc_keyboard_prompt_pending;
377 SilcAsyncOperation async_context;
378 SILC_KEYBOARD_PROMPT_PROC user_prompt_proc;
381 bool *immediate_completion;
382 } * SilcKeyboardEntryRedirectContext;
384 static void silc_keyboard_entry_redirect_abort(
385 SilcAsyncOperation op,
388 SilcKeyboardEntryRedirectContext ctx = (SilcKeyboardEntryRedirectContext)context;
391 * Flag ourselves as aborted so the irssi callback doesn't do any real
398 * Call the user routine to notify it that we are aborting, so that it may
399 * clean up anything that needs cleaning up, e.g. references. The user
400 * may not reference the SilcAsyncOperation beyond this abort call. The
401 * recommended procedure is for the user prompt routine to null out its
402 * reference to the SilcAsyncOperation context. The underlying context
403 * structure will be released when the actual wrappered callback fires,
404 * though the wrappered callback will not call into user code now that
405 * the operation has been aborted.
408 ctx->user_prompt_proc(NULL, ctx->user_context, KeyboardCompletionAborted);
411 static void silc_keyboard_entry_redirect_completion(
415 SilcKeyboardEntryRedirectContext ctx = (SilcKeyboardEntryRedirectContext)context;
418 * If we are aborted, then don't call the user routine. Note that we
419 * already notified the user that they were aborted when the abort
420 * call was made in the first place, so the user should not have any
421 * dangling references at this point.
423 * Otherwise, call the user routine.
428 ctx->user_prompt_proc(line, ctx->user_context,
429 KeyboardCompletionSuccess);
433 * If there's a flag to set on completion, such that we can detect when the
434 * operation finished immediately instead of being processed as a callback,
438 if (ctx->immediate_completion)
439 *ctx->immediate_completion = TRUE;
442 * Clean up our internal context structures. Note that we are considered
443 * responsible for handling the SilcAsyncOperation release in this model,
444 * unless we were aborted, in which case the abort request has released it.
448 silc_async_free(ctx->async_context);
453 * Mark us as not having a keyboard prompt pending.
456 silc_keyboard_prompt_pending = FALSE;
459 /* Prompt for user input. */
460 bool silc_keyboard_entry_redirect(
461 SILC_KEYBOARD_PROMPT_PROC prompt_func,
465 SilcAsyncOperation *async)
467 SilcKeyboardEntryRedirectContext ctx;
471 * Check if we already have a keyboard prompt pending. This sucks, but
472 * irssi stores the keyboard prompt data in a global, and if we request
473 * a prompt while there is already a prompt in progress, the old prompt
474 * data is leaked. If irssi gets its act together, this can (and should)
478 if (silc_keyboard_prompt_pending)
480 prompt_func(NULL, data, KeyboardCompletionFailed);
485 * Allocate our context blocks.
488 ctx = (SilcKeyboardEntryRedirectContext)silc_calloc(1, sizeof(*ctx));
492 prompt_func(NULL, data, KeyboardCompletionFailed);
496 ctx->async_context = silc_async_alloc(silc_keyboard_entry_redirect_abort,
499 if (!ctx->async_context)
502 prompt_func(NULL, data, KeyboardCompletionFailed);
507 * Initially, we don't consider ourselves as having finished.
510 completed_now = FALSE;
513 * Since irssi can't handle overlapping keyboard prompt requests, block
514 * future requests until we are finished. N.B. This should really be
515 * handled inside of irssi, but this requires a breaking change to how
516 * keyboard callbacks are processed from an API perspective. A problem
517 * exists where another user could call a keyboard redirect request
518 * external to silc while we have one pending, and cause ours to get
519 * lost, in which case we will get stuck denying future prompt requests.
521 * Fortunately, nobody else seems to use keyboard prompt requests, at least
522 * not that I can tell.
525 silc_keyboard_prompt_pending = TRUE;
528 * Set up the call to the irssi keyboard entry redirection facility.
531 ctx->user_prompt_proc = prompt_func;
532 ctx->user_context = data;
533 ctx->aborted = FALSE;
534 ctx->immediate_completion = &completed_now;
536 keyboard_entry_redirect((SIGNAL_FUNC)silc_keyboard_entry_redirect_completion,
539 ctx->immediate_completion = NULL;
542 * If we completed immediately, then there is nothing to return as the async
543 * context has already been released. In this case we have completed with a
544 * success status, but there is no SilcAsyncOperation context to return.
554 * Otherwise, we must return an async operation context to the caller, and
555 * we must unset the immediate_completion flag as we don't want to be
556 * notified anymore since we're returning out. Note that this is not safe
557 * if keyboard_entry_redirect can call from a different thread, but we are
558 * assuming that it doesn't as there's already many other things that seem
559 * to make this assumption.
562 *async = ctx->async_context;
565 * All done. Irssi will invoke the callback on this thread at a later point
573 void create_key_passphrase(const char *answer, CREATE_KEY_REC *rec)
575 char priv_key_file[128], pub_key_file[128];
579 if ((rec->passphrase == NULL) && (answer) && (*answer != '\0')) {
580 rec->passphrase = g_strdup(answer);
582 * This can continue to use keyboard_entry_redirect as it's a one-time at
583 * initialization function. If create_key_passphrase is used
584 * somewhere else, then this needs to be ripped out and changed to use
585 * the new, more correct silc_keyboard_entry_redirect.
587 keyboard_entry_redirect((SIGNAL_FUNC) create_key_passphrase,
588 format_get_text("fe-common/silc", NULL, NULL,
589 NULL, SILCTXT_CONFIG_PASS_ASK2),
590 ENTRY_REDIRECT_FLAG_HIDDEN, rec);
594 if ((answer) && (*answer != '\0') && (rec->passphrase != NULL)) {
595 if (strcmp(answer, rec->passphrase)) {
596 printformat_module("fe-common/silc", NULL, NULL,
597 MSGLEVEL_CRAP, SILCTXT_CONFIG_PASSMISMATCH);
599 g_free(rec->passphrase);
605 memset(priv_key_file, 0, sizeof(priv_key_file));
606 memset(pub_key_file, 0, sizeof(pub_key_file));
607 snprintf(priv_key_file, sizeof(priv_key_file) - 1, "%s/%s",
608 get_irssi_dir(), SILC_CLIENT_PRIVATE_KEY_NAME);
609 snprintf(pub_key_file, sizeof(pub_key_file) - 1, "%s/%s",
610 get_irssi_dir(), SILC_CLIENT_PUBLIC_KEY_NAME);
612 if (silc_create_key_pair(rec->pkcs, rec->bits, pub_key_file, priv_key_file,
613 NULL, (rec->passphrase == NULL ? "" : rec->passphrase),
614 NULL, NULL, FALSE) == TRUE)
615 printformat_module("fe-common/silc", NULL, NULL,
616 MSGLEVEL_CRAP, SILCTXT_CONFIG_CREATE);
618 printformat_module("fe-common/silc", NULL, NULL,
619 MSGLEVEL_CRAP, SILCTXT_CONFIG_CREATE_FAIL);
621 g_free(rec->passphrase);
626 void change_private_key_passphrase(const char *answer, CREATE_KEY_REC *rec)
630 if (rec->old == NULL) {
631 rec->old = g_strdup((answer == NULL ? "" : answer));
633 * This can continue to use keyboard_entry_redirect as it's a one-time at
634 * initialization function. If change_private_key_passphrase is used
635 * somewhere else, then this needs to be ripped out and changed to use
636 * the new, more correct silc_keyboard_entry_redirect.
638 keyboard_entry_redirect((SIGNAL_FUNC) change_private_key_passphrase,
639 format_get_text("fe-common/silc", NULL, NULL,
640 NULL, SILCTXT_CONFIG_PASS_ASK2),
641 ENTRY_REDIRECT_FLAG_HIDDEN, rec);
645 if ((rec->passphrase == NULL) && (answer) && (*answer != '\0')) {
646 rec->passphrase = g_strdup(answer);
648 * This can continue to use keyboard_entry_redirect as it's a one-time at
649 * initialization function. If change_private_key_passphrase is used
650 * somewhere else, then this needs to be ripped out and changed to use
651 * the new, more correct silc_keyboard_entry_redirect.
653 keyboard_entry_redirect((SIGNAL_FUNC) change_private_key_passphrase,
654 format_get_text("fe-common/silc", NULL, NULL,
655 NULL, SILCTXT_CONFIG_PASS_ASK3),
656 ENTRY_REDIRECT_FLAG_HIDDEN, rec);
660 if ((answer) && (*answer != '\0') && (rec->passphrase != NULL)) {
661 if (strcmp(answer, rec->passphrase)) {
662 printformat_module("fe-common/silc", NULL, NULL,
663 MSGLEVEL_CRAP, SILCTXT_CONFIG_PASSMISMATCH);
667 g_free(rec->passphrase);
673 if (silc_change_private_key_passphrase(rec->file, rec->old,
674 (rec->passphrase == NULL ?
675 "" : rec->passphrase)) == TRUE)
676 printformat_module("fe-common/silc", NULL, NULL,
677 MSGLEVEL_CRAP, SILCTXT_CONFIG_PASSCHANGE);
679 printformat_module("fe-common/silc", NULL, NULL,
680 MSGLEVEL_CRAP, SILCTXT_CONFIG_PASSCHANGE_FAIL);
683 g_free(rec->passphrase);