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.6 2000/07/14 06:11:32 priikone
24 * Fixed key-pair generation.
26 * Revision 1.5 2000/07/12 05:55:50 priikone
27 * Added client_parse_nickname.
29 * Revision 1.4 2000/07/10 05:40:05 priikone
30 * Added support for verifying incoming public keys from user.
31 * Shows fingerprint of the public key now plus other changes.
33 * Revision 1.3 2000/07/07 06:53:45 priikone
34 * Added support for server public key verification.
36 * Revision 1.2 2000/07/05 06:11:00 priikone
37 * Added ~./silc directory checking, autoloading of keys and
38 * tweaked the key pair generation function.
40 * Revision 1.1.1.1 2000/06/27 11:36:56 priikone
41 * Imported from internal CVS/Added Log headers.
46 #include "clientincludes.h"
48 /* Internal routine used to print lines to window. This can split the
49 line neatly if a word would overlap the line. */
51 static void silc_print_to_window(WINDOW *win, char *message)
55 str_len = strlen(message);
57 if (str_len > COLS - 1) {
58 /* Split overlapping words to next line */
59 /* XXX In principal this is wrong as this modifies the original
60 string as it replaces the last ' ' with '\n'. This could be done
61 with little more work so that it would not replace anything. */
65 while (len && message[len] != ' ')
78 wprintw(win, "%s", message);
82 /* Prints a message with three star (*) sign before the actual message
83 on the current output window. This is used to print command outputs
84 and error messages. */
85 /* XXX Change to accept SilcClientWindow and use output window
86 from there (the pointer to the output window must be added to the
87 SilcClientWindow object. */
89 void silc_say(SilcClient client, char *msg, ...)
94 memset(message, 0, sizeof(message));
95 strncat(message, "\n*** ", 5);
98 vsprintf(message + 5, msg, vp);
101 /* Print the message */
102 silc_print_to_window(client->screen->output_win[0], message);
105 /* Prints message to the screen. This is used to print the messages
106 user is typed and message that came on channels. */
108 void silc_print(SilcClient client, char *msg, ...)
113 memset(message, 0, sizeof(message));
114 strncat(message, "\n ", 2);
117 vsprintf(message + 1, msg, vp);
120 /* Print the message */
121 silc_print_to_window(client->screen->output_win[0], message);
124 /* Returns user's mail path */
126 char *silc_get_mail_path()
128 char pathbuf[MAXPATHLEN];
131 if ((path = (char *)getenv("MAIL")) != 0) {
132 strncpy(pathbuf, path, strlen(path));
134 strcpy(pathbuf, _PATH_MAILDIR);
135 strcat(pathbuf, "/");
136 strcat(pathbuf, silc_get_username());
139 return strdup(pathbuf);
142 /* gets the number of the user's mails, if possible */
144 int silc_get_number_of_emails()
151 filename = silc_get_mail_path();
153 tl = fopen(filename, "r");
155 fprintf(stderr, "Couldn't open mail file (%s).\n", filename);
157 while((fscanf(tl, "%s", data)) != EOF) {
158 if(!strcmp(data, "Subject:"))
168 /* Returns the username of the user. If the global variable LOGNAME
169 does not exists we will get the name from the password file. */
171 char *silc_get_username()
173 char *logname = NULL;
175 logname = strdup(getenv("LOGNAME"));
177 logname = getlogin();
181 pw = getpwuid(getuid());
183 fprintf(stderr, "silc_get_username: %s\n", strerror(errno));
187 logname = strdup(pw->pw_name);
194 /* Returns the real name of ther user. */
196 char *silc_get_real_name()
198 char *realname = NULL;
201 pw = getpwuid(getuid());
203 fprintf(stderr, "silc_get_username: %s\n", strerror(errno));
207 if (strchr(pw->pw_gecos, ','))
208 *strchr(pw->pw_gecos, ',') = 0;
210 realname = strdup(pw->pw_gecos);
215 /* Returns time til next minute changes. Used to update the clock when
218 int silc_client_time_til_next_min()
224 min = localtime(&curtime);
226 return 60 - min->tm_sec;
229 /* Asks passphrase from user on the input line. */
231 char *silc_client_ask_passphrase(SilcClient client)
233 char pass1[256], pass2[256];
240 wattroff(client->screen->input_win, A_INVIS);
241 silc_screen_input_print_prompt(client->screen, "Passphrase: ");
242 wattron(client->screen->input_win, A_INVIS);
245 memset(pass1, 0, sizeof(pass1));
246 wgetnstr(client->screen->input_win, pass1, sizeof(pass1));
248 /* Print retype prompt */
249 wattroff(client->screen->input_win, A_INVIS);
250 silc_screen_input_print_prompt(client->screen, "Retype passphrase: ");
251 wattron(client->screen->input_win, A_INVIS);
254 memset(pass2, 0, sizeof(pass2));
255 wgetnstr(client->screen->input_win, pass2, sizeof(pass2));
257 if (!strncmp(pass1, pass2, strlen(pass2)))
263 ret = silc_calloc(strlen(pass1), sizeof(char));
264 memcpy(ret, pass1, strlen(pass1));
266 memset(pass1, 0, sizeof(pass1));
267 memset(pass2, 0, sizeof(pass2));
269 wattroff(client->screen->input_win, A_INVIS);
270 silc_screen_input_reset(client->screen);
275 /* Asks yes/no from user on the input line. Returns TRUE on "yes" and
278 int silc_client_ask_yes_no(SilcClient client, char *prompt)
284 silc_screen_input_reset(client->screen);
287 wattroff(client->screen->input_win, A_INVIS);
288 silc_screen_input_print_prompt(client->screen, prompt);
291 memset(answer, 0, sizeof(answer));
293 wgetnstr(client->screen->input_win, answer, sizeof(answer));
294 if (!strncasecmp(answer, "yes", strlen(answer)) ||
295 !strncasecmp(answer, "y", strlen(answer))) {
297 } else if (!strncasecmp(answer, "no", strlen(answer)) ||
298 !strncasecmp(answer, "n", strlen(answer))) {
301 silc_say(client, "Type yes or no");
306 silc_screen_input_reset(client->screen);
311 /* Lists supported (builtin) ciphers */
313 void silc_client_list_ciphers()
318 /* Lists supported (builtin) hash functions */
320 void silc_client_list_hash_funcs()
325 /* Lists supported PKCS algorithms */
327 void silc_client_list_pkcs()
332 /* Displays input prompt on command line and takes input data from user */
334 char *silc_client_get_input(const char *prompt)
339 fd = open("/dev/tty", O_RDONLY);
341 fprintf(stderr, "silc: %s\n", strerror(errno));
345 memset(input, 0, sizeof(input));
347 printf("%s", prompt);
350 if ((read(fd, input, sizeof(input))) < 0) {
351 fprintf(stderr, "silc: %s\n", strerror(errno));
355 if (strlen(input) <= 1)
358 if (strchr(input, '\n'))
359 *strchr(input, '\n') = '\0';
361 return strdup(input);
364 /* Displays prompt on command line and takes passphrase with echo
367 char *silc_client_get_passphrase(const char *prompt)
374 struct termios to_old;
376 fd = open("/dev/tty", O_RDONLY);
378 fprintf(stderr, "silc: %s\n", strerror(errno));
382 signal(SIGINT, SIG_IGN);
384 /* Get terminal info */
389 to.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
390 tcsetattr(fd, TCSANOW, &to);
392 memset(input, 0, sizeof(input));
394 printf("%s", prompt);
397 if ((read(fd, input, sizeof(input))) < 0) {
398 fprintf(stderr, "silc: %s\n", strerror(errno));
402 if (strlen(input) <= 1) {
403 tcsetattr(fd, TCSANOW, &to_old);
407 if (strchr(input, '\n'))
408 *strchr(input, '\n') = '\0';
410 /* Restore old terminfo */
411 tcsetattr(fd, TCSANOW, &to_old);
412 signal(SIGINT, SIG_DFL);
414 ret = silc_calloc(strlen(input), sizeof(char));
415 memcpy(ret, input, strlen(input));
416 memset(input, 0, sizeof(input));
423 /* Returns identifier string for public key generation. */
425 char *silc_client_create_identifier()
427 char *username = NULL, *realname = NULL;
428 char hostname[256], email[256];
431 realname = silc_get_real_name();
434 memset(hostname, 0, sizeof(hostname));
435 gethostname(hostname, sizeof(hostname));
437 /* Get username (mandatory) */
438 username = silc_get_username();
442 /* Create default email address, whether it is right or not */
443 snprintf(email, sizeof(email), "%s@%s", username, hostname);
445 return silc_pkcs_encode_identifier(username, hostname, realname, email,
449 /* Creates new public key and private key pair. This is used only
450 when user wants to create new key pair from command line. */
452 int silc_client_create_key_pair(char *pkcs_name, int bits,
453 char *public_key, char *private_key,
455 SilcPublicKey *ret_pub_key,
456 SilcPrivateKey *ret_prv_key)
459 SilcPublicKey pub_key;
460 SilcPrivateKey prv_key;
463 unsigned int key_len;
465 char *pkfile = NULL, *prvfile = NULL;
467 if (!pkcs_name || !public_key || !private_key)
469 New pair of keys will be created. Please, answer to following questions.\n\
475 silc_client_get_input("PKCS name (l to list names) [rsa]: ");
477 pkcs_name = strdup("rsa");
479 if (*pkcs_name == 'l' || *pkcs_name == 'L') {
480 silc_client_list_pkcs();
481 silc_free(pkcs_name);
486 if (!silc_pkcs_is_supported(pkcs_name)) {
487 fprintf(stderr, "Unsupported PKCS `%s'", pkcs_name);
494 silc_client_get_input("Key length in bits [1024]: ");
502 char *def = silc_client_create_identifier();
504 memset(line, 0, sizeof(line));
506 snprintf(line, sizeof(line), "Identifier [%s]: ", def);
508 snprintf(line, sizeof(line),
509 "Identifier (eg. UN=jon, HN=jon.dummy.com, "
510 "RN=Jon Johnson, E=jon@dummy.com): ");
512 while (!identifier) {
513 identifier = silc_client_get_input(line);
514 if (!identifier && def)
515 identifier = strdup(def);
522 rng = silc_rng_alloc();
524 silc_math_primegen_init();
527 memset(line, 0, sizeof(line));
528 snprintf(line, sizeof(line), "Public key filename [%s] ",
529 SILC_CLIENT_PUBLIC_KEY_NAME);
530 pkfile = silc_client_get_input(line);
532 pkfile = SILC_CLIENT_PUBLIC_KEY_NAME;
538 memset(line, 0, sizeof(line));
539 snprintf(line, sizeof(line), "Public key filename [%s] ",
540 SILC_CLIENT_PRIVATE_KEY_NAME);
541 prvfile = silc_client_get_input(line);
543 prvfile = SILC_CLIENT_PRIVATE_KEY_NAME;
545 prvfile = private_key;
549 silc_pkcs_alloc(pkcs_name, &pkcs);
550 pkcs->pkcs->init(pkcs->context, bits, rng);
552 /* Save public key into file */
553 key = silc_pkcs_get_public_key(pkcs, &key_len);
554 pub_key = silc_pkcs_public_key_alloc(pkcs->pkcs->name, identifier,
556 silc_pkcs_save_public_key(pkfile, pub_key, SILC_PKCS_FILE_PEM);
558 *ret_pub_key = pub_key;
560 memset(key, 0, sizeof(key_len));
563 /* Save private key into file */
564 key = silc_pkcs_get_private_key(pkcs, &key_len);
565 prv_key = silc_pkcs_private_key_alloc(pkcs->pkcs->name, key, key_len);
567 silc_pkcs_save_private_key(prvfile, prv_key, NULL, SILC_PKCS_FILE_BIN);
569 *ret_prv_key = prv_key;
571 printf("Public key has been save into `%s'.\n", pkfile);
572 printf("Private key has been saved into `%s'.\n", prvfile);
573 printf("Press <Enter> to continue...\n");
576 memset(key, 0, sizeof(key_len));
579 silc_math_primegen_uninit();
581 silc_pkcs_free(pkcs);
586 /* This checks stats for various SILC files and directories. First it
587 checks if ~/.silc directory exist and is owned by the correct user. If
588 it doesn't exist, it will create the directory. After that it checks if
589 user's Public and Private key files exists and that they aren't expired.
590 If they doesn't exist or they are expired, they will be (re)created
593 int silc_client_check_silc_dir()
595 char filename[256], file_public_key[256], file_private_key[256];
596 char servfilename[256];
600 int firstime = FALSE;
601 time_t curtime, modtime;
603 SILC_LOG_DEBUG(("Checking ~./silc directory"));
605 memset(filename, 0, sizeof(filename));
606 memset(file_public_key, 0, sizeof(file_public_key));
607 memset(file_private_key, 0, sizeof(file_private_key));
609 pw = getpwuid(getuid());
611 fprintf(stderr, "silc: %s\n", strerror(errno));
615 identifier = silc_client_create_identifier();
617 /* We'll take home path from /etc/passwd file to be sure. */
618 snprintf(filename, sizeof(filename) - 1, "%s/.silc/", pw->pw_dir);
619 snprintf(servfilename, sizeof(servfilename) - 1, "%s/.silc/serverkeys",
623 * Check ~/.silc directory
625 if ((stat(filename, &st)) == -1) {
626 /* If dir doesn't exist */
627 if (errno == ENOENT) {
628 if (pw->pw_uid == geteuid()) {
629 if ((mkdir(filename, 0755)) == -1) {
630 fprintf(stderr, "Couldn't create `%s' directory\n", filename);
634 /* Directory was created. First time running SILC */
637 fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
642 fprintf(stderr, "%s\n", strerror(errno));
647 /* Check the owner of the dir */
648 if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
649 fprintf(stderr, "You don't seem to own `%s' directory\n",
654 /* Check the permissions of the dir */
655 if ((st.st_mode & 0777) != 0755) {
656 if ((chmod(filename, 0755)) == -1) {
657 fprintf(stderr, "Permissions for `%s' directory must be 0755\n",
665 * Check ~./silc/serverkeys directory
667 if ((stat(servfilename, &st)) == -1) {
668 /* If dir doesn't exist */
669 if (errno == ENOENT) {
670 if (pw->pw_uid == geteuid()) {
671 if ((mkdir(servfilename, 0755)) == -1) {
672 fprintf(stderr, "Couldn't create `%s' directory\n", servfilename);
676 fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
681 fprintf(stderr, "%s\n", strerror(errno));
687 * Check Public and Private keys
689 snprintf(file_public_key, sizeof(file_public_key) - 1, "%s%s",
690 filename, SILC_CLIENT_PUBLIC_KEY_NAME);
691 snprintf(file_private_key, sizeof(file_private_key) - 1, "%s%s",
692 filename, SILC_CLIENT_PRIVATE_KEY_NAME);
694 /* If running SILC first time */
696 fprintf(stdout, "Running SILC for the first time\n");
697 silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS,
698 SILC_CLIENT_DEF_PKCS_LEN,
699 file_public_key, file_private_key,
700 identifier, NULL, NULL);
704 if ((stat(file_public_key, &st)) == -1) {
705 /* If file doesn't exist */
706 if (errno == ENOENT) {
707 fprintf(stdout, "Your public 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 if ((stat(file_private_key, &st)) == -1) {
719 /* If file doesn't exist */
720 if (errno == ENOENT) {
721 fprintf(stdout, "Your private key doesn't exist\n");
722 silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS,
723 SILC_CLIENT_DEF_PKCS_LEN,
725 file_private_key, identifier, NULL, NULL);
727 fprintf(stderr, "%s\n", strerror(errno));
732 /* Check the owner of the public key */
733 if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
734 fprintf(stderr, "You don't seem to own your public key!?\n");
738 /* Check the owner of the private key */
739 if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
740 fprintf(stderr, "You don't seem to own your private key!?\n");
744 /* Check the permissions for the private key */
745 if ((st.st_mode & 0777) != 0600) {
746 fprintf(stderr, "Wrong permissions in your private key file `%s'!\n"
747 "Trying to change them ... ", file_private_key);
748 if ((chmod(file_private_key, 0600)) == -1) {
750 "Failed to change permissions for private key file!\n"
751 "Permissions for your private key file must be 0600.\n");
754 fprintf(stderr, "Done.\n\n");
757 /* See if the key has expired. */
758 modtime = st.st_mtime; /* last modified */
759 curtime = time(0) - modtime;
761 /* 86400 is seconds in a day. */
762 if (curtime >= (86400 * SILC_CLIENT_KEY_EXPIRES)) {
764 "--------------------------------------------------\n"
765 "Your private key has expired and needs to be\n"
766 "recreated. This will be done automatically now.\n"
767 "Your new key will expire in %d days from today.\n"
768 "--------------------------------------------------\n",
769 SILC_CLIENT_KEY_EXPIRES);
771 silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS,
772 SILC_CLIENT_DEF_PKCS_LEN,
774 file_private_key, identifier, NULL, NULL);
778 silc_free(identifier);
783 /* Loads public and private key from files. */
785 int silc_client_load_keys(SilcClient client)
790 SILC_LOG_DEBUG(("Loading public and private keys"));
792 pw = getpwuid(getuid());
796 memset(filename, 0, sizeof(filename));
797 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%s",
798 pw->pw_dir, SILC_CLIENT_PRIVATE_KEY_NAME);
800 if (silc_pkcs_load_private_key(filename, &client->private_key,
801 SILC_PKCS_FILE_BIN) == FALSE)
802 if (silc_pkcs_load_private_key(filename, &client->private_key,
803 SILC_PKCS_FILE_PEM) == FALSE)
806 memset(filename, 0, sizeof(filename));
807 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%s",
808 pw->pw_dir, SILC_CLIENT_PUBLIC_KEY_NAME);
810 if (silc_pkcs_load_public_key(filename, &client->public_key,
811 SILC_PKCS_FILE_PEM) == FALSE)
812 if (silc_pkcs_load_public_key(filename, &client->public_key,
813 SILC_PKCS_FILE_BIN) == FALSE)
819 /* Verifies received public key. If user decides to trust the key it is
820 saved as trusted server key for later use. If user does not trust the
821 key this returns FALSE. */
823 int silc_client_verify_server_key(SilcClient client,
824 SilcSocketConnection sock,
825 unsigned char *pk, unsigned int pk_len,
826 SilcSKEPKType pk_type)
830 char *hostname, *fingerprint;
834 hostname = sock->hostname ? sock->hostname : sock->ip;
836 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
837 silc_say(client, "We don't support server %s key type", hostname);
841 pw = getpwuid(getuid());
845 memset(filename, 0, sizeof(filename));
846 memset(file, 0, sizeof(file));
847 snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname,
849 snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s",
852 /* Check wheter this key already exists */
853 if (stat(filename, &st) < 0) {
855 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
856 silc_say(client, "Received server %s public key", hostname);
857 silc_say(client, "Fingerprint for the server %s key is", hostname);
858 silc_say(client, "%s", fingerprint);
859 silc_free(fingerprint);
861 /* Ask user to verify the key and save it */
862 if (silc_client_ask_yes_no(client,
863 "Would you like to accept the key (y/n)? "))
865 /* Save the key for future checking */
866 silc_pkcs_save_public_key_data(filename, pk, pk_len,
871 /* The key already exists, verify it. */
872 SilcPublicKey public_key;
873 unsigned char *encpk;
874 unsigned int encpk_len;
876 /* Load the key file */
877 if (!silc_pkcs_load_public_key(filename, &public_key,
879 if (!silc_pkcs_load_public_key(filename, &public_key,
880 SILC_PKCS_FILE_BIN)) {
881 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
882 silc_say(client, "Received server %s public key", hostname);
883 silc_say(client, "Fingerprint for the server %s key is", hostname);
884 silc_say(client, "%s", fingerprint);
885 silc_free(fingerprint);
886 silc_say(client, "Could not load your local copy of the server %s key",
888 if (silc_client_ask_yes_no(client,
889 "Would you like to accept the key anyway (y/n)? "))
891 /* Save the key for future checking */
893 silc_pkcs_save_public_key_data(filename, pk, pk_len,
901 /* Encode the key data */
902 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
904 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
905 silc_say(client, "Received server %s public key", hostname);
906 silc_say(client, "Fingerprint for the server %s key is", hostname);
907 silc_say(client, "%s", fingerprint);
908 silc_free(fingerprint);
909 silc_say(client, "Your local copy of the server %s key is malformed",
911 if (silc_client_ask_yes_no(client,
912 "Would you like to accept the key anyway (y/n)? "))
914 /* Save the key for future checking */
916 silc_pkcs_save_public_key_data(filename, pk, pk_len,
924 if (memcmp(encpk, pk, encpk_len)) {
925 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
926 silc_say(client, "Received server %s public key", hostname);
927 silc_say(client, "Fingerprint for the server %s key is", hostname);
928 silc_say(client, "%s", fingerprint);
929 silc_free(fingerprint);
930 silc_say(client, "Server %s key does not match with your local copy",
932 silc_say(client, "It is possible that the key has expired or changed");
933 silc_say(client, "It is also possible that some one is performing "
934 "man-in-the-middle attack");
936 /* Ask user to verify the key and save it */
937 if (silc_client_ask_yes_no(client,
938 "Would you like to accept the key anyway (y/n)? "))
940 /* Save the key for future checking */
942 silc_pkcs_save_public_key_data(filename, pk, pk_len,
947 silc_say(client, "Will not accept server %s key", hostname);
951 /* Local copy matched */
955 silc_say(client, "Will not accept server %s key", hostname);
960 /* Parse nickname string. The format may be <num>!<nickname>@<server> to
961 support multiple same nicknames. The <num> is the final unifier if same
962 nickname is on same server. Note, this is only local format and server
963 does not know anything about these. */
965 int silc_client_parse_nickname(char *string, char **nickname, char **server,
974 if (strchr(string, '!')) {
975 tlen = strcspn(string, "!");
976 memset(tmp, 0, sizeof(tmp));
977 memcpy(tmp, string, tlen);
982 if (tlen >= strlen(string))
988 if (strchr(string, '@')) {
989 tlen = strcspn(string, "@");
992 *nickname = silc_calloc(tlen + 1, sizeof(char));
993 memcpy(*nickname, string, tlen);
997 *server = silc_calloc(strlen(string) - tlen, sizeof(char));
998 memcpy(*server, string + tlen + 1, strlen(string) - tlen - 1);
1002 *nickname = strdup(string);