/*
* $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.
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()
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 <Enter> to continue...\n");
+ getchar();
+
memset(key, 0, sizeof(key_len));
silc_free(key);
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;
/* 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
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
/* 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,
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;
+}
/*
* $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.
*
* 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,
}
-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 =
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. */
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) {
* Finish protocol
*/
if (ctx->responder == TRUE) {
+ status = 0;
#if 0
status =
silc_ske_responder_phase_2(ctx->ske,
} 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
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;