5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 1997 - 2000 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; either version 2 of the License, or
12 (at your option) any later version.
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.
23 * Revision 1.3 2000/07/07 06:53:45 priikone
24 * Added support for server public key verification.
26 * Revision 1.2 2000/07/05 06:11:00 priikone
27 * Added ~./silc directory checking, autoloading of keys and
28 * tweaked the key pair generation function.
30 * Revision 1.1.1.1 2000/06/27 11:36:56 priikone
31 * Imported from internal CVS/Added Log headers.
36 #include "clientincludes.h"
38 /* Internal routine used to print lines to window. This can split the
39 line neatly if a word would overlap the line. */
41 static void silc_print_to_window(WINDOW *win, char *message)
45 str_len = strlen(message);
47 if (str_len > COLS - 1) {
48 /* Split overlapping words to next line */
49 /* XXX In principal this is wrong as this modifies the original
50 string as it replaces the last ' ' with '\n'. This could be done
51 with little more work so that it would not replace anything. */
55 while (len && message[len] != ' ')
68 wprintw(win, "%s", message);
72 /* Prints a message with three star (*) sign before the actual message
73 on the current output window. This is used to print command outputs
74 and error messages. */
75 /* XXX Change to accept SilcClientWindow and use output window
76 from there (the pointer to the output window must be added to the
77 SilcClientWindow object. */
79 void silc_say(SilcClient client, char *msg, ...)
84 memset(message, 0, sizeof(message));
85 strncat(message, "\n*** ", 5);
88 vsprintf(message + 5, msg, vp);
91 /* Print the message */
92 silc_print_to_window(client->screen->output_win[0], message);
95 /* Prints message to the screen. This is used to print the messages
96 user is typed and message that came on channels. */
98 void silc_print(SilcClient client, char *msg, ...)
103 memset(message, 0, sizeof(message));
104 strncat(message, "\n ", 2);
107 vsprintf(message + 1, msg, vp);
110 /* Print the message */
111 silc_print_to_window(client->screen->output_win[0], message);
114 /* Returns user's mail path */
116 char *silc_get_mail_path()
118 char pathbuf[MAXPATHLEN];
121 if ((path = (char *)getenv("MAIL")) != 0) {
122 strncpy(pathbuf, path, strlen(path));
124 strcpy(pathbuf, _PATH_MAILDIR);
125 strcat(pathbuf, "/");
126 strcat(pathbuf, silc_get_username());
129 return strdup(pathbuf);
132 /* gets the number of the user's mails, if possible */
134 int silc_get_number_of_emails()
141 filename = silc_get_mail_path();
143 tl = fopen(filename, "r");
145 fprintf(stderr, "Couldn't open mail file (%s).\n", filename);
147 while((fscanf(tl, "%s", data)) != EOF) {
148 if(!strcmp(data, "Subject:"))
158 /* Returns the username of the user. If the global variable LOGNAME
159 does not exists we will get the name from the password file. */
161 char *silc_get_username()
163 char *logname = NULL;
165 logname = strdup(getenv("LOGNAME"));
167 logname = getlogin();
171 pw = getpwuid(getuid());
173 fprintf(stderr, "silc_get_username: %s\n", strerror(errno));
177 logname = strdup(pw->pw_name);
184 /* Returns the real name of ther user. */
186 char *silc_get_real_name()
188 char *realname = NULL;
191 pw = getpwuid(getuid());
193 fprintf(stderr, "silc_get_username: %s\n", strerror(errno));
197 if (strchr(pw->pw_gecos, ','))
198 *strchr(pw->pw_gecos, ',') = 0;
200 realname = strdup(pw->pw_gecos);
205 /* Returns time til next minute changes. Used to update the clock when
208 int silc_client_time_til_next_min()
214 min = localtime(&curtime);
216 return 60 - min->tm_sec;
219 /* Asks passphrase from user on the input line. */
221 char *silc_client_ask_passphrase(SilcClient client)
223 char pass1[256], pass2[256];
230 wattroff(client->screen->input_win, A_INVIS);
231 silc_screen_input_print_prompt(client->screen, "Passphrase: ");
232 wattron(client->screen->input_win, A_INVIS);
235 memset(pass1, 0, sizeof(pass1));
236 wgetnstr(client->screen->input_win, pass1, sizeof(pass1));
238 /* Print retype prompt */
239 wattroff(client->screen->input_win, A_INVIS);
240 silc_screen_input_print_prompt(client->screen, "Retype passphrase: ");
241 wattron(client->screen->input_win, A_INVIS);
244 memset(pass2, 0, sizeof(pass2));
245 wgetnstr(client->screen->input_win, pass2, sizeof(pass2));
247 if (!strncmp(pass1, pass2, strlen(pass2)))
253 ret = silc_calloc(strlen(pass1), sizeof(char));
254 memcpy(ret, pass1, strlen(pass1));
256 memset(pass1, 0, sizeof(pass1));
257 memset(pass2, 0, sizeof(pass2));
259 wattroff(client->screen->input_win, A_INVIS);
260 silc_screen_input_reset(client->screen);
265 /* Asks yes/no from user on the input line. Returns TRUE on "yes" and
268 int silc_client_ask_yes_no(SilcClient client, char *prompt)
274 silc_screen_input_reset(client->screen);
277 wattroff(client->screen->input_win, A_INVIS);
278 silc_screen_input_print_prompt(client->screen, prompt);
281 memset(answer, 0, sizeof(answer));
283 wgetnstr(client->screen->input_win, answer, sizeof(answer));
284 if (!strncasecmp(answer, "yes", strlen(answer)) ||
285 !strncasecmp(answer, "y", strlen(answer))) {
287 } else if (!strncasecmp(answer, "no", strlen(answer)) ||
288 !strncasecmp(answer, "n", strlen(answer))) {
291 silc_say(client, "Type yes or no");
296 silc_screen_input_reset(client->screen);
301 /* Lists supported (builtin) ciphers */
303 void silc_client_list_ciphers()
308 /* Lists supported (builtin) hash functions */
310 void silc_client_list_hash_funcs()
315 /* Lists supported PKCS algorithms */
317 void silc_client_list_pkcs()
322 /* Displays input prompt on command line and takes input data from user */
324 char *silc_client_get_input(const char *prompt)
329 fd = open("/dev/tty", O_RDONLY);
331 fprintf(stderr, "silc: %s\n", strerror(errno));
335 memset(input, 0, sizeof(input));
337 printf("%s", prompt);
340 if ((read(fd, input, sizeof(input))) < 0) {
341 fprintf(stderr, "silc: %s\n", strerror(errno));
345 if (strlen(input) <= 1)
348 if (strchr(input, '\n'))
349 *strchr(input, '\n') = '\0';
351 return strdup(input);
354 /* Displays prompt on command line and takes passphrase with echo
357 char *silc_client_get_passphrase(const char *prompt)
364 struct termios to_old;
366 fd = open("/dev/tty", O_RDONLY);
368 fprintf(stderr, "silc: %s\n", strerror(errno));
372 signal(SIGINT, SIG_IGN);
374 /* Get terminal info */
379 to.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
380 tcsetattr(fd, TCSANOW, &to);
382 memset(input, 0, sizeof(input));
384 printf("%s", prompt);
387 if ((read(fd, input, sizeof(input))) < 0) {
388 fprintf(stderr, "silc: %s\n", strerror(errno));
392 if (strlen(input) <= 1) {
393 tcsetattr(fd, TCSANOW, &to_old);
397 if (strchr(input, '\n'))
398 *strchr(input, '\n') = '\0';
400 /* Restore old terminfo */
401 tcsetattr(fd, TCSANOW, &to_old);
402 signal(SIGINT, SIG_DFL);
404 ret = silc_calloc(strlen(input), sizeof(char));
405 memcpy(ret, input, strlen(input));
406 memset(input, 0, sizeof(input));
413 /* Returns identifier string for public key generation. */
415 char *silc_client_create_identifier()
417 char *username = NULL, *realname = NULL;
418 char hostname[256], email[256];
421 realname = silc_get_real_name();
424 memset(hostname, 0, sizeof(hostname));
425 gethostname(hostname, sizeof(hostname));
427 /* Get username (mandatory) */
428 username = silc_get_username();
432 /* Create default email address, whether it is right or not */
433 snprintf(email, sizeof(email), "%s@%s", username, hostname);
435 return silc_pkcs_encode_identifier(username, hostname, realname, email,
439 /* Creates new public key and private key pair. This is used only
440 when user wants to create new key pair from command line. */
442 int silc_client_create_key_pair(char *pkcs_name, int bits,
443 char *public_key, char *private_key,
445 SilcPublicKey *ret_pub_key,
446 SilcPrivateKey *ret_prv_key)
449 SilcPublicKey pub_key;
450 SilcPrivateKey prv_key;
453 unsigned int key_len;
454 char *pkfile = NULL, *prvfile = NULL;
456 if (!pkcs_name || !public_key || !private_key)
458 New pair of keys will be created. Please, answer to following questions.\n\
464 silc_client_get_input("PKCS name (l to list names) [rsa]: ");
466 pkcs_name = strdup("rsa");
468 if (*pkcs_name == 'l' || *pkcs_name == 'L') {
469 silc_client_list_pkcs();
470 silc_free(pkcs_name);
475 if (!silc_pkcs_is_supported(pkcs_name)) {
476 fprintf(stderr, "Unsupported PKCS `%s'", pkcs_name);
483 silc_client_get_input("Key length in bits [1024]: ");
491 char *def = silc_client_create_identifier();
494 snprintf(def, sizeof(def), "Public key identifier [%s]: ", def);
496 snprintf(def, sizeof(def),
497 "Public key identifier (eg. UN=priikone, HN=poseidon.pspt.fi, "
498 "RN=Pekka Riikonen, E=priikone@poseidon.pspt.fi): ");
501 identifier = silc_client_get_input(def);
509 rng = silc_rng_alloc();
511 silc_math_primegen_init();
515 pkfile = silc_client_get_input("Public key filename: ");
517 printf("Public key filename must be defined\n");
526 prvfile = silc_client_get_input("Private key filename: ");
528 printf("Private key filename must be defined\n");
532 prvfile = private_key;
536 silc_pkcs_alloc(pkcs_name, &pkcs);
537 pkcs->pkcs->init(pkcs->context, bits, rng);
539 /* Save public key into file */
540 key = silc_pkcs_get_public_key(pkcs, &key_len);
541 pub_key = silc_pkcs_public_key_alloc(pkcs->pkcs->name, identifier,
543 silc_pkcs_save_public_key(pkfile, pub_key);
545 *ret_pub_key = pub_key;
547 memset(key, 0, sizeof(key_len));
550 /* Save private key into file */
551 key = silc_pkcs_get_private_key(pkcs, &key_len);
552 prv_key = silc_pkcs_private_key_alloc(pkcs->pkcs->name, key, key_len);
554 silc_pkcs_save_private_key(prvfile, prv_key, NULL);
556 *ret_prv_key = prv_key;
558 printf("Public key has been save into `%s'.\n", pkfile);
559 printf("Private key has been saved into `%s'.\n", prvfile);
560 printf("Press <Enter> to continue...\n");
563 memset(key, 0, sizeof(key_len));
566 silc_math_primegen_uninit();
568 silc_pkcs_free(pkcs);
573 /* This checks stats for various SILC files and directories. First it
574 checks if ~/.silc directory exist and is owned by the correct user. If
575 it doesn't exist, it will create the directory. After that it checks if
576 user's Public and Private key files exists and that they aren't expired.
577 If they doesn't exist or they are expired, they will be (re)created
580 int silc_client_check_silc_dir()
582 char filename[256], file_public_key[256], file_private_key[256];
583 char servfilename[256];
587 int firstime = FALSE;
588 time_t curtime, modtime;
590 SILC_LOG_DEBUG(("Checking ~./silc directory"));
592 memset(filename, 0, sizeof(filename));
593 memset(file_public_key, 0, sizeof(file_public_key));
594 memset(file_private_key, 0, sizeof(file_private_key));
596 pw = getpwuid(getuid());
598 fprintf(stderr, "silc: %s\n", strerror(errno));
602 identifier = silc_client_create_identifier();
604 /* We'll take home path from /etc/passwd file to be sure. */
605 snprintf(filename, sizeof(filename) - 1, "%s/.silc/", pw->pw_dir);
606 snprintf(servfilename, sizeof(servfilename) - 1, "%s/.silc/serverkeys",
610 * Check ~/.silc directory
612 if ((stat(filename, &st)) == -1) {
613 /* If dir doesn't exist */
614 if (errno == ENOENT) {
615 if (pw->pw_uid == geteuid()) {
616 if ((mkdir(filename, 0755)) == -1) {
617 fprintf(stderr, "Couldn't create `%s' directory\n", filename);
621 /* Directory was created. First time running SILC */
624 fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
629 fprintf(stderr, "%s\n", strerror(errno));
634 /* Check the owner of the dir */
635 if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
636 fprintf(stderr, "You don't seem to own `%s' directory\n",
641 /* Check the permissions of the dir */
642 if ((st.st_mode & 0777) != 0755) {
643 if ((chmod(filename, 0755)) == -1) {
644 fprintf(stderr, "Permissions for `%s' directory must be 0755\n",
651 * Check ~./silc/serverkeys directory
653 if ((stat(servfilename, &st)) == -1) {
654 /* If dir doesn't exist */
655 if (errno == ENOENT) {
656 if (pw->pw_uid == geteuid()) {
657 if ((mkdir(servfilename, 0755)) == -1) {
658 fprintf(stderr, "Couldn't create `%s' directory\n", servfilename);
662 fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
667 fprintf(stderr, "%s\n", strerror(errno));
673 * Check Public and Private keys
675 snprintf(file_public_key, sizeof(file_public_key) - 1, "%s%s",
676 filename, SILC_CLIENT_PUBLIC_KEY_NAME);
677 snprintf(file_private_key, sizeof(file_private_key) - 1, "%s%s",
678 filename, SILC_CLIENT_PRIVATE_KEY_NAME);
680 /* If running SILC first time */
682 fprintf(stdout, "Running SILC for the first time\n");
683 silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS,
684 SILC_CLIENT_DEF_PKCS_LEN,
685 file_public_key, file_private_key,
686 identifier, NULL, NULL);
690 if ((stat(file_public_key, &st)) == -1) {
691 /* If file doesn't exist */
692 if (errno == ENOENT) {
693 fprintf(stdout, "Your public key doesn't exist\n");
694 silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS,
695 SILC_CLIENT_DEF_PKCS_LEN,
697 file_private_key, identifier, NULL, NULL);
699 fprintf(stderr, "%s\n", strerror(errno));
704 if ((stat(file_private_key, &st)) == -1) {
705 /* If file doesn't exist */
706 if (errno == ENOENT) {
707 fprintf(stdout, "Your private key doesn't exist\n");
708 silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS,
709 SILC_CLIENT_DEF_PKCS_LEN,
711 file_private_key, identifier, NULL, NULL);
713 fprintf(stderr, "%s\n", strerror(errno));
718 /* Check the owner of the public key */
719 if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
720 fprintf(stderr, "You don't seem to own your public key!?\n");
724 /* Check the owner of the private key */
725 if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
726 fprintf(stderr, "You don't seem to own your private key!?\n");
730 /* Check the permissions for the private key */
731 if ((st.st_mode & 0777) != 0600) {
732 fprintf(stderr, "Wrong permissions in your private key file `%s'!\n"
733 "Trying to change them ... ", file_private_key);
734 if ((chmod(file_private_key, 0600)) == -1) {
736 "Failed to change permissions for private key file!\n"
737 "Permissions for your private key file must be 0600.\n");
740 fprintf(stderr, "Done.\n\n");
743 /* See if the key has expired. */
744 modtime = st.st_mtime; /* last modified */
745 curtime = time(0) - modtime;
747 /* 86400 is seconds in a day. */
748 if (curtime >= (86400 * SILC_CLIENT_KEY_EXPIRES)) {
750 "--------------------------------------------------\n"
751 "Your private key has expired and needs to be\n"
752 "recreated. This will be done automatically now.\n"
753 "Your new key will expire in %d days from today.\n"
754 "--------------------------------------------------\n",
755 SILC_CLIENT_KEY_EXPIRES);
757 silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS,
758 SILC_CLIENT_DEF_PKCS_LEN,
760 file_private_key, identifier, NULL, NULL);
764 silc_free(identifier);
769 /* Loads public and private key from files. */
771 int silc_client_load_keys(SilcClient client)
776 SILC_LOG_DEBUG(("Loading public and private keys"));
778 pw = getpwuid(getuid());
782 memset(filename, 0, sizeof(filename));
783 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%s",
784 pw->pw_dir, SILC_CLIENT_PRIVATE_KEY_NAME);
786 if (silc_pkcs_load_private_key(filename, &client->private_key) == FALSE)
789 memset(filename, 0, sizeof(filename));
790 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%s",
791 pw->pw_dir, SILC_CLIENT_PUBLIC_KEY_NAME);
793 if (silc_pkcs_load_public_key(filename, &client->public_key) == FALSE)
799 /* Verifies received public key. If user decides to trust the key it is
800 saved as trusted server key for later use. If user does not trust the
801 key this returns FALSE. */
803 int silc_client_verify_server_key(SilcClient client,
804 SilcSocketConnection sock,
805 unsigned char *pk, unsigned int pk_len,
806 SilcSKEPKType pk_type)
814 hostname = sock->hostname ? sock->hostname : sock->ip;
816 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
817 silc_say(client, "We don't support server %s key type", hostname);
821 pw = getpwuid(getuid());
825 memset(filename, 0, sizeof(filename));
826 memset(file, 0, sizeof(file));
827 snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname,
829 snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s",
832 /* Check wheter this key already exists */
833 if (stat(filename, &st) < 0) {
835 silc_say(client, "Received server %s public key", hostname);
836 /* XXX print fingerprint of the key */
838 /* Ask user to verify the key and save it */
839 if (silc_client_ask_yes_no(client,
840 "Would you like to accept the server key (y/n)? "))
842 /* Save the key for future checking */
843 silc_pkcs_save_public_key_data(filename, pk, pk_len);
847 /* The key already exists, verify it. */
848 SilcPublicKey public_key;
849 unsigned char *encpk;
850 unsigned int encpk_len;
852 /* Load the key file */
853 if (!silc_pkcs_load_public_key(filename, &public_key)) {
854 silc_say(client, "Received server %s public key", hostname);
855 silc_say(client, "Could not load your local copy of the server %s key",
857 if (silc_client_ask_yes_no(client,
858 "Would you like to accept the server key anyway (y/n)? "))
860 /* Save the key for future checking */
862 silc_pkcs_save_public_key_data(filename, pk, pk_len);
869 /* Encode the key data */
870 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
872 silc_say(client, "Received server %s public key", hostname);
873 silc_say(client, "Your local copy of the server %s key is malformed",
875 if (silc_client_ask_yes_no(client,
876 "Would you like to accept the server key anyway (y/n)? "))
878 /* Save the key for future checking */
880 silc_pkcs_save_public_key_data(filename, pk, pk_len);
887 if (memcmp(encpk, pk, encpk_len)) {
888 silc_say(client, "Received server %s public key", hostname);
889 silc_say(client, "Server %s key does not match with your local copy",
891 silc_say(client, "It is possible that the key has expired or changed");
892 silc_say(client, "It is also possible that some one is performing "
893 "man-in-the-middle attack");
895 /* Ask user to verify the key and save it */
896 if (silc_client_ask_yes_no(client,
897 "Would you like to accept the server key anyway (y/n)? "))
899 /* Save the key for future checking */
901 silc_pkcs_save_public_key_data(filename, pk, pk_len);
905 silc_say(client, "Will not accept server %s key", hostname);
909 /* Local copy matched */
913 silc_say(client, "Will not accept server %s key", hostname);