From: Pekka Riikonen Date: Fri, 7 Jul 2000 06:53:45 +0000 (+0000) Subject: Added support for server public key verification. X-Git-Tag: SILC.0.1~486 X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=commitdiff_plain;h=a7523d77141e450917a1a93c00a0ff0d4b9761cb Added support for server public key verification. --- diff --git a/apps/silc/clientutil.c b/apps/silc/clientutil.c index 66ba9717..8fd39a0d 100644 --- a/apps/silc/clientutil.c +++ b/apps/silc/clientutil.c @@ -20,6 +20,9 @@ /* * $Id$ * $Log$ + * Revision 1.3 2000/07/07 06:53:45 priikone + * Added support for server public key verification. + * * Revision 1.2 2000/07/05 06:11:00 priikone * Added ~./silc directory checking, autoloading of keys and * tweaked the key pair generation function. @@ -259,6 +262,42 @@ char *silc_client_ask_passphrase(SilcClient client) return ret; } +/* Asks yes/no from user on the input line. Returns TRUE on "yes" and + FALSE on "no". */ + +int silc_client_ask_yes_no(SilcClient client, char *prompt) +{ + char answer[4]; + int ret; + + again: + silc_screen_input_reset(client->screen); + + /* Print prompt */ + wattroff(client->screen->input_win, A_INVIS); + silc_screen_input_print_prompt(client->screen, prompt); + + /* Get string */ + memset(answer, 0, sizeof(answer)); + echo(); + wgetnstr(client->screen->input_win, answer, sizeof(answer)); + if (!strncasecmp(answer, "yes", strlen(answer)) || + !strncasecmp(answer, "y", strlen(answer))) { + ret = TRUE; + } else if (!strncasecmp(answer, "no", strlen(answer)) || + !strncasecmp(answer, "n", strlen(answer))) { + ret = FALSE; + } else { + silc_say(client, "Type yes or no"); + goto again; + } + noecho(); + + silc_screen_input_reset(client->screen); + + return ret; +} + /* Lists supported (builtin) ciphers */ void silc_client_list_ciphers() @@ -516,6 +555,11 @@ New pair of keys will be created. Please, answer to following questions.\n\ if (ret_prv_key) *ret_prv_key = prv_key; + printf("Public key has been save into `%s'.\n", pkfile); + printf("Private key has been saved into `%s'.\n", prvfile); + printf("Press to continue...\n"); + getchar(); + memset(key, 0, sizeof(key_len)); silc_free(key); @@ -536,6 +580,7 @@ New pair of keys will be created. Please, answer to following questions.\n\ int silc_client_check_silc_dir() { char filename[256], file_public_key[256], file_private_key[256]; + char servfilename[256]; char *identifier; struct stat st; struct passwd *pw; @@ -558,6 +603,8 @@ int silc_client_check_silc_dir() /* We'll take home path from /etc/passwd file to be sure. */ snprintf(filename, sizeof(filename) - 1, "%s/.silc/", pw->pw_dir); + snprintf(servfilename, sizeof(servfilename) - 1, "%s/.silc/serverkeys", + pw->pw_dir); /* * Check ~/.silc directory @@ -599,6 +646,28 @@ int silc_client_check_silc_dir() return FALSE; } } + + /* + * Check ~./silc/serverkeys directory + */ + if ((stat(servfilename, &st)) == -1) { + /* If dir doesn't exist */ + if (errno == ENOENT) { + if (pw->pw_uid == geteuid()) { + if ((mkdir(servfilename, 0755)) == -1) { + fprintf(stderr, "Couldn't create `%s' directory\n", servfilename); + return FALSE; + } + } else { + fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n", + servfilename); + return FALSE; + } + } else { + fprintf(stderr, "%s\n", strerror(errno)); + return FALSE; + } + } /* * Check Public and Private keys @@ -610,7 +679,7 @@ int silc_client_check_silc_dir() /* If running SILC first time */ if (firstime) { - fprintf(stdout, "Running SILC for the first time.\n"); + fprintf(stdout, "Running SILC for the first time\n"); silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, SILC_CLIENT_DEF_PKCS_LEN, file_public_key, file_private_key, @@ -726,3 +795,121 @@ int silc_client_load_keys(SilcClient client) return TRUE; } + +/* Verifies received public key. If user decides to trust the key it is + saved as trusted server key for later use. If user does not trust the + key this returns FALSE. */ + +int silc_client_verify_server_key(SilcClient client, + SilcSocketConnection sock, + unsigned char *pk, unsigned int pk_len, + SilcSKEPKType pk_type) +{ + char filename[256]; + char file[256]; + char *hostname; + struct passwd *pw; + struct stat st; + + hostname = sock->hostname ? sock->hostname : sock->ip; + + if (pk_type != SILC_SKE_PK_TYPE_SILC) { + silc_say(client, "We don't support server %s key type", hostname); + return FALSE; + } + + pw = getpwuid(getuid()); + if (!pw) + return FALSE; + + memset(filename, 0, sizeof(filename)); + memset(file, 0, sizeof(file)); + snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname, + sock->port); + snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s", + pw->pw_dir, file); + + /* Check wheter this key already exists */ + if (stat(filename, &st) < 0) { + + silc_say(client, "Received server %s public key", hostname); + /* XXX print fingerprint of the key */ + + /* Ask user to verify the key and save it */ + if (silc_client_ask_yes_no(client, + "Would you like to accept the server key (y/n)? ")) + { + /* Save the key for future checking */ + silc_pkcs_save_public_key_data(filename, pk, pk_len); + return TRUE; + } + } else { + /* The key already exists, verify it. */ + SilcPublicKey public_key; + unsigned char *encpk; + unsigned int encpk_len; + + /* Load the key file */ + if (!silc_pkcs_load_public_key(filename, &public_key)) { + silc_say(client, "Received server %s public key", hostname); + silc_say(client, "Could not load your local copy of the server %s key", + hostname); + if (silc_client_ask_yes_no(client, + "Would you like to accept the server key anyway (y/n)? ")) + { + /* Save the key for future checking */ + unlink(filename); + silc_pkcs_save_public_key_data(filename, pk, pk_len); + return TRUE; + } + + return FALSE; + } + + /* Encode the key data */ + encpk = silc_pkcs_public_key_encode(public_key, &encpk_len); + if (!encpk) { + silc_say(client, "Received server %s public key", hostname); + silc_say(client, "Your local copy of the server %s key is malformed", + hostname); + if (silc_client_ask_yes_no(client, + "Would you like to accept the server key anyway (y/n)? ")) + { + /* Save the key for future checking */ + unlink(filename); + silc_pkcs_save_public_key_data(filename, pk, pk_len); + return TRUE; + } + + return FALSE; + } + + if (memcmp(encpk, pk, encpk_len)) { + silc_say(client, "Received server %s public key", hostname); + silc_say(client, "Server %s key does not match with your local copy", + hostname); + silc_say(client, "It is possible that the key has expired or changed"); + silc_say(client, "It is also possible that some one is performing " + "man-in-the-middle attack"); + + /* Ask user to verify the key and save it */ + if (silc_client_ask_yes_no(client, + "Would you like to accept the server key anyway (y/n)? ")) + { + /* Save the key for future checking */ + unlink(filename); + silc_pkcs_save_public_key_data(filename, pk, pk_len); + return TRUE; + } + + silc_say(client, "Will not accept server %s key", hostname); + return FALSE; + } + + /* Local copy matched */ + return TRUE; + } + + silc_say(client, "Will not accept server %s key", hostname); + return FALSE; +} diff --git a/apps/silc/clientutil.h b/apps/silc/clientutil.h index 298c3bda..1ad7286a 100644 --- a/apps/silc/clientutil.h +++ b/apps/silc/clientutil.h @@ -30,6 +30,7 @@ char *silc_get_username(); char *silc_get_real_name(); int silc_client_time_til_next_min(); char *silc_client_ask_passphrase(SilcClient client); +int silc_client_ask_yes_no(SilcClient client, char *prompt); char *silc_client_get_input(const char *prompt); char *silc_client_get_passphrase(const char *prompt); void silc_client_list_ciphers(); @@ -43,5 +44,9 @@ int silc_client_create_key_pair(char *pkcs_name, int bits, SilcPrivateKey *ret_prv_key); int silc_client_check_silc_dir(); int silc_client_load_keys(SilcClient client); +int silc_client_verify_server_key(SilcClient client, + SilcSocketConnection sock, + unsigned char *pk, unsigned int pk_len, + SilcSKEPKType pk_type); #endif diff --git a/apps/silc/protocol.c b/apps/silc/protocol.c index 5cc3c48a..541d26db 100644 --- a/apps/silc/protocol.c +++ b/apps/silc/protocol.c @@ -23,6 +23,9 @@ /* * $Id$ * $Log$ + * Revision 1.4 2000/07/07 06:53:45 priikone + * Added support for server public key verification. + * * Revision 1.3 2000/07/06 07:14:16 priikone * Deprecated old `channel_auth' protocol. * @@ -55,6 +58,8 @@ const SilcProtocolObject silc_protocol_list[] = * Key Exhange protocol functions */ +/* Function that is called when SKE protocol sends packets to network. */ + static void silc_client_protocol_ke_send_packet(SilcSKE ske, SilcBuffer packet, SilcPacketType type, @@ -71,20 +76,15 @@ static void silc_client_protocol_ke_send_packet(SilcSKE ske, } -static void silc_client_protocol_ke_phase1_cb(SilcSKE ske, - void *context) -{ - SilcProtocol protocol = (SilcProtocol)context; - SilcClientKEInternalContext *ctx = - (SilcClientKEInternalContext *)protocol->context; - SilcClient client = (SilcClient)ctx->client; - - SILC_LOG_DEBUG(("Start")); +/* Callback that is called when we have received KE2 payload from + responder. We try to verify the public key now. */ -} - -static void silc_client_protocol_ke_finish_cb(SilcSKE ske, - void *context) +static SilcSKEStatus +silc_client_protocol_ke_verify_key(SilcSKE ske, + unsigned char *pk_data, + unsigned int pk_len, + SilcSKEPKType pk_type, + void *context) { SilcProtocol protocol = (SilcProtocol)context; SilcClientKEInternalContext *ctx = @@ -93,6 +93,11 @@ static void silc_client_protocol_ke_finish_cb(SilcSKE ske, SILC_LOG_DEBUG(("Start")); + if (!silc_client_verify_server_key(client, ctx->sock, + pk_data, pk_len, pk_type)) + return SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY; + + return SILC_SKE_STATUS_OK; } /* Sets the negotiated key material into use for particular connection. */ @@ -226,11 +231,7 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange) paylaod reply we just got from the responder. The callback function will receive the processed payload where we will save it. */ - status = - silc_ske_initiator_phase_1(ctx->ske, - ctx->packet, - silc_client_protocol_ke_phase1_cb, - context); + status = silc_ske_initiator_phase_1(ctx->ske, ctx->packet, NULL, NULL); } switch(status) { @@ -281,6 +282,7 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange) * Finish protocol */ if (ctx->responder == TRUE) { + status = 0; #if 0 status = silc_ske_responder_phase_2(ctx->ske, @@ -291,16 +293,15 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange) } else { /* Finish the protocol. This verifies the Key Exchange 2 payload sent by responder. */ - status = - silc_ske_initiator_finish(ctx->ske, - ctx->packet, - silc_client_protocol_ke_finish_cb, - context); + status = silc_ske_initiator_finish(ctx->ske, ctx->packet, + silc_client_protocol_ke_verify_key, + context, NULL, NULL); } - switch(status) { - default: - break; + if (status != SILC_SKE_STATUS_OK) { + protocol->state = SILC_PROTOCOL_STATE_ERROR; + protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 0); + return; } /* Send Ok to the other end. We will end the protocol as server @@ -338,7 +339,10 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange) case SILC_PROTOCOL_STATE_ERROR: /* On error the final callback is always called. */ - /* protocol->final_callback(pptr, context);*/ + if (protocol->final_callback) + protocol->execute_final(client->timeout_queue, 0, protocol, fd); + else + silc_protocol_free(protocol); break; case SILC_PROTOCOL_STATE_UNKNOWN: break;