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.4 2000/07/10 05:40:05 priikone
24 * Added support for verifying incoming public keys from user.
25 * Shows fingerprint of the public key now plus other changes.
27 * Revision 1.3 2000/07/07 06:53:45 priikone
28 * Added support for server public key verification.
30 * Revision 1.2 2000/07/05 06:11:00 priikone
31 * Added ~./silc directory checking, autoloading of keys and
32 * tweaked the key pair generation function.
34 * Revision 1.1.1.1 2000/06/27 11:36:56 priikone
35 * Imported from internal CVS/Added Log headers.
40 #include "clientincludes.h"
42 /* Internal routine used to print lines to window. This can split the
43 line neatly if a word would overlap the line. */
45 static void silc_print_to_window(WINDOW *win, char *message)
49 str_len = strlen(message);
51 if (str_len > COLS - 1) {
52 /* Split overlapping words to next line */
53 /* XXX In principal this is wrong as this modifies the original
54 string as it replaces the last ' ' with '\n'. This could be done
55 with little more work so that it would not replace anything. */
59 while (len && message[len] != ' ')
72 wprintw(win, "%s", message);
76 /* Prints a message with three star (*) sign before the actual message
77 on the current output window. This is used to print command outputs
78 and error messages. */
79 /* XXX Change to accept SilcClientWindow and use output window
80 from there (the pointer to the output window must be added to the
81 SilcClientWindow object. */
83 void silc_say(SilcClient client, char *msg, ...)
88 memset(message, 0, sizeof(message));
89 strncat(message, "\n*** ", 5);
92 vsprintf(message + 5, msg, vp);
95 /* Print the message */
96 silc_print_to_window(client->screen->output_win[0], message);
99 /* Prints message to the screen. This is used to print the messages
100 user is typed and message that came on channels. */
102 void silc_print(SilcClient client, char *msg, ...)
107 memset(message, 0, sizeof(message));
108 strncat(message, "\n ", 2);
111 vsprintf(message + 1, msg, vp);
114 /* Print the message */
115 silc_print_to_window(client->screen->output_win[0], message);
118 /* Returns user's mail path */
120 char *silc_get_mail_path()
122 char pathbuf[MAXPATHLEN];
125 if ((path = (char *)getenv("MAIL")) != 0) {
126 strncpy(pathbuf, path, strlen(path));
128 strcpy(pathbuf, _PATH_MAILDIR);
129 strcat(pathbuf, "/");
130 strcat(pathbuf, silc_get_username());
133 return strdup(pathbuf);
136 /* gets the number of the user's mails, if possible */
138 int silc_get_number_of_emails()
145 filename = silc_get_mail_path();
147 tl = fopen(filename, "r");
149 fprintf(stderr, "Couldn't open mail file (%s).\n", filename);
151 while((fscanf(tl, "%s", data)) != EOF) {
152 if(!strcmp(data, "Subject:"))
162 /* Returns the username of the user. If the global variable LOGNAME
163 does not exists we will get the name from the password file. */
165 char *silc_get_username()
167 char *logname = NULL;
169 logname = strdup(getenv("LOGNAME"));
171 logname = getlogin();
175 pw = getpwuid(getuid());
177 fprintf(stderr, "silc_get_username: %s\n", strerror(errno));
181 logname = strdup(pw->pw_name);
188 /* Returns the real name of ther user. */
190 char *silc_get_real_name()
192 char *realname = NULL;
195 pw = getpwuid(getuid());
197 fprintf(stderr, "silc_get_username: %s\n", strerror(errno));
201 if (strchr(pw->pw_gecos, ','))
202 *strchr(pw->pw_gecos, ',') = 0;
204 realname = strdup(pw->pw_gecos);
209 /* Returns time til next minute changes. Used to update the clock when
212 int silc_client_time_til_next_min()
218 min = localtime(&curtime);
220 return 60 - min->tm_sec;
223 /* Asks passphrase from user on the input line. */
225 char *silc_client_ask_passphrase(SilcClient client)
227 char pass1[256], pass2[256];
234 wattroff(client->screen->input_win, A_INVIS);
235 silc_screen_input_print_prompt(client->screen, "Passphrase: ");
236 wattron(client->screen->input_win, A_INVIS);
239 memset(pass1, 0, sizeof(pass1));
240 wgetnstr(client->screen->input_win, pass1, sizeof(pass1));
242 /* Print retype prompt */
243 wattroff(client->screen->input_win, A_INVIS);
244 silc_screen_input_print_prompt(client->screen, "Retype passphrase: ");
245 wattron(client->screen->input_win, A_INVIS);
248 memset(pass2, 0, sizeof(pass2));
249 wgetnstr(client->screen->input_win, pass2, sizeof(pass2));
251 if (!strncmp(pass1, pass2, strlen(pass2)))
257 ret = silc_calloc(strlen(pass1), sizeof(char));
258 memcpy(ret, pass1, strlen(pass1));
260 memset(pass1, 0, sizeof(pass1));
261 memset(pass2, 0, sizeof(pass2));
263 wattroff(client->screen->input_win, A_INVIS);
264 silc_screen_input_reset(client->screen);
269 /* Asks yes/no from user on the input line. Returns TRUE on "yes" and
272 int silc_client_ask_yes_no(SilcClient client, char *prompt)
278 silc_screen_input_reset(client->screen);
281 wattroff(client->screen->input_win, A_INVIS);
282 silc_screen_input_print_prompt(client->screen, prompt);
285 memset(answer, 0, sizeof(answer));
287 wgetnstr(client->screen->input_win, answer, sizeof(answer));
288 if (!strncasecmp(answer, "yes", strlen(answer)) ||
289 !strncasecmp(answer, "y", strlen(answer))) {
291 } else if (!strncasecmp(answer, "no", strlen(answer)) ||
292 !strncasecmp(answer, "n", strlen(answer))) {
295 silc_say(client, "Type yes or no");
300 silc_screen_input_reset(client->screen);
305 /* Lists supported (builtin) ciphers */
307 void silc_client_list_ciphers()
312 /* Lists supported (builtin) hash functions */
314 void silc_client_list_hash_funcs()
319 /* Lists supported PKCS algorithms */
321 void silc_client_list_pkcs()
326 /* Displays input prompt on command line and takes input data from user */
328 char *silc_client_get_input(const char *prompt)
333 fd = open("/dev/tty", O_RDONLY);
335 fprintf(stderr, "silc: %s\n", strerror(errno));
339 memset(input, 0, sizeof(input));
341 printf("%s", prompt);
344 if ((read(fd, input, sizeof(input))) < 0) {
345 fprintf(stderr, "silc: %s\n", strerror(errno));
349 if (strlen(input) <= 1)
352 if (strchr(input, '\n'))
353 *strchr(input, '\n') = '\0';
355 return strdup(input);
358 /* Displays prompt on command line and takes passphrase with echo
361 char *silc_client_get_passphrase(const char *prompt)
368 struct termios to_old;
370 fd = open("/dev/tty", O_RDONLY);
372 fprintf(stderr, "silc: %s\n", strerror(errno));
376 signal(SIGINT, SIG_IGN);
378 /* Get terminal info */
383 to.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
384 tcsetattr(fd, TCSANOW, &to);
386 memset(input, 0, sizeof(input));
388 printf("%s", prompt);
391 if ((read(fd, input, sizeof(input))) < 0) {
392 fprintf(stderr, "silc: %s\n", strerror(errno));
396 if (strlen(input) <= 1) {
397 tcsetattr(fd, TCSANOW, &to_old);
401 if (strchr(input, '\n'))
402 *strchr(input, '\n') = '\0';
404 /* Restore old terminfo */
405 tcsetattr(fd, TCSANOW, &to_old);
406 signal(SIGINT, SIG_DFL);
408 ret = silc_calloc(strlen(input), sizeof(char));
409 memcpy(ret, input, strlen(input));
410 memset(input, 0, sizeof(input));
417 /* Returns identifier string for public key generation. */
419 char *silc_client_create_identifier()
421 char *username = NULL, *realname = NULL;
422 char hostname[256], email[256];
425 realname = silc_get_real_name();
428 memset(hostname, 0, sizeof(hostname));
429 gethostname(hostname, sizeof(hostname));
431 /* Get username (mandatory) */
432 username = silc_get_username();
436 /* Create default email address, whether it is right or not */
437 snprintf(email, sizeof(email), "%s@%s", username, hostname);
439 return silc_pkcs_encode_identifier(username, hostname, realname, email,
443 /* Creates new public key and private key pair. This is used only
444 when user wants to create new key pair from command line. */
446 int silc_client_create_key_pair(char *pkcs_name, int bits,
447 char *public_key, char *private_key,
449 SilcPublicKey *ret_pub_key,
450 SilcPrivateKey *ret_prv_key)
453 SilcPublicKey pub_key;
454 SilcPrivateKey prv_key;
457 unsigned int key_len;
458 char *pkfile = NULL, *prvfile = NULL;
460 if (!pkcs_name || !public_key || !private_key)
462 New pair of keys will be created. Please, answer to following questions.\n\
468 silc_client_get_input("PKCS name (l to list names) [rsa]: ");
470 pkcs_name = strdup("rsa");
472 if (*pkcs_name == 'l' || *pkcs_name == 'L') {
473 silc_client_list_pkcs();
474 silc_free(pkcs_name);
479 if (!silc_pkcs_is_supported(pkcs_name)) {
480 fprintf(stderr, "Unsupported PKCS `%s'", pkcs_name);
487 silc_client_get_input("Key length in bits [1024]: ");
495 char *def = silc_client_create_identifier();
498 snprintf(def, sizeof(def), "Public key identifier [%s]: ", def);
500 snprintf(def, sizeof(def),
501 "Public key identifier (eg. UN=priikone, HN=poseidon.pspt.fi, "
502 "RN=Pekka Riikonen, E=priikone@poseidon.pspt.fi): ");
505 identifier = silc_client_get_input(def);
513 rng = silc_rng_alloc();
515 silc_math_primegen_init();
519 pkfile = silc_client_get_input("Public key filename: ");
521 printf("Public key filename must be defined\n");
530 prvfile = silc_client_get_input("Private key filename: ");
532 printf("Private key filename must be defined\n");
536 prvfile = private_key;
540 silc_pkcs_alloc(pkcs_name, &pkcs);
541 pkcs->pkcs->init(pkcs->context, bits, rng);
543 /* Save public key into file */
544 key = silc_pkcs_get_public_key(pkcs, &key_len);
545 pub_key = silc_pkcs_public_key_alloc(pkcs->pkcs->name, identifier,
547 silc_pkcs_save_public_key(pkfile, pub_key, SILC_PKCS_FILE_PEM);
549 *ret_pub_key = pub_key;
551 memset(key, 0, sizeof(key_len));
554 /* Save private key into file */
555 key = silc_pkcs_get_private_key(pkcs, &key_len);
556 prv_key = silc_pkcs_private_key_alloc(pkcs->pkcs->name, key, key_len);
558 silc_pkcs_save_private_key(prvfile, prv_key, NULL, SILC_PKCS_FILE_BIN);
560 *ret_prv_key = prv_key;
562 printf("Public key has been save into `%s'.\n", pkfile);
563 printf("Private key has been saved into `%s'.\n", prvfile);
564 printf("Press <Enter> to continue...\n");
567 memset(key, 0, sizeof(key_len));
570 silc_math_primegen_uninit();
572 silc_pkcs_free(pkcs);
577 /* This checks stats for various SILC files and directories. First it
578 checks if ~/.silc directory exist and is owned by the correct user. If
579 it doesn't exist, it will create the directory. After that it checks if
580 user's Public and Private key files exists and that they aren't expired.
581 If they doesn't exist or they are expired, they will be (re)created
584 int silc_client_check_silc_dir()
586 char filename[256], file_public_key[256], file_private_key[256];
587 char servfilename[256];
591 int firstime = FALSE;
592 time_t curtime, modtime;
594 SILC_LOG_DEBUG(("Checking ~./silc directory"));
596 memset(filename, 0, sizeof(filename));
597 memset(file_public_key, 0, sizeof(file_public_key));
598 memset(file_private_key, 0, sizeof(file_private_key));
600 pw = getpwuid(getuid());
602 fprintf(stderr, "silc: %s\n", strerror(errno));
606 identifier = silc_client_create_identifier();
608 /* We'll take home path from /etc/passwd file to be sure. */
609 snprintf(filename, sizeof(filename) - 1, "%s/.silc/", pw->pw_dir);
610 snprintf(servfilename, sizeof(servfilename) - 1, "%s/.silc/serverkeys",
614 * Check ~/.silc directory
616 if ((stat(filename, &st)) == -1) {
617 /* If dir doesn't exist */
618 if (errno == ENOENT) {
619 if (pw->pw_uid == geteuid()) {
620 if ((mkdir(filename, 0755)) == -1) {
621 fprintf(stderr, "Couldn't create `%s' directory\n", filename);
625 /* Directory was created. First time running SILC */
628 fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
633 fprintf(stderr, "%s\n", strerror(errno));
638 /* Check the owner of the dir */
639 if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
640 fprintf(stderr, "You don't seem to own `%s' directory\n",
645 /* Check the permissions of the dir */
646 if ((st.st_mode & 0777) != 0755) {
647 if ((chmod(filename, 0755)) == -1) {
648 fprintf(stderr, "Permissions for `%s' directory must be 0755\n",
656 * Check ~./silc/serverkeys directory
658 if ((stat(servfilename, &st)) == -1) {
659 /* If dir doesn't exist */
660 if (errno == ENOENT) {
661 if (pw->pw_uid == geteuid()) {
662 if ((mkdir(servfilename, 0755)) == -1) {
663 fprintf(stderr, "Couldn't create `%s' directory\n", servfilename);
667 fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
672 fprintf(stderr, "%s\n", strerror(errno));
678 * Check Public and Private keys
680 snprintf(file_public_key, sizeof(file_public_key) - 1, "%s%s",
681 filename, SILC_CLIENT_PUBLIC_KEY_NAME);
682 snprintf(file_private_key, sizeof(file_private_key) - 1, "%s%s",
683 filename, SILC_CLIENT_PRIVATE_KEY_NAME);
685 /* If running SILC first time */
687 fprintf(stdout, "Running SILC for the first time\n");
688 silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS,
689 SILC_CLIENT_DEF_PKCS_LEN,
690 file_public_key, file_private_key,
691 identifier, NULL, NULL);
695 if ((stat(file_public_key, &st)) == -1) {
696 /* If file doesn't exist */
697 if (errno == ENOENT) {
698 fprintf(stdout, "Your public key doesn't exist\n");
699 silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS,
700 SILC_CLIENT_DEF_PKCS_LEN,
702 file_private_key, identifier, NULL, NULL);
704 fprintf(stderr, "%s\n", strerror(errno));
709 if ((stat(file_private_key, &st)) == -1) {
710 /* If file doesn't exist */
711 if (errno == ENOENT) {
712 fprintf(stdout, "Your private key doesn't exist\n");
713 silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS,
714 SILC_CLIENT_DEF_PKCS_LEN,
716 file_private_key, identifier, NULL, NULL);
718 fprintf(stderr, "%s\n", strerror(errno));
723 /* Check the owner of the public key */
724 if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
725 fprintf(stderr, "You don't seem to own your public key!?\n");
729 /* Check the owner of the private key */
730 if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
731 fprintf(stderr, "You don't seem to own your private key!?\n");
735 /* Check the permissions for the private key */
736 if ((st.st_mode & 0777) != 0600) {
737 fprintf(stderr, "Wrong permissions in your private key file `%s'!\n"
738 "Trying to change them ... ", file_private_key);
739 if ((chmod(file_private_key, 0600)) == -1) {
741 "Failed to change permissions for private key file!\n"
742 "Permissions for your private key file must be 0600.\n");
745 fprintf(stderr, "Done.\n\n");
748 /* See if the key has expired. */
749 modtime = st.st_mtime; /* last modified */
750 curtime = time(0) - modtime;
752 /* 86400 is seconds in a day. */
753 if (curtime >= (86400 * SILC_CLIENT_KEY_EXPIRES)) {
755 "--------------------------------------------------\n"
756 "Your private key has expired and needs to be\n"
757 "recreated. This will be done automatically now.\n"
758 "Your new key will expire in %d days from today.\n"
759 "--------------------------------------------------\n",
760 SILC_CLIENT_KEY_EXPIRES);
762 silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS,
763 SILC_CLIENT_DEF_PKCS_LEN,
765 file_private_key, identifier, NULL, NULL);
769 silc_free(identifier);
774 /* Loads public and private key from files. */
776 int silc_client_load_keys(SilcClient client)
781 SILC_LOG_DEBUG(("Loading public and private keys"));
783 pw = getpwuid(getuid());
787 memset(filename, 0, sizeof(filename));
788 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%s",
789 pw->pw_dir, SILC_CLIENT_PRIVATE_KEY_NAME);
791 if (silc_pkcs_load_private_key(filename, &client->private_key,
792 SILC_PKCS_FILE_BIN) == FALSE)
793 if (silc_pkcs_load_private_key(filename, &client->private_key,
794 SILC_PKCS_FILE_PEM) == FALSE)
797 memset(filename, 0, sizeof(filename));
798 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%s",
799 pw->pw_dir, SILC_CLIENT_PUBLIC_KEY_NAME);
801 if (silc_pkcs_load_public_key(filename, &client->public_key,
802 SILC_PKCS_FILE_PEM) == FALSE)
803 if (silc_pkcs_load_public_key(filename, &client->public_key,
804 SILC_PKCS_FILE_BIN) == FALSE)
810 /* Verifies received public key. If user decides to trust the key it is
811 saved as trusted server key for later use. If user does not trust the
812 key this returns FALSE. */
814 int silc_client_verify_server_key(SilcClient client,
815 SilcSocketConnection sock,
816 unsigned char *pk, unsigned int pk_len,
817 SilcSKEPKType pk_type)
821 char *hostname, *fingerprint;
825 hostname = sock->hostname ? sock->hostname : sock->ip;
827 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
828 silc_say(client, "We don't support server %s key type", hostname);
832 pw = getpwuid(getuid());
836 memset(filename, 0, sizeof(filename));
837 memset(file, 0, sizeof(file));
838 snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname,
840 snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s",
843 /* Check wheter this key already exists */
844 if (stat(filename, &st) < 0) {
846 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
847 silc_say(client, "Received server %s public key", hostname);
848 silc_say(client, "Fingerprint for the server %s key is", hostname);
849 silc_say(client, "%s", fingerprint);
850 silc_free(fingerprint);
852 /* Ask user to verify the key and save it */
853 if (silc_client_ask_yes_no(client,
854 "Would you like to accept the key (y/n)? "))
856 /* Save the key for future checking */
857 silc_pkcs_save_public_key_data(filename, pk, pk_len,
862 /* The key already exists, verify it. */
863 SilcPublicKey public_key;
864 unsigned char *encpk;
865 unsigned int encpk_len;
867 /* Load the key file */
868 if (!silc_pkcs_load_public_key(filename, &public_key,
870 if (!silc_pkcs_load_public_key(filename, &public_key,
871 SILC_PKCS_FILE_BIN)) {
872 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
873 silc_say(client, "Received server %s public key", hostname);
874 silc_say(client, "Fingerprint for the server %s key is", hostname);
875 silc_say(client, "%s", fingerprint);
876 silc_free(fingerprint);
877 silc_say(client, "Could not load your local copy of the server %s key",
879 if (silc_client_ask_yes_no(client,
880 "Would you like to accept the key anyway (y/n)? "))
882 /* Save the key for future checking */
884 silc_pkcs_save_public_key_data(filename, pk, pk_len,
892 /* Encode the key data */
893 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
895 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
896 silc_say(client, "Received server %s public key", hostname);
897 silc_say(client, "Fingerprint for the server %s key is", hostname);
898 silc_say(client, "%s", fingerprint);
899 silc_free(fingerprint);
900 silc_say(client, "Your local copy of the server %s key is malformed",
902 if (silc_client_ask_yes_no(client,
903 "Would you like to accept the key anyway (y/n)? "))
905 /* Save the key for future checking */
907 silc_pkcs_save_public_key_data(filename, pk, pk_len,
915 if (memcmp(encpk, pk, encpk_len)) {
916 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
917 silc_say(client, "Received server %s public key", hostname);
918 silc_say(client, "Fingerprint for the server %s key is", hostname);
919 silc_say(client, "%s", fingerprint);
920 silc_free(fingerprint);
921 silc_say(client, "Server %s key does not match with your local copy",
923 silc_say(client, "It is possible that the key has expired or changed");
924 silc_say(client, "It is also possible that some one is performing "
925 "man-in-the-middle attack");
927 /* Ask user to verify the key and save it */
928 if (silc_client_ask_yes_no(client,
929 "Would you like to accept the key anyway (y/n)? "))
931 /* Save the key for future checking */
933 silc_pkcs_save_public_key_data(filename, pk, pk_len,
938 silc_say(client, "Will not accept server %s key", hostname);
942 /* Local copy matched */
946 silc_say(client, "Will not accept server %s key", hostname);