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.5 2000/07/12 05:55:50 priikone
24 * Added client_parse_nickname.
26 * Revision 1.4 2000/07/10 05:40:05 priikone
27 * Added support for verifying incoming public keys from user.
28 * Shows fingerprint of the public key now plus other changes.
30 * Revision 1.3 2000/07/07 06:53:45 priikone
31 * Added support for server public key verification.
33 * Revision 1.2 2000/07/05 06:11:00 priikone
34 * Added ~./silc directory checking, autoloading of keys and
35 * tweaked the key pair generation function.
37 * Revision 1.1.1.1 2000/06/27 11:36:56 priikone
38 * Imported from internal CVS/Added Log headers.
43 #include "clientincludes.h"
45 /* Internal routine used to print lines to window. This can split the
46 line neatly if a word would overlap the line. */
48 static void silc_print_to_window(WINDOW *win, char *message)
52 str_len = strlen(message);
54 if (str_len > COLS - 1) {
55 /* Split overlapping words to next line */
56 /* XXX In principal this is wrong as this modifies the original
57 string as it replaces the last ' ' with '\n'. This could be done
58 with little more work so that it would not replace anything. */
62 while (len && message[len] != ' ')
75 wprintw(win, "%s", message);
79 /* Prints a message with three star (*) sign before the actual message
80 on the current output window. This is used to print command outputs
81 and error messages. */
82 /* XXX Change to accept SilcClientWindow and use output window
83 from there (the pointer to the output window must be added to the
84 SilcClientWindow object. */
86 void silc_say(SilcClient client, char *msg, ...)
91 memset(message, 0, sizeof(message));
92 strncat(message, "\n*** ", 5);
95 vsprintf(message + 5, msg, vp);
98 /* Print the message */
99 silc_print_to_window(client->screen->output_win[0], message);
102 /* Prints message to the screen. This is used to print the messages
103 user is typed and message that came on channels. */
105 void silc_print(SilcClient client, char *msg, ...)
110 memset(message, 0, sizeof(message));
111 strncat(message, "\n ", 2);
114 vsprintf(message + 1, msg, vp);
117 /* Print the message */
118 silc_print_to_window(client->screen->output_win[0], message);
121 /* Returns user's mail path */
123 char *silc_get_mail_path()
125 char pathbuf[MAXPATHLEN];
128 if ((path = (char *)getenv("MAIL")) != 0) {
129 strncpy(pathbuf, path, strlen(path));
131 strcpy(pathbuf, _PATH_MAILDIR);
132 strcat(pathbuf, "/");
133 strcat(pathbuf, silc_get_username());
136 return strdup(pathbuf);
139 /* gets the number of the user's mails, if possible */
141 int silc_get_number_of_emails()
148 filename = silc_get_mail_path();
150 tl = fopen(filename, "r");
152 fprintf(stderr, "Couldn't open mail file (%s).\n", filename);
154 while((fscanf(tl, "%s", data)) != EOF) {
155 if(!strcmp(data, "Subject:"))
165 /* Returns the username of the user. If the global variable LOGNAME
166 does not exists we will get the name from the password file. */
168 char *silc_get_username()
170 char *logname = NULL;
172 logname = strdup(getenv("LOGNAME"));
174 logname = getlogin();
178 pw = getpwuid(getuid());
180 fprintf(stderr, "silc_get_username: %s\n", strerror(errno));
184 logname = strdup(pw->pw_name);
191 /* Returns the real name of ther user. */
193 char *silc_get_real_name()
195 char *realname = NULL;
198 pw = getpwuid(getuid());
200 fprintf(stderr, "silc_get_username: %s\n", strerror(errno));
204 if (strchr(pw->pw_gecos, ','))
205 *strchr(pw->pw_gecos, ',') = 0;
207 realname = strdup(pw->pw_gecos);
212 /* Returns time til next minute changes. Used to update the clock when
215 int silc_client_time_til_next_min()
221 min = localtime(&curtime);
223 return 60 - min->tm_sec;
226 /* Asks passphrase from user on the input line. */
228 char *silc_client_ask_passphrase(SilcClient client)
230 char pass1[256], pass2[256];
237 wattroff(client->screen->input_win, A_INVIS);
238 silc_screen_input_print_prompt(client->screen, "Passphrase: ");
239 wattron(client->screen->input_win, A_INVIS);
242 memset(pass1, 0, sizeof(pass1));
243 wgetnstr(client->screen->input_win, pass1, sizeof(pass1));
245 /* Print retype prompt */
246 wattroff(client->screen->input_win, A_INVIS);
247 silc_screen_input_print_prompt(client->screen, "Retype passphrase: ");
248 wattron(client->screen->input_win, A_INVIS);
251 memset(pass2, 0, sizeof(pass2));
252 wgetnstr(client->screen->input_win, pass2, sizeof(pass2));
254 if (!strncmp(pass1, pass2, strlen(pass2)))
260 ret = silc_calloc(strlen(pass1), sizeof(char));
261 memcpy(ret, pass1, strlen(pass1));
263 memset(pass1, 0, sizeof(pass1));
264 memset(pass2, 0, sizeof(pass2));
266 wattroff(client->screen->input_win, A_INVIS);
267 silc_screen_input_reset(client->screen);
272 /* Asks yes/no from user on the input line. Returns TRUE on "yes" and
275 int silc_client_ask_yes_no(SilcClient client, char *prompt)
281 silc_screen_input_reset(client->screen);
284 wattroff(client->screen->input_win, A_INVIS);
285 silc_screen_input_print_prompt(client->screen, prompt);
288 memset(answer, 0, sizeof(answer));
290 wgetnstr(client->screen->input_win, answer, sizeof(answer));
291 if (!strncasecmp(answer, "yes", strlen(answer)) ||
292 !strncasecmp(answer, "y", strlen(answer))) {
294 } else if (!strncasecmp(answer, "no", strlen(answer)) ||
295 !strncasecmp(answer, "n", strlen(answer))) {
298 silc_say(client, "Type yes or no");
303 silc_screen_input_reset(client->screen);
308 /* Lists supported (builtin) ciphers */
310 void silc_client_list_ciphers()
315 /* Lists supported (builtin) hash functions */
317 void silc_client_list_hash_funcs()
322 /* Lists supported PKCS algorithms */
324 void silc_client_list_pkcs()
329 /* Displays input prompt on command line and takes input data from user */
331 char *silc_client_get_input(const char *prompt)
336 fd = open("/dev/tty", O_RDONLY);
338 fprintf(stderr, "silc: %s\n", strerror(errno));
342 memset(input, 0, sizeof(input));
344 printf("%s", prompt);
347 if ((read(fd, input, sizeof(input))) < 0) {
348 fprintf(stderr, "silc: %s\n", strerror(errno));
352 if (strlen(input) <= 1)
355 if (strchr(input, '\n'))
356 *strchr(input, '\n') = '\0';
358 return strdup(input);
361 /* Displays prompt on command line and takes passphrase with echo
364 char *silc_client_get_passphrase(const char *prompt)
371 struct termios to_old;
373 fd = open("/dev/tty", O_RDONLY);
375 fprintf(stderr, "silc: %s\n", strerror(errno));
379 signal(SIGINT, SIG_IGN);
381 /* Get terminal info */
386 to.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
387 tcsetattr(fd, TCSANOW, &to);
389 memset(input, 0, sizeof(input));
391 printf("%s", prompt);
394 if ((read(fd, input, sizeof(input))) < 0) {
395 fprintf(stderr, "silc: %s\n", strerror(errno));
399 if (strlen(input) <= 1) {
400 tcsetattr(fd, TCSANOW, &to_old);
404 if (strchr(input, '\n'))
405 *strchr(input, '\n') = '\0';
407 /* Restore old terminfo */
408 tcsetattr(fd, TCSANOW, &to_old);
409 signal(SIGINT, SIG_DFL);
411 ret = silc_calloc(strlen(input), sizeof(char));
412 memcpy(ret, input, strlen(input));
413 memset(input, 0, sizeof(input));
420 /* Returns identifier string for public key generation. */
422 char *silc_client_create_identifier()
424 char *username = NULL, *realname = NULL;
425 char hostname[256], email[256];
428 realname = silc_get_real_name();
431 memset(hostname, 0, sizeof(hostname));
432 gethostname(hostname, sizeof(hostname));
434 /* Get username (mandatory) */
435 username = silc_get_username();
439 /* Create default email address, whether it is right or not */
440 snprintf(email, sizeof(email), "%s@%s", username, hostname);
442 return silc_pkcs_encode_identifier(username, hostname, realname, email,
446 /* Creates new public key and private key pair. This is used only
447 when user wants to create new key pair from command line. */
449 int silc_client_create_key_pair(char *pkcs_name, int bits,
450 char *public_key, char *private_key,
452 SilcPublicKey *ret_pub_key,
453 SilcPrivateKey *ret_prv_key)
456 SilcPublicKey pub_key;
457 SilcPrivateKey prv_key;
460 unsigned int key_len;
461 char *pkfile = NULL, *prvfile = NULL;
463 if (!pkcs_name || !public_key || !private_key)
465 New pair of keys will be created. Please, answer to following questions.\n\
471 silc_client_get_input("PKCS name (l to list names) [rsa]: ");
473 pkcs_name = strdup("rsa");
475 if (*pkcs_name == 'l' || *pkcs_name == 'L') {
476 silc_client_list_pkcs();
477 silc_free(pkcs_name);
482 if (!silc_pkcs_is_supported(pkcs_name)) {
483 fprintf(stderr, "Unsupported PKCS `%s'", pkcs_name);
490 silc_client_get_input("Key length in bits [1024]: ");
498 char *def = silc_client_create_identifier();
501 snprintf(def, sizeof(def), "Public key identifier [%s]: ", def);
503 snprintf(def, sizeof(def),
504 "Public key identifier (eg. UN=priikone, HN=poseidon.pspt.fi, "
505 "RN=Pekka Riikonen, E=priikone@poseidon.pspt.fi): ");
508 identifier = silc_client_get_input(def);
516 rng = silc_rng_alloc();
518 silc_math_primegen_init();
522 pkfile = silc_client_get_input("Public key filename: ");
524 printf("Public key filename must be defined\n");
533 prvfile = silc_client_get_input("Private key filename: ");
535 printf("Private key filename must be defined\n");
539 prvfile = private_key;
543 silc_pkcs_alloc(pkcs_name, &pkcs);
544 pkcs->pkcs->init(pkcs->context, bits, rng);
546 /* Save public key into file */
547 key = silc_pkcs_get_public_key(pkcs, &key_len);
548 pub_key = silc_pkcs_public_key_alloc(pkcs->pkcs->name, identifier,
550 silc_pkcs_save_public_key(pkfile, pub_key, SILC_PKCS_FILE_PEM);
552 *ret_pub_key = pub_key;
554 memset(key, 0, sizeof(key_len));
557 /* Save private key into file */
558 key = silc_pkcs_get_private_key(pkcs, &key_len);
559 prv_key = silc_pkcs_private_key_alloc(pkcs->pkcs->name, key, key_len);
561 silc_pkcs_save_private_key(prvfile, prv_key, NULL, SILC_PKCS_FILE_BIN);
563 *ret_prv_key = prv_key;
565 printf("Public key has been save into `%s'.\n", pkfile);
566 printf("Private key has been saved into `%s'.\n", prvfile);
567 printf("Press <Enter> to continue...\n");
570 memset(key, 0, sizeof(key_len));
573 silc_math_primegen_uninit();
575 silc_pkcs_free(pkcs);
580 /* This checks stats for various SILC files and directories. First it
581 checks if ~/.silc directory exist and is owned by the correct user. If
582 it doesn't exist, it will create the directory. After that it checks if
583 user's Public and Private key files exists and that they aren't expired.
584 If they doesn't exist or they are expired, they will be (re)created
587 int silc_client_check_silc_dir()
589 char filename[256], file_public_key[256], file_private_key[256];
590 char servfilename[256];
594 int firstime = FALSE;
595 time_t curtime, modtime;
597 SILC_LOG_DEBUG(("Checking ~./silc directory"));
599 memset(filename, 0, sizeof(filename));
600 memset(file_public_key, 0, sizeof(file_public_key));
601 memset(file_private_key, 0, sizeof(file_private_key));
603 pw = getpwuid(getuid());
605 fprintf(stderr, "silc: %s\n", strerror(errno));
609 identifier = silc_client_create_identifier();
611 /* We'll take home path from /etc/passwd file to be sure. */
612 snprintf(filename, sizeof(filename) - 1, "%s/.silc/", pw->pw_dir);
613 snprintf(servfilename, sizeof(servfilename) - 1, "%s/.silc/serverkeys",
617 * Check ~/.silc directory
619 if ((stat(filename, &st)) == -1) {
620 /* If dir doesn't exist */
621 if (errno == ENOENT) {
622 if (pw->pw_uid == geteuid()) {
623 if ((mkdir(filename, 0755)) == -1) {
624 fprintf(stderr, "Couldn't create `%s' directory\n", filename);
628 /* Directory was created. First time running SILC */
631 fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
636 fprintf(stderr, "%s\n", strerror(errno));
641 /* Check the owner of the dir */
642 if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
643 fprintf(stderr, "You don't seem to own `%s' directory\n",
648 /* Check the permissions of the dir */
649 if ((st.st_mode & 0777) != 0755) {
650 if ((chmod(filename, 0755)) == -1) {
651 fprintf(stderr, "Permissions for `%s' directory must be 0755\n",
659 * Check ~./silc/serverkeys directory
661 if ((stat(servfilename, &st)) == -1) {
662 /* If dir doesn't exist */
663 if (errno == ENOENT) {
664 if (pw->pw_uid == geteuid()) {
665 if ((mkdir(servfilename, 0755)) == -1) {
666 fprintf(stderr, "Couldn't create `%s' directory\n", servfilename);
670 fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
675 fprintf(stderr, "%s\n", strerror(errno));
681 * Check Public and Private keys
683 snprintf(file_public_key, sizeof(file_public_key) - 1, "%s%s",
684 filename, SILC_CLIENT_PUBLIC_KEY_NAME);
685 snprintf(file_private_key, sizeof(file_private_key) - 1, "%s%s",
686 filename, SILC_CLIENT_PRIVATE_KEY_NAME);
688 /* If running SILC first time */
690 fprintf(stdout, "Running SILC for the first time\n");
691 silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS,
692 SILC_CLIENT_DEF_PKCS_LEN,
693 file_public_key, file_private_key,
694 identifier, NULL, NULL);
698 if ((stat(file_public_key, &st)) == -1) {
699 /* If file doesn't exist */
700 if (errno == ENOENT) {
701 fprintf(stdout, "Your public key doesn't exist\n");
702 silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS,
703 SILC_CLIENT_DEF_PKCS_LEN,
705 file_private_key, identifier, NULL, NULL);
707 fprintf(stderr, "%s\n", strerror(errno));
712 if ((stat(file_private_key, &st)) == -1) {
713 /* If file doesn't exist */
714 if (errno == ENOENT) {
715 fprintf(stdout, "Your private key doesn't exist\n");
716 silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS,
717 SILC_CLIENT_DEF_PKCS_LEN,
719 file_private_key, identifier, NULL, NULL);
721 fprintf(stderr, "%s\n", strerror(errno));
726 /* Check the owner of the public key */
727 if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
728 fprintf(stderr, "You don't seem to own your public key!?\n");
732 /* Check the owner of the private key */
733 if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
734 fprintf(stderr, "You don't seem to own your private key!?\n");
738 /* Check the permissions for the private key */
739 if ((st.st_mode & 0777) != 0600) {
740 fprintf(stderr, "Wrong permissions in your private key file `%s'!\n"
741 "Trying to change them ... ", file_private_key);
742 if ((chmod(file_private_key, 0600)) == -1) {
744 "Failed to change permissions for private key file!\n"
745 "Permissions for your private key file must be 0600.\n");
748 fprintf(stderr, "Done.\n\n");
751 /* See if the key has expired. */
752 modtime = st.st_mtime; /* last modified */
753 curtime = time(0) - modtime;
755 /* 86400 is seconds in a day. */
756 if (curtime >= (86400 * SILC_CLIENT_KEY_EXPIRES)) {
758 "--------------------------------------------------\n"
759 "Your private key has expired and needs to be\n"
760 "recreated. This will be done automatically now.\n"
761 "Your new key will expire in %d days from today.\n"
762 "--------------------------------------------------\n",
763 SILC_CLIENT_KEY_EXPIRES);
765 silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS,
766 SILC_CLIENT_DEF_PKCS_LEN,
768 file_private_key, identifier, NULL, NULL);
772 silc_free(identifier);
777 /* Loads public and private key from files. */
779 int silc_client_load_keys(SilcClient client)
784 SILC_LOG_DEBUG(("Loading public and private keys"));
786 pw = getpwuid(getuid());
790 memset(filename, 0, sizeof(filename));
791 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%s",
792 pw->pw_dir, SILC_CLIENT_PRIVATE_KEY_NAME);
794 if (silc_pkcs_load_private_key(filename, &client->private_key,
795 SILC_PKCS_FILE_BIN) == FALSE)
796 if (silc_pkcs_load_private_key(filename, &client->private_key,
797 SILC_PKCS_FILE_PEM) == FALSE)
800 memset(filename, 0, sizeof(filename));
801 snprintf(filename, sizeof(filename) - 1, "%s/.silc/%s",
802 pw->pw_dir, SILC_CLIENT_PUBLIC_KEY_NAME);
804 if (silc_pkcs_load_public_key(filename, &client->public_key,
805 SILC_PKCS_FILE_PEM) == FALSE)
806 if (silc_pkcs_load_public_key(filename, &client->public_key,
807 SILC_PKCS_FILE_BIN) == FALSE)
813 /* Verifies received public key. If user decides to trust the key it is
814 saved as trusted server key for later use. If user does not trust the
815 key this returns FALSE. */
817 int silc_client_verify_server_key(SilcClient client,
818 SilcSocketConnection sock,
819 unsigned char *pk, unsigned int pk_len,
820 SilcSKEPKType pk_type)
824 char *hostname, *fingerprint;
828 hostname = sock->hostname ? sock->hostname : sock->ip;
830 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
831 silc_say(client, "We don't support server %s key type", hostname);
835 pw = getpwuid(getuid());
839 memset(filename, 0, sizeof(filename));
840 memset(file, 0, sizeof(file));
841 snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname,
843 snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s",
846 /* Check wheter this key already exists */
847 if (stat(filename, &st) < 0) {
849 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
850 silc_say(client, "Received server %s public key", hostname);
851 silc_say(client, "Fingerprint for the server %s key is", hostname);
852 silc_say(client, "%s", fingerprint);
853 silc_free(fingerprint);
855 /* Ask user to verify the key and save it */
856 if (silc_client_ask_yes_no(client,
857 "Would you like to accept the key (y/n)? "))
859 /* Save the key for future checking */
860 silc_pkcs_save_public_key_data(filename, pk, pk_len,
865 /* The key already exists, verify it. */
866 SilcPublicKey public_key;
867 unsigned char *encpk;
868 unsigned int encpk_len;
870 /* Load the key file */
871 if (!silc_pkcs_load_public_key(filename, &public_key,
873 if (!silc_pkcs_load_public_key(filename, &public_key,
874 SILC_PKCS_FILE_BIN)) {
875 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
876 silc_say(client, "Received server %s public key", hostname);
877 silc_say(client, "Fingerprint for the server %s key is", hostname);
878 silc_say(client, "%s", fingerprint);
879 silc_free(fingerprint);
880 silc_say(client, "Could not load your local copy of the server %s key",
882 if (silc_client_ask_yes_no(client,
883 "Would you like to accept the key anyway (y/n)? "))
885 /* Save the key for future checking */
887 silc_pkcs_save_public_key_data(filename, pk, pk_len,
895 /* Encode the key data */
896 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
898 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
899 silc_say(client, "Received server %s public key", hostname);
900 silc_say(client, "Fingerprint for the server %s key is", hostname);
901 silc_say(client, "%s", fingerprint);
902 silc_free(fingerprint);
903 silc_say(client, "Your local copy of the server %s key is malformed",
905 if (silc_client_ask_yes_no(client,
906 "Would you like to accept the key anyway (y/n)? "))
908 /* Save the key for future checking */
910 silc_pkcs_save_public_key_data(filename, pk, pk_len,
918 if (memcmp(encpk, pk, encpk_len)) {
919 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
920 silc_say(client, "Received server %s public key", hostname);
921 silc_say(client, "Fingerprint for the server %s key is", hostname);
922 silc_say(client, "%s", fingerprint);
923 silc_free(fingerprint);
924 silc_say(client, "Server %s key does not match with your local copy",
926 silc_say(client, "It is possible that the key has expired or changed");
927 silc_say(client, "It is also possible that some one is performing "
928 "man-in-the-middle attack");
930 /* Ask user to verify the key and save it */
931 if (silc_client_ask_yes_no(client,
932 "Would you like to accept the key anyway (y/n)? "))
934 /* Save the key for future checking */
936 silc_pkcs_save_public_key_data(filename, pk, pk_len,
941 silc_say(client, "Will not accept server %s key", hostname);
945 /* Local copy matched */
949 silc_say(client, "Will not accept server %s key", hostname);
954 /* Parse nickname string. The format may be <num>!<nickname>@<server> to
955 support multiple same nicknames. The <num> is the final unifier if same
956 nickname is on same server. Note, this is only local format and server
957 does not know anything about these. */
959 int silc_client_parse_nickname(char *string, char **nickname, char **server,
968 if (strchr(string, '!')) {
969 tlen = strcspn(string, "!");
970 memset(tmp, 0, sizeof(tmp));
971 memcpy(tmp, string, tlen);
976 if (tlen >= strlen(string))
982 if (strchr(string, '@')) {
983 tlen = strcspn(string, "@");
986 *nickname = silc_calloc(tlen + 1, sizeof(char));
987 memcpy(*nickname, string, tlen);
991 *server = silc_calloc(strlen(string) - tlen, sizeof(char));
992 memcpy(*server, string + tlen + 1, strlen(string) - tlen - 1);
996 *nickname = strdup(string);