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.7 2000/07/19 07:07:16 priikone
24 * Search mail by From:
26 * Revision 1.6 2000/07/14 06:11:32 priikone
27 * Fixed key-pair generation.
29 * Revision 1.5 2000/07/12 05:55:50 priikone
30 * Added client_parse_nickname.
32 * Revision 1.4 2000/07/10 05:40:05 priikone
33 * Added support for verifying incoming public keys from user.
34 * Shows fingerprint of the public key now plus other changes.
36 * Revision 1.3 2000/07/07 06:53:45 priikone
37 * Added support for server public key verification.
39 * Revision 1.2 2000/07/05 06:11:00 priikone
40 * Added ~./silc directory checking, autoloading of keys and
41 * tweaked the key pair generation function.
43 * Revision 1.1.1.1 2000/06/27 11:36:56 priikone
44 * Imported from internal CVS/Added Log headers.
49 #include "clientincludes.h"
51 /* Internal routine used to print lines to window. This can split the
52 line neatly if a word would overlap the line. */
54 static void silc_print_to_window(WINDOW *win, char *message)
58 str_len = strlen(message);
60 if (str_len > COLS - 1) {
61 /* Split overlapping words to next line */
62 /* XXX In principal this is wrong as this modifies the original
63 string as it replaces the last ' ' with '\n'. This could be done
64 with little more work so that it would not replace anything. */
68 while (len && message[len] != ' ')
81 wprintw(win, "%s", message);
85 /* Prints a message with three star (*) sign before the actual message
86 on the current output window. This is used to print command outputs
87 and error messages. */
88 /* XXX Change to accept SilcClientWindow and use output window
89 from there (the pointer to the output window must be added to the
90 SilcClientWindow object. */
92 void silc_say(SilcClient client, char *msg, ...)
97 memset(message, 0, sizeof(message));
98 strncat(message, "\n*** ", 5);
101 vsprintf(message + 5, msg, vp);
104 /* Print the message */
105 silc_print_to_window(client->screen->output_win[0], message);
108 /* Prints message to the screen. This is used to print the messages
109 user is typed and message that came on channels. */
111 void silc_print(SilcClient client, char *msg, ...)
116 memset(message, 0, sizeof(message));
117 strncat(message, "\n ", 2);
120 vsprintf(message + 1, msg, vp);
123 /* Print the message */
124 silc_print_to_window(client->screen->output_win[0], message);
127 /* Returns user's mail path */
129 char *silc_get_mail_path()
131 char pathbuf[MAXPATHLEN];
134 path = getenv("MAIL");
136 strncpy(pathbuf, path, strlen(path));
138 strcpy(pathbuf, _PATH_MAILDIR);
139 strcat(pathbuf, "/");
140 strcat(pathbuf, silc_get_username());
143 return strdup(pathbuf);
146 /* gets the number of the user's mails, if possible */
148 int silc_get_number_of_emails()
155 filename = silc_get_mail_path();
157 tl = fopen(filename, "r");
159 fprintf(stderr, "Couldn't open mail file (%s).\n", filename);
161 while((fscanf(tl, "%s", data)) != EOF) {
162 if(!strcmp(data, "From:"))
172 /* Returns the username of the user. If the global variable LOGNAME
173 does not exists we will get the name from the password file. */
175 char *silc_get_username()
177 char *logname = NULL;
179 logname = strdup(getenv("LOGNAME"));
181 logname = getlogin();
185 pw = getpwuid(getuid());
187 fprintf(stderr, "silc_get_username: %s\n", strerror(errno));
191 logname = strdup(pw->pw_name);
198 /* Returns the real name of ther user. */
200 char *silc_get_real_name()
202 char *realname = NULL;
205 pw = getpwuid(getuid());
207 fprintf(stderr, "silc_get_username: %s\n", strerror(errno));
211 if (strchr(pw->pw_gecos, ','))
212 *strchr(pw->pw_gecos, ',') = 0;
214 realname = strdup(pw->pw_gecos);
219 /* Returns time til next minute changes. Used to update the clock when
222 int silc_client_time_til_next_min()
228 min = localtime(&curtime);
230 return 60 - min->tm_sec;
233 /* Asks passphrase from user on the input line. */
235 char *silc_client_ask_passphrase(SilcClient client)
237 char pass1[256], pass2[256];
244 wattroff(client->screen->input_win, A_INVIS);
245 silc_screen_input_print_prompt(client->screen, "Passphrase: ");
246 wattron(client->screen->input_win, A_INVIS);
249 memset(pass1, 0, sizeof(pass1));
250 wgetnstr(client->screen->input_win, pass1, sizeof(pass1));
252 /* Print retype prompt */
253 wattroff(client->screen->input_win, A_INVIS);
254 silc_screen_input_print_prompt(client->screen, "Retype passphrase: ");
255 wattron(client->screen->input_win, A_INVIS);
258 memset(pass2, 0, sizeof(pass2));
259 wgetnstr(client->screen->input_win, pass2, sizeof(pass2));
261 if (!strncmp(pass1, pass2, strlen(pass2)))
267 ret = silc_calloc(strlen(pass1), sizeof(char));
268 memcpy(ret, pass1, strlen(pass1));
270 memset(pass1, 0, sizeof(pass1));
271 memset(pass2, 0, sizeof(pass2));
273 wattroff(client->screen->input_win, A_INVIS);
274 silc_screen_input_reset(client->screen);
279 /* Asks yes/no from user on the input line. Returns TRUE on "yes" and
282 int silc_client_ask_yes_no(SilcClient client, char *prompt)
288 silc_screen_input_reset(client->screen);
291 wattroff(client->screen->input_win, A_INVIS);
292 silc_screen_input_print_prompt(client->screen, prompt);
295 memset(answer, 0, sizeof(answer));
297 wgetnstr(client->screen->input_win, answer, sizeof(answer));
298 if (!strncasecmp(answer, "yes", strlen(answer)) ||
299 !strncasecmp(answer, "y", strlen(answer))) {
301 } else if (!strncasecmp(answer, "no", strlen(answer)) ||
302 !strncasecmp(answer, "n", strlen(answer))) {
305 silc_say(client, "Type yes or no");
310 silc_screen_input_reset(client->screen);
315 /* Lists supported (builtin) ciphers */
317 void silc_client_list_ciphers()
322 /* Lists supported (builtin) hash functions */
324 void silc_client_list_hash_funcs()
329 /* Lists supported PKCS algorithms */
331 void silc_client_list_pkcs()
336 /* Displays input prompt on command line and takes input data from user */
338 char *silc_client_get_input(const char *prompt)
343 fd = open("/dev/tty", O_RDONLY);
345 fprintf(stderr, "silc: %s\n", strerror(errno));
349 memset(input, 0, sizeof(input));
351 printf("%s", prompt);
354 if ((read(fd, input, sizeof(input))) < 0) {
355 fprintf(stderr, "silc: %s\n", strerror(errno));
359 if (strlen(input) <= 1)
362 if (strchr(input, '\n'))
363 *strchr(input, '\n') = '\0';
365 return strdup(input);
368 /* Displays prompt on command line and takes passphrase with echo
371 char *silc_client_get_passphrase(const char *prompt)
378 struct termios to_old;
380 fd = open("/dev/tty", O_RDONLY);
382 fprintf(stderr, "silc: %s\n", strerror(errno));
386 signal(SIGINT, SIG_IGN);
388 /* Get terminal info */
393 to.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
394 tcsetattr(fd, TCSANOW, &to);
396 memset(input, 0, sizeof(input));
398 printf("%s", prompt);
401 if ((read(fd, input, sizeof(input))) < 0) {
402 fprintf(stderr, "silc: %s\n", strerror(errno));
406 if (strlen(input) <= 1) {
407 tcsetattr(fd, TCSANOW, &to_old);
411 if (strchr(input, '\n'))
412 *strchr(input, '\n') = '\0';
414 /* Restore old terminfo */
415 tcsetattr(fd, TCSANOW, &to_old);
416 signal(SIGINT, SIG_DFL);
418 ret = silc_calloc(strlen(input), sizeof(char));
419 memcpy(ret, input, strlen(input));
420 memset(input, 0, sizeof(input));
427 /* Returns identifier string for public key generation. */
429 char *silc_client_create_identifier()
431 char *username = NULL, *realname = NULL;
432 char hostname[256], email[256];
435 realname = silc_get_real_name();
438 memset(hostname, 0, sizeof(hostname));
439 gethostname(hostname, sizeof(hostname));
441 /* Get username (mandatory) */
442 username = silc_get_username();
446 /* Create default email address, whether it is right or not */
447 snprintf(email, sizeof(email), "%s@%s", username, hostname);
449 return silc_pkcs_encode_identifier(username, hostname, realname, email,
453 /* Creates new public key and private key pair. This is used only
454 when user wants to create new key pair from command line. */
456 int silc_client_create_key_pair(char *pkcs_name, int bits,
457 char *public_key, char *private_key,
459 SilcPublicKey *ret_pub_key,
460 SilcPrivateKey *ret_prv_key)
463 SilcPublicKey pub_key;
464 SilcPrivateKey prv_key;
467 unsigned int key_len;
469 char *pkfile = NULL, *prvfile = NULL;
471 if (!pkcs_name || !public_key || !private_key)
473 New pair of keys will be created. Please, answer to following questions.\n\
479 silc_client_get_input("PKCS name (l to list names) [rsa]: ");
481 pkcs_name = strdup("rsa");
483 if (*pkcs_name == 'l' || *pkcs_name == 'L') {
484 silc_client_list_pkcs();
485 silc_free(pkcs_name);
490 if (!silc_pkcs_is_supported(pkcs_name)) {
491 fprintf(stderr, "Unsupported PKCS `%s'", pkcs_name);
498 silc_client_get_input("Key length in bits [1024]: ");
506 char *def = silc_client_create_identifier();
508 memset(line, 0, sizeof(line));
510 snprintf(line, sizeof(line), "Identifier [%s]: ", def);
512 snprintf(line, sizeof(line),
513 "Identifier (eg. UN=jon, HN=jon.dummy.com, "
514 "RN=Jon Johnson, E=jon@dummy.com): ");
516 while (!identifier) {
517 identifier = silc_client_get_input(line);
518 if (!identifier && def)
519 identifier = strdup(def);
526 rng = silc_rng_alloc();
528 silc_math_primegen_init();
531 memset(line, 0, sizeof(line));
532 snprintf(line, sizeof(line), "Public key filename [%s] ",
533 SILC_CLIENT_PUBLIC_KEY_NAME);
534 pkfile = silc_client_get_input(line);
536 pkfile = SILC_CLIENT_PUBLIC_KEY_NAME;
542 memset(line, 0, sizeof(line));
543 snprintf(line, sizeof(line), "Public key filename [%s] ",
544 SILC_CLIENT_PRIVATE_KEY_NAME);
545 prvfile = silc_client_get_input(line);
547 prvfile = SILC_CLIENT_PRIVATE_KEY_NAME;
549 prvfile = private_key;
553 silc_pkcs_alloc(pkcs_name, &pkcs);
554 pkcs->pkcs->init(pkcs->context, bits, rng);
556 /* Save public key into file */
557 key = silc_pkcs_get_public_key(pkcs, &key_len);
558 pub_key = silc_pkcs_public_key_alloc(pkcs->pkcs->name, identifier,
560 silc_pkcs_save_public_key(pkfile, pub_key, SILC_PKCS_FILE_PEM);
562 *ret_pub_key = pub_key;
564 memset(key, 0, sizeof(key_len));
567 /* Save private key into file */
568 key = silc_pkcs_get_private_key(pkcs, &key_len);
569 prv_key = silc_pkcs_private_key_alloc(pkcs->pkcs->name, key, key_len);
571 silc_pkcs_save_private_key(prvfile, prv_key, NULL, SILC_PKCS_FILE_BIN);
573 *ret_prv_key = prv_key;
575 printf("Public key has been save into `%s'.\n", pkfile);
576 printf("Private key has been saved into `%s'.\n", prvfile);
577 printf("Press <Enter> to continue...\n");
580 memset(key, 0, sizeof(key_len));
583 silc_math_primegen_uninit();
585 silc_pkcs_free(pkcs);
590 /* This checks stats for various SILC files and directories. First it
591 checks if ~/.silc directory exist and is owned by the correct user. If
592 it doesn't exist, it will create the directory. After that it checks if
593 user's Public and Private key files exists and that they aren't expired.
594 If they doesn't exist or they are expired, they will be (re)created
597 int silc_client_check_silc_dir()
599 char filename[256], file_public_key[256], file_private_key[256];
600 char servfilename[256];
604 int firstime = FALSE;
605 time_t curtime, modtime;
607 SILC_LOG_DEBUG(("Checking ~./silc directory"));
609 memset(filename, 0, sizeof(filename));
610 memset(file_public_key, 0, sizeof(file_public_key));
611 memset(file_private_key, 0, sizeof(file_private_key));
613 pw = getpwuid(getuid());
615 fprintf(stderr, "silc: %s\n", strerror(errno));
619 identifier = silc_client_create_identifier();
621 /* We'll take home path from /etc/passwd file to be sure. */
622 snprintf(filename, sizeof(filename) - 1, "%s/.silc/", pw->pw_dir);
623 snprintf(servfilename, sizeof(servfilename) - 1, "%s/.silc/serverkeys",
627 * Check ~/.silc directory
629 if ((stat(filename, &st)) == -1) {
630 /* If dir doesn't exist */
631 if (errno == ENOENT) {
632 if (pw->pw_uid == geteuid()) {
633 if ((mkdir(filename, 0755)) == -1) {
634 fprintf(stderr, "Couldn't create `%s' directory\n", filename);
638 /* Directory was created. First time running SILC */
641 fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
646 fprintf(stderr, "%s\n", strerror(errno));
651 /* Check the owner of the dir */
652 if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
653 fprintf(stderr, "You don't seem to own `%s' directory\n",
658 /* Check the permissions of the dir */
659 if ((st.st_mode & 0777) != 0755) {
660 if ((chmod(filename, 0755)) == -1) {
661 fprintf(stderr, "Permissions for `%s' directory must be 0755\n",
669 * Check ~./silc/serverkeys directory
671 if ((stat(servfilename, &st)) == -1) {
672 /* If dir doesn't exist */
673 if (errno == ENOENT) {
674 if (pw->pw_uid == geteuid()) {
675 if ((mkdir(servfilename, 0755)) == -1) {
676 fprintf(stderr, "Couldn't create `%s' directory\n", servfilename);
680 fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
685 fprintf(stderr, "%s\n", strerror(errno));
691 * Check Public and Private keys
693 snprintf(file_public_key, sizeof(file_public_key) - 1, "%s%s",
694 filename, SILC_CLIENT_PUBLIC_KEY_NAME);
695 snprintf(file_private_key, sizeof(file_private_key) - 1, "%s%s",
696 filename, SILC_CLIENT_PRIVATE_KEY_NAME);
698 /* If running SILC first time */
700 fprintf(stdout, "Running SILC for the first time\n");
701 silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS,
702 SILC_CLIENT_DEF_PKCS_LEN,
703 file_public_key, file_private_key,
704 identifier, NULL, NULL);
708 if ((stat(file_public_key, &st)) == -1) {
709 /* If file doesn't exist */
710 if (errno == ENOENT) {
711 fprintf(stdout, "Your public key doesn't exist\n");
712 silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS,
713 SILC_CLIENT_DEF_PKCS_LEN,
715 file_private_key, identifier, NULL, NULL);
717 fprintf(stderr, "%s\n", strerror(errno));
722 if ((stat(file_private_key, &st)) == -1) {
723 /* If file doesn't exist */
724 if (errno == ENOENT) {
725 fprintf(stdout, "Your private key doesn't exist\n");
726 silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS,
727 SILC_CLIENT_DEF_PKCS_LEN,
729 file_private_key, identifier, NULL, NULL);
731 fprintf(stderr, "%s\n", strerror(errno));
736 /* Check the owner of the public key */
737 if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
738 fprintf(stderr, "You don't seem to own your public key!?\n");
742 /* Check the owner of the private key */
743 if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
744 fprintf(stderr, "You don't seem to own your private key!?\n");
748 /* Check the permissions for the private key */
749 if ((st.st_mode & 0777) != 0600) {
750 fprintf(stderr, "Wrong permissions in your private key file `%s'!\n"
751 "Trying to change them ... ", file_private_key);
752 if ((chmod(file_private_key, 0600)) == -1) {
754 "Failed to change permissions for private key file!\n"
755 "Permissions for your private key file must be 0600.\n");
758 fprintf(stderr, "Done.\n\n");
761 /* See if the key has expired. */
762 modtime = st.st_mtime; /* last modified */
763 curtime = time(0) - modtime;
765 /* 86400 is seconds in a day. */
766 if (curtime >= (86400 * SILC_CLIENT_KEY_EXPIRES)) {
768 "--------------------------------------------------\n"
769 "Your private key has expired and needs to be\n"
770 "recreated. This will be done automatically now.\n"
771 "Your new key will expire in %d days from today.\n"
772 "--------------------------------------------------\n",
773 SILC_CLIENT_KEY_EXPIRES);
775 silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS,
776 SILC_CLIENT_DEF_PKCS_LEN,
778 file_private_key, identifier, NULL, NULL);
782 silc_free(identifier);
787 /* Loads public and private key from files. */
789 int silc_client_load_keys(SilcClient client)
794 SILC_LOG_DEBUG(("Loading public and private keys"));
796 pw = getpwuid(getuid());
800 memset(filename, 0, sizeof(filename));
801 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%s",
802 pw->pw_dir, SILC_CLIENT_PRIVATE_KEY_NAME);
804 if (silc_pkcs_load_private_key(filename, &client->private_key,
805 SILC_PKCS_FILE_BIN) == FALSE)
806 if (silc_pkcs_load_private_key(filename, &client->private_key,
807 SILC_PKCS_FILE_PEM) == FALSE)
810 memset(filename, 0, sizeof(filename));
811 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%s",
812 pw->pw_dir, SILC_CLIENT_PUBLIC_KEY_NAME);
814 if (silc_pkcs_load_public_key(filename, &client->public_key,
815 SILC_PKCS_FILE_PEM) == FALSE)
816 if (silc_pkcs_load_public_key(filename, &client->public_key,
817 SILC_PKCS_FILE_BIN) == FALSE)
823 /* Verifies received public key. If user decides to trust the key it is
824 saved as trusted server key for later use. If user does not trust the
825 key this returns FALSE. */
827 int silc_client_verify_server_key(SilcClient client,
828 SilcSocketConnection sock,
829 unsigned char *pk, unsigned int pk_len,
830 SilcSKEPKType pk_type)
834 char *hostname, *fingerprint;
838 hostname = sock->hostname ? sock->hostname : sock->ip;
840 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
841 silc_say(client, "We don't support server %s key type", hostname);
845 pw = getpwuid(getuid());
849 memset(filename, 0, sizeof(filename));
850 memset(file, 0, sizeof(file));
851 snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname,
853 snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s",
856 /* Check wheter this key already exists */
857 if (stat(filename, &st) < 0) {
859 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
860 silc_say(client, "Received server %s public key", hostname);
861 silc_say(client, "Fingerprint for the server %s key is", hostname);
862 silc_say(client, "%s", fingerprint);
863 silc_free(fingerprint);
865 /* Ask user to verify the key and save it */
866 if (silc_client_ask_yes_no(client,
867 "Would you like to accept the key (y/n)? "))
869 /* Save the key for future checking */
870 silc_pkcs_save_public_key_data(filename, pk, pk_len,
875 /* The key already exists, verify it. */
876 SilcPublicKey public_key;
877 unsigned char *encpk;
878 unsigned int encpk_len;
880 /* Load the key file */
881 if (!silc_pkcs_load_public_key(filename, &public_key,
883 if (!silc_pkcs_load_public_key(filename, &public_key,
884 SILC_PKCS_FILE_BIN)) {
885 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
886 silc_say(client, "Received server %s public key", hostname);
887 silc_say(client, "Fingerprint for the server %s key is", hostname);
888 silc_say(client, "%s", fingerprint);
889 silc_free(fingerprint);
890 silc_say(client, "Could not load your local copy of the server %s key",
892 if (silc_client_ask_yes_no(client,
893 "Would you like to accept the key anyway (y/n)? "))
895 /* Save the key for future checking */
897 silc_pkcs_save_public_key_data(filename, pk, pk_len,
905 /* Encode the key data */
906 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
908 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
909 silc_say(client, "Received server %s public key", hostname);
910 silc_say(client, "Fingerprint for the server %s key is", hostname);
911 silc_say(client, "%s", fingerprint);
912 silc_free(fingerprint);
913 silc_say(client, "Your local copy of the server %s key is malformed",
915 if (silc_client_ask_yes_no(client,
916 "Would you like to accept the key anyway (y/n)? "))
918 /* Save the key for future checking */
920 silc_pkcs_save_public_key_data(filename, pk, pk_len,
928 if (memcmp(encpk, pk, encpk_len)) {
929 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
930 silc_say(client, "Received server %s public key", hostname);
931 silc_say(client, "Fingerprint for the server %s key is", hostname);
932 silc_say(client, "%s", fingerprint);
933 silc_free(fingerprint);
934 silc_say(client, "Server %s key does not match with your local copy",
936 silc_say(client, "It is possible that the key has expired or changed");
937 silc_say(client, "It is also possible that some one is performing "
938 "man-in-the-middle attack");
940 /* Ask user to verify the key and save it */
941 if (silc_client_ask_yes_no(client,
942 "Would you like to accept the key anyway (y/n)? "))
944 /* Save the key for future checking */
946 silc_pkcs_save_public_key_data(filename, pk, pk_len,
951 silc_say(client, "Will not accept server %s key", hostname);
955 /* Local copy matched */
959 silc_say(client, "Will not accept server %s key", hostname);
964 /* Parse nickname string. The format may be <num>!<nickname>@<server> to
965 support multiple same nicknames. The <num> is the final unifier if same
966 nickname is on same server. Note, this is only local format and server
967 does not know anything about these. */
969 int silc_client_parse_nickname(char *string, char **nickname, char **server,
978 if (strchr(string, '!')) {
979 tlen = strcspn(string, "!");
980 memset(tmp, 0, sizeof(tmp));
981 memcpy(tmp, string, tlen);
986 if (tlen >= strlen(string))
992 if (strchr(string, '@')) {
993 tlen = strcspn(string, "@");
996 *nickname = silc_calloc(tlen + 1, sizeof(char));
997 memcpy(*nickname, string, tlen);
1001 *server = silc_calloc(strlen(string) - tlen, sizeof(char));
1002 memcpy(*server, string + tlen + 1, strlen(string) - tlen - 1);
1006 *nickname = strdup(string);