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.8 2000/07/20 05:49:32 priikone
24 * Added default mail directory path.
26 * Revision 1.7 2000/07/19 07:07:16 priikone
27 * Search mail by From:
29 * Revision 1.6 2000/07/14 06:11:32 priikone
30 * Fixed key-pair generation.
32 * Revision 1.5 2000/07/12 05:55:50 priikone
33 * Added client_parse_nickname.
35 * Revision 1.4 2000/07/10 05:40:05 priikone
36 * Added support for verifying incoming public keys from user.
37 * Shows fingerprint of the public key now plus other changes.
39 * Revision 1.3 2000/07/07 06:53:45 priikone
40 * Added support for server public key verification.
42 * Revision 1.2 2000/07/05 06:11:00 priikone
43 * Added ~./silc directory checking, autoloading of keys and
44 * tweaked the key pair generation function.
46 * Revision 1.1.1.1 2000/06/27 11:36:56 priikone
47 * Imported from internal CVS/Added Log headers.
52 #include "clientincludes.h"
54 /* Internal routine used to print lines to window. This can split the
55 line neatly if a word would overlap the line. */
57 static void silc_print_to_window(WINDOW *win, char *message)
61 str_len = strlen(message);
63 if (str_len > COLS - 1) {
64 /* Split overlapping words to next line */
65 /* XXX In principal this is wrong as this modifies the original
66 string as it replaces the last ' ' with '\n'. This could be done
67 with little more work so that it would not replace anything. */
71 while (len && message[len] != ' ')
84 wprintw(win, "%s", message);
88 /* Prints a message with three star (*) sign before the actual message
89 on the current output window. This is used to print command outputs
90 and error messages. */
91 /* XXX Change to accept SilcClientWindow and use output window
92 from there (the pointer to the output window must be added to the
93 SilcClientWindow object. */
95 void silc_say(SilcClient client, char *msg, ...)
100 memset(message, 0, sizeof(message));
101 strncat(message, "\n*** ", 5);
104 vsprintf(message + 5, msg, vp);
107 /* Print the message */
108 silc_print_to_window(client->screen->output_win[0], message);
111 /* Prints message to the screen. This is used to print the messages
112 user is typed and message that came on channels. */
114 void silc_print(SilcClient client, char *msg, ...)
119 memset(message, 0, sizeof(message));
120 strncat(message, "\n ", 2);
123 vsprintf(message + 1, msg, vp);
126 /* Print the message */
127 silc_print_to_window(client->screen->output_win[0], message);
130 /* Returns user's mail path */
132 char *silc_get_mail_path()
134 char pathbuf[MAXPATHLEN];
137 #ifndef _PATH_MAILDIR
138 #define _PATH_MAILDIR "/var/mail"
141 path = getenv("MAIL");
143 strncpy(pathbuf, path, strlen(path));
145 strcpy(pathbuf, _PATH_MAILDIR);
146 strcat(pathbuf, "/");
147 strcat(pathbuf, silc_get_username());
150 return strdup(pathbuf);
153 /* gets the number of the user's mails, if possible */
155 int silc_get_number_of_emails()
162 filename = silc_get_mail_path();
164 tl = fopen(filename, "r");
166 fprintf(stderr, "Couldn't open mail file (%s).\n", filename);
168 while((fscanf(tl, "%s", data)) != EOF) {
169 if(!strcmp(data, "From:"))
179 /* Returns the username of the user. If the global variable LOGNAME
180 does not exists we will get the name from the password file. */
182 char *silc_get_username()
184 char *logname = NULL;
186 logname = strdup(getenv("LOGNAME"));
188 logname = getlogin();
192 pw = getpwuid(getuid());
194 fprintf(stderr, "silc_get_username: %s\n", strerror(errno));
198 logname = strdup(pw->pw_name);
205 /* Returns the real name of ther user. */
207 char *silc_get_real_name()
209 char *realname = NULL;
212 pw = getpwuid(getuid());
214 fprintf(stderr, "silc_get_username: %s\n", strerror(errno));
218 if (strchr(pw->pw_gecos, ','))
219 *strchr(pw->pw_gecos, ',') = 0;
221 realname = strdup(pw->pw_gecos);
226 /* Returns time til next minute changes. Used to update the clock when
229 int silc_client_time_til_next_min()
235 min = localtime(&curtime);
237 return 60 - min->tm_sec;
240 /* Asks passphrase from user on the input line. */
242 char *silc_client_ask_passphrase(SilcClient client)
244 char pass1[256], pass2[256];
251 wattroff(client->screen->input_win, A_INVIS);
252 silc_screen_input_print_prompt(client->screen, "Passphrase: ");
253 wattron(client->screen->input_win, A_INVIS);
256 memset(pass1, 0, sizeof(pass1));
257 wgetnstr(client->screen->input_win, pass1, sizeof(pass1));
259 /* Print retype prompt */
260 wattroff(client->screen->input_win, A_INVIS);
261 silc_screen_input_print_prompt(client->screen, "Retype passphrase: ");
262 wattron(client->screen->input_win, A_INVIS);
265 memset(pass2, 0, sizeof(pass2));
266 wgetnstr(client->screen->input_win, pass2, sizeof(pass2));
268 if (!strncmp(pass1, pass2, strlen(pass2)))
274 ret = silc_calloc(strlen(pass1), sizeof(char));
275 memcpy(ret, pass1, strlen(pass1));
277 memset(pass1, 0, sizeof(pass1));
278 memset(pass2, 0, sizeof(pass2));
280 wattroff(client->screen->input_win, A_INVIS);
281 silc_screen_input_reset(client->screen);
286 /* Asks yes/no from user on the input line. Returns TRUE on "yes" and
289 int silc_client_ask_yes_no(SilcClient client, char *prompt)
295 silc_screen_input_reset(client->screen);
298 wattroff(client->screen->input_win, A_INVIS);
299 silc_screen_input_print_prompt(client->screen, prompt);
302 memset(answer, 0, sizeof(answer));
304 wgetnstr(client->screen->input_win, answer, sizeof(answer));
305 if (!strncasecmp(answer, "yes", strlen(answer)) ||
306 !strncasecmp(answer, "y", strlen(answer))) {
308 } else if (!strncasecmp(answer, "no", strlen(answer)) ||
309 !strncasecmp(answer, "n", strlen(answer))) {
312 silc_say(client, "Type yes or no");
317 silc_screen_input_reset(client->screen);
322 /* Lists supported (builtin) ciphers */
324 void silc_client_list_ciphers()
329 /* Lists supported (builtin) hash functions */
331 void silc_client_list_hash_funcs()
336 /* Lists supported PKCS algorithms */
338 void silc_client_list_pkcs()
343 /* Displays input prompt on command line and takes input data from user */
345 char *silc_client_get_input(const char *prompt)
350 fd = open("/dev/tty", O_RDONLY);
352 fprintf(stderr, "silc: %s\n", strerror(errno));
356 memset(input, 0, sizeof(input));
358 printf("%s", prompt);
361 if ((read(fd, input, sizeof(input))) < 0) {
362 fprintf(stderr, "silc: %s\n", strerror(errno));
366 if (strlen(input) <= 1)
369 if (strchr(input, '\n'))
370 *strchr(input, '\n') = '\0';
372 return strdup(input);
375 /* Displays prompt on command line and takes passphrase with echo
378 char *silc_client_get_passphrase(const char *prompt)
385 struct termios to_old;
387 fd = open("/dev/tty", O_RDONLY);
389 fprintf(stderr, "silc: %s\n", strerror(errno));
393 signal(SIGINT, SIG_IGN);
395 /* Get terminal info */
400 to.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
401 tcsetattr(fd, TCSANOW, &to);
403 memset(input, 0, sizeof(input));
405 printf("%s", prompt);
408 if ((read(fd, input, sizeof(input))) < 0) {
409 fprintf(stderr, "silc: %s\n", strerror(errno));
413 if (strlen(input) <= 1) {
414 tcsetattr(fd, TCSANOW, &to_old);
418 if (strchr(input, '\n'))
419 *strchr(input, '\n') = '\0';
421 /* Restore old terminfo */
422 tcsetattr(fd, TCSANOW, &to_old);
423 signal(SIGINT, SIG_DFL);
425 ret = silc_calloc(strlen(input), sizeof(char));
426 memcpy(ret, input, strlen(input));
427 memset(input, 0, sizeof(input));
434 /* Returns identifier string for public key generation. */
436 char *silc_client_create_identifier()
438 char *username = NULL, *realname = NULL;
439 char hostname[256], email[256];
442 realname = silc_get_real_name();
445 memset(hostname, 0, sizeof(hostname));
446 gethostname(hostname, sizeof(hostname));
448 /* Get username (mandatory) */
449 username = silc_get_username();
453 /* Create default email address, whether it is right or not */
454 snprintf(email, sizeof(email), "%s@%s", username, hostname);
456 return silc_pkcs_encode_identifier(username, hostname, realname, email,
460 /* Creates new public key and private key pair. This is used only
461 when user wants to create new key pair from command line. */
463 int silc_client_create_key_pair(char *pkcs_name, int bits,
464 char *public_key, char *private_key,
466 SilcPublicKey *ret_pub_key,
467 SilcPrivateKey *ret_prv_key)
470 SilcPublicKey pub_key;
471 SilcPrivateKey prv_key;
474 unsigned int key_len;
476 char *pkfile = NULL, *prvfile = NULL;
478 if (!pkcs_name || !public_key || !private_key)
480 New pair of keys will be created. Please, answer to following questions.\n\
486 silc_client_get_input("PKCS name (l to list names) [rsa]: ");
488 pkcs_name = strdup("rsa");
490 if (*pkcs_name == 'l' || *pkcs_name == 'L') {
491 silc_client_list_pkcs();
492 silc_free(pkcs_name);
497 if (!silc_pkcs_is_supported(pkcs_name)) {
498 fprintf(stderr, "Unsupported PKCS `%s'", pkcs_name);
505 silc_client_get_input("Key length in bits [1024]: ");
513 char *def = silc_client_create_identifier();
515 memset(line, 0, sizeof(line));
517 snprintf(line, sizeof(line), "Identifier [%s]: ", def);
519 snprintf(line, sizeof(line),
520 "Identifier (eg. UN=jon, HN=jon.dummy.com, "
521 "RN=Jon Johnson, E=jon@dummy.com): ");
523 while (!identifier) {
524 identifier = silc_client_get_input(line);
525 if (!identifier && def)
526 identifier = strdup(def);
533 rng = silc_rng_alloc();
535 silc_math_primegen_init();
538 memset(line, 0, sizeof(line));
539 snprintf(line, sizeof(line), "Public key filename [%s] ",
540 SILC_CLIENT_PUBLIC_KEY_NAME);
541 pkfile = silc_client_get_input(line);
543 pkfile = SILC_CLIENT_PUBLIC_KEY_NAME;
549 memset(line, 0, sizeof(line));
550 snprintf(line, sizeof(line), "Public key filename [%s] ",
551 SILC_CLIENT_PRIVATE_KEY_NAME);
552 prvfile = silc_client_get_input(line);
554 prvfile = SILC_CLIENT_PRIVATE_KEY_NAME;
556 prvfile = private_key;
560 silc_pkcs_alloc(pkcs_name, &pkcs);
561 pkcs->pkcs->init(pkcs->context, bits, rng);
563 /* Save public key into file */
564 key = silc_pkcs_get_public_key(pkcs, &key_len);
565 pub_key = silc_pkcs_public_key_alloc(pkcs->pkcs->name, identifier,
567 silc_pkcs_save_public_key(pkfile, pub_key, SILC_PKCS_FILE_PEM);
569 *ret_pub_key = pub_key;
571 memset(key, 0, sizeof(key_len));
574 /* Save private key into file */
575 key = silc_pkcs_get_private_key(pkcs, &key_len);
576 prv_key = silc_pkcs_private_key_alloc(pkcs->pkcs->name, key, key_len);
578 silc_pkcs_save_private_key(prvfile, prv_key, NULL, SILC_PKCS_FILE_BIN);
580 *ret_prv_key = prv_key;
582 printf("Public key has been save into `%s'.\n", pkfile);
583 printf("Private key has been saved into `%s'.\n", prvfile);
584 printf("Press <Enter> to continue...\n");
587 memset(key, 0, sizeof(key_len));
590 silc_math_primegen_uninit();
592 silc_pkcs_free(pkcs);
597 /* This checks stats for various SILC files and directories. First it
598 checks if ~/.silc directory exist and is owned by the correct user. If
599 it doesn't exist, it will create the directory. After that it checks if
600 user's Public and Private key files exists and that they aren't expired.
601 If they doesn't exist or they are expired, they will be (re)created
604 int silc_client_check_silc_dir()
606 char filename[256], file_public_key[256], file_private_key[256];
607 char servfilename[256];
611 int firstime = FALSE;
612 time_t curtime, modtime;
614 SILC_LOG_DEBUG(("Checking ~./silc directory"));
616 memset(filename, 0, sizeof(filename));
617 memset(file_public_key, 0, sizeof(file_public_key));
618 memset(file_private_key, 0, sizeof(file_private_key));
620 pw = getpwuid(getuid());
622 fprintf(stderr, "silc: %s\n", strerror(errno));
626 identifier = silc_client_create_identifier();
628 /* We'll take home path from /etc/passwd file to be sure. */
629 snprintf(filename, sizeof(filename) - 1, "%s/.silc/", pw->pw_dir);
630 snprintf(servfilename, sizeof(servfilename) - 1, "%s/.silc/serverkeys",
634 * Check ~/.silc directory
636 if ((stat(filename, &st)) == -1) {
637 /* If dir doesn't exist */
638 if (errno == ENOENT) {
639 if (pw->pw_uid == geteuid()) {
640 if ((mkdir(filename, 0755)) == -1) {
641 fprintf(stderr, "Couldn't create `%s' directory\n", filename);
645 /* Directory was created. First time running SILC */
648 fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
653 fprintf(stderr, "%s\n", strerror(errno));
658 /* Check the owner of the dir */
659 if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
660 fprintf(stderr, "You don't seem to own `%s' directory\n",
665 /* Check the permissions of the dir */
666 if ((st.st_mode & 0777) != 0755) {
667 if ((chmod(filename, 0755)) == -1) {
668 fprintf(stderr, "Permissions for `%s' directory must be 0755\n",
676 * Check ~./silc/serverkeys directory
678 if ((stat(servfilename, &st)) == -1) {
679 /* If dir doesn't exist */
680 if (errno == ENOENT) {
681 if (pw->pw_uid == geteuid()) {
682 if ((mkdir(servfilename, 0755)) == -1) {
683 fprintf(stderr, "Couldn't create `%s' directory\n", servfilename);
687 fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
692 fprintf(stderr, "%s\n", strerror(errno));
698 * Check Public and Private keys
700 snprintf(file_public_key, sizeof(file_public_key) - 1, "%s%s",
701 filename, SILC_CLIENT_PUBLIC_KEY_NAME);
702 snprintf(file_private_key, sizeof(file_private_key) - 1, "%s%s",
703 filename, SILC_CLIENT_PRIVATE_KEY_NAME);
705 /* If running SILC first time */
707 fprintf(stdout, "Running SILC for the first time\n");
708 silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS,
709 SILC_CLIENT_DEF_PKCS_LEN,
710 file_public_key, file_private_key,
711 identifier, NULL, NULL);
715 if ((stat(file_public_key, &st)) == -1) {
716 /* If file doesn't exist */
717 if (errno == ENOENT) {
718 fprintf(stdout, "Your public key doesn't exist\n");
719 silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS,
720 SILC_CLIENT_DEF_PKCS_LEN,
722 file_private_key, identifier, NULL, NULL);
724 fprintf(stderr, "%s\n", strerror(errno));
729 if ((stat(file_private_key, &st)) == -1) {
730 /* If file doesn't exist */
731 if (errno == ENOENT) {
732 fprintf(stdout, "Your private key doesn't exist\n");
733 silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS,
734 SILC_CLIENT_DEF_PKCS_LEN,
736 file_private_key, identifier, NULL, NULL);
738 fprintf(stderr, "%s\n", strerror(errno));
743 /* Check the owner of the public key */
744 if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
745 fprintf(stderr, "You don't seem to own your public key!?\n");
749 /* Check the owner of the private key */
750 if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
751 fprintf(stderr, "You don't seem to own your private key!?\n");
755 /* Check the permissions for the private key */
756 if ((st.st_mode & 0777) != 0600) {
757 fprintf(stderr, "Wrong permissions in your private key file `%s'!\n"
758 "Trying to change them ... ", file_private_key);
759 if ((chmod(file_private_key, 0600)) == -1) {
761 "Failed to change permissions for private key file!\n"
762 "Permissions for your private key file must be 0600.\n");
765 fprintf(stderr, "Done.\n\n");
768 /* See if the key has expired. */
769 modtime = st.st_mtime; /* last modified */
770 curtime = time(0) - modtime;
772 /* 86400 is seconds in a day. */
773 if (curtime >= (86400 * SILC_CLIENT_KEY_EXPIRES)) {
775 "--------------------------------------------------\n"
776 "Your private key has expired and needs to be\n"
777 "recreated. This will be done automatically now.\n"
778 "Your new key will expire in %d days from today.\n"
779 "--------------------------------------------------\n",
780 SILC_CLIENT_KEY_EXPIRES);
782 silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS,
783 SILC_CLIENT_DEF_PKCS_LEN,
785 file_private_key, identifier, NULL, NULL);
789 silc_free(identifier);
794 /* Loads public and private key from files. */
796 int silc_client_load_keys(SilcClient client)
801 SILC_LOG_DEBUG(("Loading public and private keys"));
803 pw = getpwuid(getuid());
807 memset(filename, 0, sizeof(filename));
808 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%s",
809 pw->pw_dir, SILC_CLIENT_PRIVATE_KEY_NAME);
811 if (silc_pkcs_load_private_key(filename, &client->private_key,
812 SILC_PKCS_FILE_BIN) == FALSE)
813 if (silc_pkcs_load_private_key(filename, &client->private_key,
814 SILC_PKCS_FILE_PEM) == FALSE)
817 memset(filename, 0, sizeof(filename));
818 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%s",
819 pw->pw_dir, SILC_CLIENT_PUBLIC_KEY_NAME);
821 if (silc_pkcs_load_public_key(filename, &client->public_key,
822 SILC_PKCS_FILE_PEM) == FALSE)
823 if (silc_pkcs_load_public_key(filename, &client->public_key,
824 SILC_PKCS_FILE_BIN) == FALSE)
830 /* Verifies received public key. If user decides to trust the key it is
831 saved as trusted server key for later use. If user does not trust the
832 key this returns FALSE. */
834 int silc_client_verify_server_key(SilcClient client,
835 SilcSocketConnection sock,
836 unsigned char *pk, unsigned int pk_len,
837 SilcSKEPKType pk_type)
841 char *hostname, *fingerprint;
845 hostname = sock->hostname ? sock->hostname : sock->ip;
847 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
848 silc_say(client, "We don't support server %s key type", hostname);
852 pw = getpwuid(getuid());
856 memset(filename, 0, sizeof(filename));
857 memset(file, 0, sizeof(file));
858 snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname,
860 snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s",
863 /* Check wheter this key already exists */
864 if (stat(filename, &st) < 0) {
866 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
867 silc_say(client, "Received server %s public key", hostname);
868 silc_say(client, "Fingerprint for the server %s key is", hostname);
869 silc_say(client, "%s", fingerprint);
870 silc_free(fingerprint);
872 /* Ask user to verify the key and save it */
873 if (silc_client_ask_yes_no(client,
874 "Would you like to accept the key (y/n)? "))
876 /* Save the key for future checking */
877 silc_pkcs_save_public_key_data(filename, pk, pk_len,
882 /* The key already exists, verify it. */
883 SilcPublicKey public_key;
884 unsigned char *encpk;
885 unsigned int encpk_len;
887 /* Load the key file */
888 if (!silc_pkcs_load_public_key(filename, &public_key,
890 if (!silc_pkcs_load_public_key(filename, &public_key,
891 SILC_PKCS_FILE_BIN)) {
892 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
893 silc_say(client, "Received server %s public key", hostname);
894 silc_say(client, "Fingerprint for the server %s key is", hostname);
895 silc_say(client, "%s", fingerprint);
896 silc_free(fingerprint);
897 silc_say(client, "Could not load your local copy of the server %s key",
899 if (silc_client_ask_yes_no(client,
900 "Would you like to accept the key anyway (y/n)? "))
902 /* Save the key for future checking */
904 silc_pkcs_save_public_key_data(filename, pk, pk_len,
912 /* Encode the key data */
913 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
915 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
916 silc_say(client, "Received server %s public key", hostname);
917 silc_say(client, "Fingerprint for the server %s key is", hostname);
918 silc_say(client, "%s", fingerprint);
919 silc_free(fingerprint);
920 silc_say(client, "Your local copy of the server %s key is malformed",
922 if (silc_client_ask_yes_no(client,
923 "Would you like to accept the key anyway (y/n)? "))
925 /* Save the key for future checking */
927 silc_pkcs_save_public_key_data(filename, pk, pk_len,
935 if (memcmp(encpk, pk, encpk_len)) {
936 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
937 silc_say(client, "Received server %s public key", hostname);
938 silc_say(client, "Fingerprint for the server %s key is", hostname);
939 silc_say(client, "%s", fingerprint);
940 silc_free(fingerprint);
941 silc_say(client, "Server %s key does not match with your local copy",
943 silc_say(client, "It is possible that the key has expired or changed");
944 silc_say(client, "It is also possible that some one is performing "
945 "man-in-the-middle attack");
947 /* Ask user to verify the key and save it */
948 if (silc_client_ask_yes_no(client,
949 "Would you like to accept the key anyway (y/n)? "))
951 /* Save the key for future checking */
953 silc_pkcs_save_public_key_data(filename, pk, pk_len,
958 silc_say(client, "Will not accept server %s key", hostname);
962 /* Local copy matched */
966 silc_say(client, "Will not accept server %s key", hostname);
971 /* Parse nickname string. The format may be <num>!<nickname>@<server> to
972 support multiple same nicknames. The <num> is the final unifier if same
973 nickname is on same server. Note, this is only local format and server
974 does not know anything about these. */
976 int silc_client_parse_nickname(char *string, char **nickname, char **server,
985 if (strchr(string, '!')) {
986 tlen = strcspn(string, "!");
987 memset(tmp, 0, sizeof(tmp));
988 memcpy(tmp, string, tlen);
993 if (tlen >= strlen(string))
999 if (strchr(string, '@')) {
1000 tlen = strcspn(string, "@");
1003 *nickname = silc_calloc(tlen + 1, sizeof(char));
1004 memcpy(*nickname, string, tlen);
1008 *server = silc_calloc(strlen(string) - tlen, sizeof(char));
1009 memcpy(*server, string + tlen + 1, strlen(string) - tlen - 1);
1013 *nickname = strdup(string);