X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=apps%2Fsilcd%2Fprotocol.c;h=f232c3d84f94cb86384443fe1b7f766a502451d9;hb=413da0f8686910f5e627393157566ae729ca99c4;hp=fe6139302b87d832c230b68eead6880de19a6c9a;hpb=318d79b391bf6288e3e28c840217a7097f3d0392;p=silc.git diff --git a/apps/silcd/protocol.c b/apps/silcd/protocol.c index fe613930..f232c3d8 100644 --- a/apps/silcd/protocol.c +++ b/apps/silcd/protocol.c @@ -2,15 +2,15 @@ protocol.c - Author: Pekka Riikonen + Author: Pekka Riikonen - Copyright (C) 1997 - 2000 Pekka Riikonen + Copyright (C) 1997 - 2003 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -20,59 +20,170 @@ /* * Server side of the protocols. */ -/* - * $Id$ - * $Log$ - * Revision 1.9 2000/07/20 10:17:25 priikone - * Added dynamic protocol registering/unregistering support. The - * patch was provided by cras. - * - * Revision 1.8 2000/07/19 07:08:09 priikone - * Added version detection support to SKE. - * - * Revision 1.7 2000/07/14 06:14:20 priikone - * Put the HMAC keys into the HMAC object instead of having them - * saved elsewhere; now we can use silc_hmac_make insteaad of - * silc_hmac_make_with_key. - * - * Revision 1.6 2000/07/12 05:59:41 priikone - * Major rewrite of ID Cache system. Support added for the new - * ID cache system. Major rewrite of ID List stuff on server. All - * SilcXXXList's are now called SilcXXXEntry's and they are pointers - * by default. A lot rewritten ID list functions. - * - * Revision 1.5 2000/07/10 05:42:14 priikone - * Support for public key encoding functions added. - * - * Revision 1.4 2000/07/07 06:55:59 priikone - * Added SILC style public key support and made server to use - * it at all time. - * - * Revision 1.3 2000/07/06 07:15:31 priikone - * Cleaner code fro password and public key authentication. - * Deprecated old `channel_auth' protocol. - * - * Revision 1.2 2000/07/05 06:13:04 priikone - * Support for SILC style public keys added. - * - * Revision 1.1.1.1 2000/06/27 11:36:56 priikone - * Imported from internal CVS/Added Log headers. - * - * - */ +/* $Id$ */ #include "serverincludes.h" #include "server_internal.h" SILC_TASK_CALLBACK(silc_server_protocol_connection_auth); SILC_TASK_CALLBACK(silc_server_protocol_key_exchange); - -extern char *silc_version_string; +SILC_TASK_CALLBACK(silc_server_protocol_rekey); /* * Key Exhange protocol functions */ +static bool +silc_verify_public_key_internal(SilcServer server, SilcSocketConnection sock, + SilcSocketType conn_type, + unsigned char *pk, SilcUInt32 pk_len, + SilcSKEPKType pk_type) +{ + char file[256], filename[256], *fingerprint; + struct stat st; + + if (pk_type != SILC_SKE_PK_TYPE_SILC) { + SILC_LOG_WARNING(("We don't support %s (%s) port %d public key type %d", + sock->hostname, sock->ip, sock->port, pk_type)); + return FALSE; + } + + /* Accept client keys without verification */ + if (conn_type == SILC_SOCKET_TYPE_CLIENT) { + SILC_LOG_DEBUG(("Accepting client public key without verification")); + return TRUE; + } + + /* XXX For now, accept server keys without verification too. We are + currently always doing mutual authentication so the proof of posession + of the private key is verified, and if server is authenticated in + conn auth protocol with public key we MUST have the key already. */ + return TRUE; + /* Rest is unreachable code! */ + + memset(filename, 0, sizeof(filename)); + memset(file, 0, sizeof(file)); + snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", sock->hostname, + sock->port); + snprintf(filename, sizeof(filename) - 1, SILC_ETCDIR "/serverkeys/%s", + file); + + /* Create serverkeys directory if it doesn't exist. */ + if (stat(SILC_ETCDIR "/serverkeys", &st) < 0) { + /* If dir doesn't exist */ + if (errno == ENOENT) { + if (mkdir(SILC_ETCDIR "/serverkeys", 0755) < 0) { + SILC_LOG_ERROR(("Couldn't create `%s' directory\n", + SILC_ETCDIR "/serverkeys")); + return TRUE; + } + } else { + SILC_LOG_ERROR(("%s\n", strerror(errno))); + return TRUE; + } + } + + /* Take fingerprint of the public key */ + fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); + SILC_LOG_DEBUG(("Received server %s (%s) port %d public key (%s)", + sock->hostname, sock->ip, sock->port, fingerprint)); + silc_free(fingerprint); + + /* Check whether this key already exists */ + if (stat(filename, &st) < 0) { + /* We don't have it, then cache it. */ + SILC_LOG_DEBUG(("New public key from server")); + + silc_pkcs_save_public_key_data(filename, pk, pk_len, + SILC_PKCS_FILE_PEM); + return TRUE; + } else { + /* The key already exists, verify it. */ + SilcPublicKey public_key; + unsigned char *encpk; + SilcUInt32 encpk_len; + + SILC_LOG_DEBUG(("We have the public key saved locally")); + + /* Load the key file */ + if (!silc_pkcs_load_public_key(filename, &public_key, + SILC_PKCS_FILE_PEM)) + if (!silc_pkcs_load_public_key(filename, &public_key, + SILC_PKCS_FILE_BIN)) { + SILC_LOG_WARNING(("Could not load local copy of the %s (%s) port %d " + "server public key", sock->hostname, sock->ip, + sock->port)); + + /* Save the key for future checking */ + unlink(filename); + silc_pkcs_save_public_key_data(filename, pk, pk_len, + SILC_PKCS_FILE_PEM); + return TRUE; + } + + /* Encode the key data */ + encpk = silc_pkcs_public_key_encode(public_key, &encpk_len); + if (!encpk) { + SILC_LOG_WARNING(("Local copy of the server %s (%s) port %d public key " + "is malformed", sock->hostname, sock->ip, sock->port)); + + /* Save the key for future checking */ + unlink(filename); + silc_pkcs_save_public_key_data(filename, pk, pk_len, SILC_PKCS_FILE_PEM); + return TRUE; + } + + if (memcmp(pk, encpk, encpk_len)) { + SILC_LOG_WARNING(("%s (%s) port %d server public key does not match " + "with local copy", sock->hostname, sock->ip, + sock->port)); + SILC_LOG_WARNING(("It is possible that the key has expired or changed")); + SILC_LOG_WARNING(("It is also possible that some one is performing " + "man-in-the-middle attack")); + SILC_LOG_WARNING(("Will not accept the server %s (%s) port %d public " + "key", + sock->hostname, sock->ip, sock->port)); + return FALSE; + } + + /* Local copy matched */ + return TRUE; + } +} + +/* Callback that is called when we have received KE2 payload from + responder. We try to verify the public key now. */ + +static void +silc_server_protocol_ke_verify_key(SilcSKE ske, + unsigned char *pk_data, + SilcUInt32 pk_len, + SilcSKEPKType pk_type, + void *context, + SilcSKEVerifyCbCompletion completion, + void *completion_context) +{ + SilcProtocol protocol = (SilcProtocol)context; + SilcServerKEInternalContext *ctx = + (SilcServerKEInternalContext *)protocol->context; + SilcServer server = (SilcServer)ctx->server; + + SILC_LOG_DEBUG(("Verifying received public key")); + + if (silc_verify_public_key_internal( + server, ctx->sock, + (ctx->responder == FALSE ? + SILC_SOCKET_TYPE_ROUTER: + ctx->sconfig.ref_ptr ? SILC_SOCKET_TYPE_SERVER : + ctx->rconfig.ref_ptr ? SILC_SOCKET_TYPE_ROUTER : + SILC_SOCKET_TYPE_CLIENT), + pk_data, pk_len, pk_type)) + completion(ske, SILC_SKE_STATUS_OK, completion_context); + else + completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY, + completion_context); +} + /* Packet sending callback. This function is provided as packet sending routine to the Key Exchange functions. */ @@ -82,7 +193,7 @@ static void silc_server_protocol_ke_send_packet(SilcSKE ske, void *context) { SilcProtocol protocol = (SilcProtocol)context; - SilcServerKEInternalContext *ctx = + SilcServerKEInternalContext *ctx = (SilcServerKEInternalContext *)protocol->context; SilcServer server = (SilcServer)ctx->server; @@ -93,63 +204,202 @@ static void silc_server_protocol_ke_send_packet(SilcSKE ske, /* Sets the negotiated key material into use for particular connection. */ -static void silc_server_protocol_ke_set_keys(SilcSKE ske, - SilcSocketConnection sock, - SilcSKEKeyMaterial *keymat, - SilcCipher cipher, - SilcPKCS pkcs, - SilcHash hash, - int is_responder) +int silc_server_protocol_ke_set_keys(SilcServer server, + SilcSKE ske, + SilcSocketConnection sock, + SilcSKEKeyMaterial *keymat, + SilcCipher cipher, + SilcPKCS pkcs, + SilcHash hash, + SilcHmac hmac, + SilcSKEDiffieHellmanGroup group, + bool is_responder) { SilcUnknownEntry conn_data; - SilcHash nhash; + SilcIDListData idata; + const char *cname = silc_cipher_get_name(cipher); - SILC_LOG_DEBUG(("Setting new key into use")); + SILC_LOG_DEBUG(("Setting new keys into use")); conn_data = silc_calloc(1, sizeof(*conn_data)); + idata = (SilcIDListData)conn_data; /* Allocate cipher to be used in the communication */ - silc_cipher_alloc(cipher->cipher->name, &conn_data->send_key); - silc_cipher_alloc(cipher->cipher->name, &conn_data->receive_key); - + if (!silc_cipher_alloc((char *)cname, &idata->send_key)) { + silc_free(conn_data); + SILC_LOG_ERROR(("Cannot allocate algorithm: %s", cname)); + return FALSE; + } + if (!silc_cipher_alloc((char *)cname, &idata->receive_key)) { + silc_free(conn_data); + SILC_LOG_ERROR(("Cannot allocate algorithm: %s", cname)); + return FALSE; + } + + if (!silc_hmac_alloc((char *)silc_hmac_get_name(hmac), NULL, + &idata->hmac_send)) { + silc_cipher_free(idata->send_key); + silc_cipher_free(idata->receive_key); + silc_free(conn_data); + SILC_LOG_ERROR(("Cannot allocate algorithm: %s", + silc_hmac_get_name(hmac))); + return FALSE; + } + + if (!silc_hmac_alloc((char *)silc_hmac_get_name(hmac), NULL, + &idata->hmac_receive)) { + silc_cipher_free(idata->send_key); + silc_cipher_free(idata->receive_key); + silc_hmac_free(idata->hmac_send); + silc_free(conn_data); + SILC_LOG_ERROR(("Cannot allocate algorithm: %s", + silc_hmac_get_name(hmac))); + return FALSE; + } + if (is_responder == TRUE) { - conn_data->send_key->cipher->set_key(conn_data->send_key->context, - keymat->receive_enc_key, - keymat->enc_key_len); - conn_data->send_key->set_iv(conn_data->send_key, keymat->receive_iv); - conn_data->receive_key->cipher->set_key(conn_data->receive_key->context, - keymat->send_enc_key, - keymat->enc_key_len); - conn_data->receive_key->set_iv(conn_data->receive_key, keymat->send_iv); - + silc_cipher_set_key(idata->send_key, keymat->receive_enc_key, + keymat->enc_key_len); + silc_cipher_set_iv(idata->send_key, keymat->receive_iv); + silc_cipher_set_key(idata->receive_key, keymat->send_enc_key, + keymat->enc_key_len); + silc_cipher_set_iv(idata->receive_key, keymat->send_iv); + silc_hmac_set_key(idata->hmac_send, keymat->receive_hmac_key, + keymat->hmac_key_len); + silc_hmac_set_key(idata->hmac_receive, keymat->send_hmac_key, + keymat->hmac_key_len); } else { - conn_data->send_key->cipher->set_key(conn_data->send_key->context, - keymat->send_enc_key, - keymat->enc_key_len); - conn_data->send_key->set_iv(conn_data->send_key, keymat->send_iv); - conn_data->receive_key->cipher->set_key(conn_data->receive_key->context, - keymat->receive_enc_key, - keymat->enc_key_len); - conn_data->receive_key->set_iv(conn_data->receive_key, keymat->receive_iv); + silc_cipher_set_key(idata->send_key, keymat->send_enc_key, + keymat->enc_key_len); + silc_cipher_set_iv(idata->send_key, keymat->send_iv); + silc_cipher_set_key(idata->receive_key, keymat->receive_enc_key, + keymat->enc_key_len); + silc_cipher_set_iv(idata->receive_key, keymat->receive_iv); + silc_hmac_set_key(idata->hmac_send, keymat->send_hmac_key, + keymat->hmac_key_len); + silc_hmac_set_key(idata->hmac_receive, keymat->receive_hmac_key, + keymat->hmac_key_len); } - /* Allocate PKCS to be used */ -#if 0 - /* XXX Do we ever need to allocate PKCS for the connection?? - If yes, we need to change KE protocol to get the initiators - public key. */ - silc_pkcs_alloc(pkcs->pkcs->name, &conn_data->pkcs); - conn_data->public_key = silc_pkcs_public_key_alloc(XXX); - silc_pkcs_set_public_key(conn_data->pkcs, ske->ke2_payload->pk_data, - ske->ke2_payload->pk_len); -#endif - - /* Save HMAC key to be used in the communication. */ - silc_hash_alloc(hash->hash->name, &nhash); - silc_hmac_alloc(nhash, &conn_data->hmac); - silc_hmac_set_key(conn_data->hmac, keymat->hmac_key, keymat->hmac_key_len); + idata->rekey = silc_calloc(1, sizeof(*idata->rekey)); + idata->rekey->send_enc_key = silc_memdup(keymat->send_enc_key, + keymat->enc_key_len / 8); + idata->rekey->enc_key_len = keymat->enc_key_len / 8; + + if (ske->prop->flags & SILC_SKE_SP_FLAG_PFS) + idata->rekey->pfs = TRUE; + idata->rekey->ske_group = silc_ske_group_get_number(group); + + /* Save the hash */ + if (!silc_hash_alloc(silc_hash_get_name(hash), &idata->hash)) { + silc_cipher_free(idata->send_key); + silc_cipher_free(idata->receive_key); + silc_hmac_free(idata->hmac_send); + silc_hmac_free(idata->hmac_receive); + silc_free(conn_data); + SILC_LOG_ERROR(("Cannot allocate algorithm: %s", + silc_hash_get_name(hash))); + return FALSE; + } + + /* Save the remote host's public key */ + silc_pkcs_public_key_decode(ske->ke1_payload->pk_data, + ske->ke1_payload->pk_len, &idata->public_key); + if (ske->prop->flags & SILC_SKE_SP_FLAG_MUTUAL) + silc_hash_make(server->sha1hash, ske->ke1_payload->pk_data, + ske->ke1_payload->pk_len, idata->fingerprint); sock->user_data = (void *)conn_data; + + SILC_LOG_INFO(("%s (%s) security properties: %s %s %s %s", + sock->hostname, sock->ip, + silc_cipher_get_name(idata->send_key), + (char *)silc_hmac_get_name(idata->hmac_send), + silc_hash_get_name(idata->hash), + ske->prop->flags & SILC_SKE_SP_FLAG_PFS ? "PFS" : "")); + + return TRUE; +} + +/* Check remote host version string */ + +SilcSKEStatus silc_ske_check_version(SilcSKE ske, unsigned char *version, + SilcUInt32 len, void *context) +{ + SilcUInt32 l_protocol_version = 0, r_protocol_version = 0; + + SILC_LOG_INFO(("%s (%s) is version %s", ske->sock->hostname, + ske->sock->ip, version)); + + if (!silc_parse_version_string(version, &r_protocol_version, NULL, NULL, + NULL, NULL)) { + SILC_LOG_ERROR(("%s (%s) %s is not allowed/supported version", + ske->sock->hostname, ske->sock->ip, version)); + return SILC_SKE_STATUS_BAD_VERSION; + } + + if (!silc_parse_version_string(silc_version_string, + &l_protocol_version, NULL, NULL, + NULL, NULL)) { + SILC_LOG_ERROR(("%s (%s) %s is not allowed/supported version", + ske->sock->hostname, ske->sock->ip, version)); + return SILC_SKE_STATUS_BAD_VERSION; + } + + /* If remote is too new, don't connect */ + if (l_protocol_version < r_protocol_version) { + SILC_LOG_ERROR(("%s (%s) %s is not allowed/supported version", + ske->sock->hostname, ske->sock->ip, version)); + return SILC_SKE_STATUS_BAD_VERSION; + } + + ske->sock->version = r_protocol_version; + + return SILC_SKE_STATUS_OK; +} + +/* Callback that is called by the SKE to indicate that it is safe to + continue the execution of the protocol. This is used only if we are + initiator. Is given as argument to the silc_ske_initiator_finish or + silc_ske_responder_phase_2 functions. This is called due to the fact + that the public key verification process is asynchronous and we must + not continue the protocl until the public key has been verified and + this callback is called. */ + +static void silc_server_protocol_ke_continue(SilcSKE ske, void *context) +{ + SilcProtocol protocol = (SilcProtocol)context; + SilcServerKEInternalContext *ctx = + (SilcServerKEInternalContext *)protocol->context; + SilcServer server = (SilcServer)ctx->server; + + if (ske->status != SILC_SKE_STATUS_OK) { + SILC_LOG_ERROR(("Error (%s) during Key Exchange protocol with %s (%s)", + silc_ske_map_status(ske->status), ctx->sock->hostname, + ctx->sock->ip)); + + protocol->state = SILC_PROTOCOL_STATE_ERROR; + silc_protocol_execute(protocol, server->schedule, 0, 300000); + return; + } + + /* Send Ok to the other end. We will end the protocol as responder + sends Ok to us when we will take the new keys into use. */ + if (ctx->responder == FALSE) { + SILC_LOG_DEBUG(("Ending key exchange protocol")); + silc_ske_end(ctx->ske); + + /* End the protocol on the next round */ + protocol->state = SILC_PROTOCOL_STATE_END; + } + + /* Advance protocol state and call the next state if we are responder. + This happens when this callback was sent to silc_ske_responder_phase_2 + function. */ + if (ctx->responder == TRUE) { + protocol->state++; + silc_protocol_execute(protocol, server->schedule, 0, 100000); + } } /* Performs key exchange protocol. This is used for both initiator @@ -159,17 +409,15 @@ static void silc_server_protocol_ke_set_keys(SilcSKE ske, SILC_TASK_CALLBACK(silc_server_protocol_key_exchange) { SilcProtocol protocol = (SilcProtocol)context; - SilcServerKEInternalContext *ctx = + SilcServerKEInternalContext *ctx = (SilcServerKEInternalContext *)protocol->context; SilcServer server = (SilcServer)ctx->server; - SilcSKEStatus status = 0; - - SILC_LOG_DEBUG(("Start")); + SilcSKEStatus status = SILC_SKE_STATUS_OK; if (protocol->state == SILC_PROTOCOL_STATE_UNKNOWN) protocol->state = SILC_PROTOCOL_STATE_START; - SILC_LOG_DEBUG(("State=%d", protocol->state)); + SILC_LOG_DEBUG(("Current protocol state %d", protocol->state)); switch(protocol->state) { case SILC_PROTOCOL_STATE_START: @@ -180,221 +428,274 @@ SILC_TASK_CALLBACK(silc_server_protocol_key_exchange) SilcSKE ske; /* Allocate Key Exchange object */ - ske = silc_ske_alloc(); - ctx->ske = ske; - + ctx->ske = ske = silc_ske_alloc(server->rng, server); + + silc_ske_set_callbacks(ske, silc_server_protocol_ke_send_packet, NULL, + silc_server_protocol_ke_verify_key, + silc_server_protocol_ke_continue, + silc_ske_check_version, context); + if (ctx->responder == TRUE) { /* Start the key exchange by processing the received security properties packet from initiator. */ + SILC_LOG_DEBUG(("Process security property list (KE)")); status = silc_ske_responder_start(ske, ctx->rng, ctx->sock, silc_version_string, - ctx->packet, NULL, NULL); + ctx->packet->buffer, ctx->flags); } else { SilcSKEStartPayload *start_payload; + SILC_LOG_DEBUG(("Send security property list (KE)")); + /* Assemble security properties. */ - silc_ske_assemble_security_properties(ske, silc_version_string, + silc_ske_assemble_security_properties(ske, ctx->flags, + silc_version_string, &start_payload); /* Start the key exchange by sending our security properties to the remote end. */ status = silc_ske_initiator_start(ske, ctx->rng, ctx->sock, - start_payload, - silc_server_protocol_ke_send_packet, - context); + start_payload); } + /* Return now if the procedure is pending. */ + if (status == SILC_SKE_STATUS_PENDING) + return; + if (status != SILC_SKE_STATUS_OK) { - SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol", - status)); - SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol", - status)); + SILC_LOG_ERROR(("Error (%s) during Key Exchange protocol with %s (%s)", + silc_ske_map_status(status), ctx->sock->hostname, + ctx->sock->ip)); protocol->state = SILC_PROTOCOL_STATE_ERROR; - protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000); + silc_protocol_execute(protocol, server->schedule, 0, 300000); return; } /* Advance protocol state and call the next state if we are responder */ protocol->state++; if (ctx->responder == TRUE) - protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 100000); + silc_protocol_execute(protocol, server->schedule, 0, 100000); } break; case 2: { - /* - * Phase 1 + /* + * Phase 1 */ if (ctx->responder == TRUE) { /* Sends the selected security properties to the initiator. */ - status = - silc_ske_responder_phase_1(ctx->ske, - ctx->ske->start_payload, - silc_server_protocol_ke_send_packet, - context); + SILC_LOG_DEBUG(("Send security property list reply (KE)")); + status = silc_ske_responder_phase_1(ctx->ske); } else { /* Call Phase-1 function. This processes the Key Exchange Start 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, - NULL, NULL); + SILC_LOG_DEBUG(("Process security property list reply (KE)")); + status = silc_ske_initiator_phase_1(ctx->ske, ctx->packet->buffer); } + /* Return now if the procedure is pending. */ + if (status == SILC_SKE_STATUS_PENDING) + return; + if (status != SILC_SKE_STATUS_OK) { - SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol", - status)); - SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol", - status)); + SILC_LOG_ERROR(("Error (%s) during Key Exchange protocol with %s (%s)", + silc_ske_map_status(status), ctx->sock->hostname, + ctx->sock->ip)); protocol->state = SILC_PROTOCOL_STATE_ERROR; - protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000); + silc_protocol_execute(protocol, server->schedule, 0, 300000); return; } /* Advance protocol state and call next state if we are initiator */ protocol->state++; if (ctx->responder == FALSE) - protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 100000); + silc_protocol_execute(protocol, server->schedule, 0, 100000); } break; case 3: { - /* - * Phase 2 + /* + * Phase 2 */ if (ctx->responder == TRUE) { /* Process the received Key Exchange 1 Payload packet from the initiator. This also creates our parts of the Diffie - Hellman algorithm. */ - status = - silc_ske_responder_phase_2(ctx->ske, ctx->packet, NULL, NULL); + Hellman algorithm. The silc_server_protocol_ke_continue + will be called after the public key has been verified. */ + SILC_LOG_DEBUG(("Process KE1 packet")); + status = silc_ske_responder_phase_2(ctx->ske, ctx->packet->buffer); } else { /* Call the Phase-2 function. This creates Diffie Hellman key exchange parameters and sends our public part inside Key Exhange 1 Payload to the responder. */ - status = - silc_ske_initiator_phase_2(ctx->ske, - server->public_key, - silc_server_protocol_ke_send_packet, - context); + SILC_LOG_DEBUG(("Send KE1 packet")); + status = silc_ske_initiator_phase_2(ctx->ske, + server->public_key, + server->private_key, + SILC_SKE_PK_TYPE_SILC); + protocol->state++; } + /* Return now if the procedure is pending. */ + if (status == SILC_SKE_STATUS_PENDING) + return; + if (status != SILC_SKE_STATUS_OK) { - SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol", - status)); - SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol", - status)); + SILC_LOG_ERROR(("Error (%s) during Key Exchange protocol with %s (%s)", + silc_ske_map_status(status), ctx->sock->hostname, + ctx->sock->ip)); protocol->state = SILC_PROTOCOL_STATE_ERROR; - protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000); + silc_protocol_execute(protocol, server->schedule, 0, 300000); return; } - - /* Advance protocol state and call the next state if we are responder */ - protocol->state++; - if (ctx->responder == TRUE) - protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 100000); } break; case 4: { - /* + /* * Finish protocol */ if (ctx->responder == TRUE) { /* This creates the key exchange material and sends our public parts to the initiator inside Key Exchange 2 Payload. */ - status = - silc_ske_responder_finish(ctx->ske, - server->public_key, server->private_key, - SILC_SKE_PK_TYPE_SILC, - silc_server_protocol_ke_send_packet, - context); + SILC_LOG_DEBUG(("Process KE2 packet")); + status = silc_ske_responder_finish(ctx->ske, + server->public_key, + server->private_key, + SILC_SKE_PK_TYPE_SILC); + + /* End the protocol on the next round */ + protocol->state = SILC_PROTOCOL_STATE_END; } else { /* Finish the protocol. This verifies the Key Exchange 2 payload - sent by responder. */ - status = - silc_ske_initiator_finish(ctx->ske, - ctx->packet, NULL, NULL, NULL, NULL); + sent by responder. The silc_server_protocol_ke_continue will + be called after the public key has been verified. */ + SILC_LOG_DEBUG(("Send KE2 packet")); + status = silc_ske_initiator_finish(ctx->ske, ctx->packet->buffer); } + /* Return now if the procedure is pending. */ + if (status == SILC_SKE_STATUS_PENDING) + return; + if (status != SILC_SKE_STATUS_OK) { - SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol", - status)); - SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol", - status)); + SILC_LOG_ERROR(("Error (%s) during Key Exchange protocol with %s (%s)", + silc_ske_map_status(status), ctx->sock->hostname, + ctx->sock->ip)); protocol->state = SILC_PROTOCOL_STATE_ERROR; - protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000); + silc_protocol_execute(protocol, server->schedule, 0, 300000); return; } - - /* Send Ok to the other end. We will end the protocol as responder - sends Ok to us when we will take the new keys into use. */ - if (ctx->responder == FALSE) - silc_ske_end(ctx->ske, silc_server_protocol_ke_send_packet, context); - - /* End the protocol on the next round */ - protocol->state = SILC_PROTOCOL_STATE_END; } break; + case SILC_PROTOCOL_STATE_END: { - /* + /* * End protocol */ SilcSKEKeyMaterial *keymat; + int key_len = silc_cipher_get_key_len(ctx->ske->prop->cipher); + int hash_len = silc_hash_len(ctx->ske->prop->hash); - /* Send Ok to the other end if we are responder. If we are - initiator we have sent this already. */ - if (ctx->responder == TRUE) - silc_ske_end(ctx->ske, silc_server_protocol_ke_send_packet, context); + SILC_LOG_DEBUG(("Process computed key material")); /* Process the key material */ keymat = silc_calloc(1, sizeof(*keymat)); - silc_ske_process_key_material(ctx->ske, 16, (16 * 8), 16, keymat); + status = silc_ske_process_key_material(ctx->ske, 16, key_len, hash_len, + keymat); + if (status != SILC_SKE_STATUS_OK) { + SILC_LOG_ERROR(("Error during Key Exchange protocol: " + "could not process key material")); + + protocol->state = SILC_PROTOCOL_STATE_ERROR; + silc_protocol_execute(protocol, server->schedule, 0, 300000); + silc_ske_free_key_material(keymat); + return; + } + ctx->keymat = keymat; - /* Take the new keys into use. */ - silc_server_protocol_ke_set_keys(ctx->ske, ctx->sock, keymat, - ctx->ske->prop->cipher, - ctx->ske->prop->pkcs, - ctx->ske->prop->hash, - ctx->responder); + /* Send Ok to the other end if we are responder. If we are initiator + we have sent this already. */ + if (ctx->responder == TRUE) { + SILC_LOG_DEBUG(("Ending key exchange protocol")); + silc_ske_end(ctx->ske); + } - /* Unregister the timeout task since the protocol has ended. + /* Unregister the timeout task since the protocol has ended. This was the timeout task to be executed if the protocol is not completed fast enough. */ if (ctx->timeout_task) - silc_task_unregister(server->timeout_queue, ctx->timeout_task); + silc_schedule_task_del(server->schedule, ctx->timeout_task); + + /* Assure that after calling final callback there cannot be pending + executions for this protocol anymore. This just unregisters any + timeout callbacks for this protocol. */ + silc_protocol_cancel(protocol, server->schedule); /* Call the final callback */ if (protocol->final_callback) - protocol->execute_final(server->timeout_queue, 0, protocol, fd); + silc_protocol_execute_final(protocol, server->schedule); else silc_protocol_free(protocol); } break; + case SILC_PROTOCOL_STATE_ERROR: /* * Error occured */ - /* Unregister the timeout task since the protocol has ended. + /* Send abort notification */ + silc_ske_abort(ctx->ske, ctx->ske->status); + + /* Unregister the timeout task since the protocol has ended. + This was the timeout task to be executed if the protocol is + not completed fast enough. */ + if (ctx->timeout_task) + silc_schedule_task_del(server->schedule, ctx->timeout_task); + + /* Assure that after calling final callback there cannot be pending + executions for this protocol anymore. This just unregisters any + timeout callbacks for this protocol. */ + silc_protocol_cancel(protocol, server->schedule); + + /* On error the final callback is always called. */ + if (protocol->final_callback) + silc_protocol_execute_final(protocol, server->schedule); + else + silc_protocol_free(protocol); + break; + + case SILC_PROTOCOL_STATE_FAILURE: + /* + * We have received failure from remote + */ + + /* Unregister the timeout task since the protocol has ended. This was the timeout task to be executed if the protocol is not completed fast enough. */ if (ctx->timeout_task) - silc_task_unregister(server->timeout_queue, ctx->timeout_task); + silc_schedule_task_del(server->schedule, ctx->timeout_task); + + /* Assure that after calling final callback there cannot be pending + executions for this protocol anymore. This just unregisters any + timeout callbacks for this protocol. */ + silc_protocol_cancel(protocol, server->schedule); /* On error the final callback is always called. */ if (protocol->final_callback) - protocol->execute_final(server->timeout_queue, 0, protocol, fd); + silc_protocol_execute_final(protocol, server->schedule); else silc_protocol_free(protocol); break; + case SILC_PROTOCOL_STATE_UNKNOWN: break; } @@ -404,39 +705,33 @@ SILC_TASK_CALLBACK(silc_server_protocol_key_exchange) * Connection Authentication protocol functions */ -/* XXX move these to somehwere else */ - -int silc_server_password_authentication(SilcServer server, char *auth1, - char *auth2) +static int +silc_server_password_authentication(SilcServer server, char *local_auth, + char *remote_auth) { - if (!auth1 || !auth2) + if (!remote_auth || !local_auth || strlen(local_auth) != strlen(remote_auth)) return FALSE; - if (!memcmp(auth1, auth2, strlen(auth1))) + if (!memcmp(remote_auth, local_auth, strlen(local_auth))) return TRUE; return FALSE; } -int silc_server_public_key_authentication(SilcServer server, - char *pkfile, - unsigned char *sign, - unsigned int sign_len, - SilcSKE ske) +static int +silc_server_public_key_authentication(SilcServer server, + SilcPublicKey pub_key, + unsigned char *sign, + SilcUInt32 sign_len, + SilcSKE ske) { - SilcPublicKey pub_key; SilcPKCS pkcs; int len; SilcBuffer auth; - if (!pkfile || !sign) + if (!pub_key || !sign) return FALSE; - /* Load public key from file */ - if (!silc_pkcs_load_public_key(pkfile, &pub_key, SILC_PKCS_FILE_PEM)) - if (!silc_pkcs_load_public_key(pkfile, &pub_key, SILC_PKCS_FILE_BIN)) - return FALSE; - silc_pkcs_alloc(pub_key->name, &pkcs); if (!silc_pkcs_public_key_set(pkcs, pub_key)) { silc_pkcs_free(pkcs); @@ -455,43 +750,133 @@ int silc_server_public_key_authentication(SilcServer server, SILC_STR_END); /* Verify signature */ - if (pkcs->pkcs->verify(pkcs->context, sign, sign_len, - auth->data, auth->len)) - { - silc_pkcs_free(pkcs); - silc_pkcs_public_key_free(pub_key); - silc_buffer_free(auth); - return TRUE; - } + if (silc_pkcs_verify_with_hash(pkcs, ske->prop->hash, sign, sign_len, + auth->data, auth->len)) { + silc_pkcs_free(pkcs); + silc_buffer_free(auth); + return TRUE; + } silc_pkcs_free(pkcs); - silc_pkcs_public_key_free(pub_key); silc_buffer_free(auth); return FALSE; } -/* Performs connection authentication protocol. If responder, we +static int +silc_server_get_public_key_auth(SilcServer server, + unsigned char **auth_data, + SilcUInt32 *auth_data_len, + SilcSKE ske) +{ + int len; + SilcPKCS pkcs; + SilcBuffer auth; + + pkcs = server->pkcs; + + /* Make the authentication data. Protocol says it is HASH plus + KE Start Payload. */ + len = ske->hash_len + ske->start_payload_copy->len; + auth = silc_buffer_alloc(len); + silc_buffer_pull_tail(auth, len); + silc_buffer_format(auth, + SILC_STR_UI_XNSTRING(ske->hash, ske->hash_len), + SILC_STR_UI_XNSTRING(ske->start_payload_copy->data, + ske->start_payload_copy->len), + SILC_STR_END); + + *auth_data = silc_calloc((silc_pkcs_get_key_len(pkcs) / 8) + 1, + sizeof(**auth_data)); + if (silc_pkcs_sign_with_hash(pkcs, ske->prop->hash, auth->data, + auth->len, *auth_data, auth_data_len)) { + silc_buffer_free(auth); + return TRUE; + } + + SILC_LOG_ERROR(("Error computing signature")); + + silc_free(*auth_data); + silc_buffer_free(auth); + return FALSE; +} + +/* Function that actually performs the authentication to the remote. This + supports both passphrase and public key authentication. */ + +static bool +silc_server_get_authentication(SilcServerConnAuthInternalContext *ctx, + char *local_passphrase, + SilcHashTable local_publickeys, + unsigned char *remote_auth, + SilcUInt32 remote_auth_len) +{ + SilcServer server = (SilcServer)ctx->server; + SilcSKE ske = ctx->ske; + bool result = FALSE; + + /* If we don't have authentication data set at all we do not require + authentication at all */ + if (!local_passphrase && (!local_publickeys || + !silc_hash_table_count(local_publickeys))) { + SILC_LOG_DEBUG(("No authentication required")); + return TRUE; + } + + /* If both passphrase and public key is provided then we'll try both of + them and see which one of them authenticates. If only one of them is + set, then try only that. */ + + /* Try first passphrase (as it is faster to check) */ + if (local_passphrase) { + SILC_LOG_DEBUG(("Password authentication")); + result = silc_server_password_authentication(server, local_passphrase, + remote_auth); + } + + /* Try public key authenetication */ + if (!result && local_publickeys) { + SilcPublicKey cached_key; + SilcPublicKey remote_key = + ((SilcIDListData)ctx->sock->user_data)->public_key; + + SILC_LOG_DEBUG(("Public key authentication")); + + /* Find the public key to be used in authentication */ + cached_key = silc_server_find_public_key(server, local_publickeys, + remote_key); + if (!cached_key) + return FALSE; + + result = silc_server_public_key_authentication(server, cached_key, + remote_auth, + remote_auth_len, ske); + } + + SILC_LOG_DEBUG(("Authentication %s", result ? "successful" : "failed")); + + return result; +} + +/* Performs connection authentication protocol. If responder, we authenticate the remote data received. If initiator, we will send authentication data to the remote end. */ SILC_TASK_CALLBACK(silc_server_protocol_connection_auth) { SilcProtocol protocol = (SilcProtocol)context; - SilcServerConnAuthInternalContext *ctx = + SilcServerConnAuthInternalContext *ctx = (SilcServerConnAuthInternalContext *)protocol->context; SilcServer server = (SilcServer)ctx->server; - SILC_LOG_DEBUG(("Start")); - if (protocol->state == SILC_PROTOCOL_STATE_UNKNOWN) protocol->state = SILC_PROTOCOL_STATE_START; - SILC_LOG_DEBUG(("State=%d", protocol->state)); + SILC_LOG_DEBUG(("Current protocol state %d", protocol->state)); switch(protocol->state) { case SILC_PROTOCOL_STATE_START: { - /* + /* * Start protocol. */ @@ -500,52 +885,60 @@ SILC_TASK_CALLBACK(silc_server_protocol_connection_auth) * We are receiving party */ int ret; - unsigned short payload_len; - unsigned short conn_type; - unsigned char *auth_data; + SilcUInt16 payload_len; + SilcUInt16 conn_type; + unsigned char *auth_data = NULL; - SILC_LOG_INFO(("Performing authentication protocol for %s", - ctx->sock->hostname ? ctx->sock->hostname : - ctx->sock->ip)); + SILC_LOG_INFO(("Performing authentication protocol for %s (%s)", + ctx->sock->hostname, ctx->sock->ip)); /* Parse the received authentication data packet. The received payload is Connection Auth Payload. */ - silc_buffer_unformat(ctx->packet, - SILC_STR_UI_SHORT(&payload_len), - SILC_STR_UI_SHORT(&conn_type), - SILC_STR_END); - - if (payload_len != ctx->packet->len) { + ret = silc_buffer_unformat(ctx->packet->buffer, + SILC_STR_UI_SHORT(&payload_len), + SILC_STR_UI_SHORT(&conn_type), + SILC_STR_END); + if (ret == -1) { SILC_LOG_ERROR(("Bad payload in authentication packet")); - SILC_LOG_DEBUG(("Bad payload in authentication packet")); protocol->state = SILC_PROTOCOL_STATE_ERROR; - protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000); + silc_protocol_execute(protocol, server->schedule, 0, 300000); + return; + } + + if (payload_len != ctx->packet->buffer->len) { + SILC_LOG_ERROR(("Bad payload length in authentication packet")); + protocol->state = SILC_PROTOCOL_STATE_ERROR; + silc_protocol_execute(protocol, server->schedule, 0, 300000); return; } - + payload_len -= 4; - - if (conn_type < SILC_SOCKET_TYPE_CLIENT || + + if (conn_type < SILC_SOCKET_TYPE_CLIENT || conn_type > SILC_SOCKET_TYPE_ROUTER) { - SILC_LOG_ERROR(("Bad connection type %d", conn_type)); - SILC_LOG_DEBUG(("Bad connection type %d", conn_type)); + SILC_LOG_ERROR(("Bad connection type (%d) in authentication packet", + conn_type)); protocol->state = SILC_PROTOCOL_STATE_ERROR; - protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000); + silc_protocol_execute(protocol, server->schedule, 0, 300000); return; } - + if (payload_len > 0) { /* Get authentication data */ - silc_buffer_pull(ctx->packet, 4); - silc_buffer_unformat(ctx->packet, - SILC_STR_UI_XNSTRING_ALLOC(&auth_data, - payload_len), - SILC_STR_END); - } else { - auth_data = NULL; + silc_buffer_pull(ctx->packet->buffer, 4); + ret = silc_buffer_unformat(ctx->packet->buffer, + SILC_STR_UI_XNSTRING_ALLOC(&auth_data, + payload_len), + SILC_STR_END); + if (ret == -1) { + SILC_LOG_DEBUG(("Bad payload in authentication payload")); + protocol->state = SILC_PROTOCOL_STATE_ERROR; + silc_protocol_execute(protocol, server->schedule, 0, 300000); + return; + } } - /* + /* * Check the remote connection type and make sure that we have * configured this connection. If we haven't allowed this connection * the authentication must be failed. @@ -555,312 +948,152 @@ SILC_TASK_CALLBACK(silc_server_protocol_connection_auth) /* Remote end is client */ if (conn_type == SILC_SOCKET_TYPE_CLIENT) { - SilcConfigServerSectionClientConnection *client = NULL; - client = - silc_config_server_find_client_conn(server->config, - ctx->sock->ip, - ctx->sock->port); - if (!client) - client = - silc_config_server_find_client_conn(server->config, - ctx->sock->hostname, - ctx->sock->port); - - if (client) { - switch(client->auth_meth) { - case SILC_PROTOCOL_CONN_AUTH_NONE: - /* No authentication required */ - SILC_LOG_DEBUG(("No authentication required")); - break; - - case SILC_PROTOCOL_CONN_AUTH_PASSWORD: - /* Password authentication */ - SILC_LOG_DEBUG(("Password authentication")); - ret = silc_server_password_authentication(server, auth_data, - client->auth_data); - - if (ret) { - memset(auth_data, 0, payload_len); - silc_free(auth_data); - auth_data = NULL; - break; - } + SilcServerConfigClient *client = ctx->cconfig.ref_ptr; + if (client) { + ret = silc_server_get_authentication(ctx, client->passphrase, + client->publickeys, + auth_data, payload_len); + if (!ret) { /* Authentication failed */ SILC_LOG_ERROR(("Authentication failed")); - SILC_LOG_DEBUG(("Authentication failed")); + silc_free(auth_data); protocol->state = SILC_PROTOCOL_STATE_ERROR; - protocol->execute(server->timeout_queue, 0, - protocol, fd, 0, 300000); - return; - break; - - case SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY: - /* Public key authentication */ - SILC_LOG_DEBUG(("Public key authentication")); - ret = silc_server_public_key_authentication(server, - client->auth_data, - auth_data, - payload_len, - ctx->ske); - - if (ret) { - memset(auth_data, 0, payload_len); - silc_free(auth_data); - auth_data = NULL; - break; - } - - SILC_LOG_ERROR(("Authentication failed")); - SILC_LOG_DEBUG(("Authentication failed")); - protocol->state = SILC_PROTOCOL_STATE_ERROR; - protocol->execute(server->timeout_queue, 0, - protocol, fd, 0, 300000); + silc_protocol_execute(protocol, server->schedule, 0, 300000); return; } } else { - SILC_LOG_DEBUG(("No configuration for remote connection")); - SILC_LOG_ERROR(("Remote connection not configured")); + SILC_LOG_ERROR(("Remote client connection not configured")); SILC_LOG_ERROR(("Authentication failed")); - memset(auth_data, 0, payload_len); silc_free(auth_data); - auth_data = NULL; protocol->state = SILC_PROTOCOL_STATE_ERROR; - protocol->execute(server->timeout_queue, 0, - protocol, fd, 0, 300000); + silc_protocol_execute(protocol, server->schedule, + 0, 300000); return; } } - + /* Remote end is server */ if (conn_type == SILC_SOCKET_TYPE_SERVER) { - SilcConfigServerSectionServerConnection *serv = NULL; - serv = - silc_config_server_find_server_conn(server->config, - ctx->sock->ip, - ctx->sock->port); - if (!serv) - serv = - silc_config_server_find_server_conn(server->config, - ctx->sock->hostname, - ctx->sock->port); - + SilcServerConfigServer *serv = ctx->sconfig.ref_ptr; + if (serv) { - switch(serv->auth_meth) { - case SILC_PROTOCOL_CONN_AUTH_NONE: - /* No authentication required */ - SILC_LOG_DEBUG(("No authentication required")); - break; - - case SILC_PROTOCOL_CONN_AUTH_PASSWORD: - /* Password authentication */ - SILC_LOG_DEBUG(("Password authentication")); - ret = silc_server_password_authentication(server, auth_data, - serv->auth_data); - - if (ret) { - memset(auth_data, 0, payload_len); - silc_free(auth_data); - auth_data = NULL; - break; - } - + ret = silc_server_get_authentication(ctx, serv->passphrase, + serv->publickeys, + auth_data, payload_len); + if (!ret) { /* Authentication failed */ SILC_LOG_ERROR(("Authentication failed")); - SILC_LOG_DEBUG(("Authentication failed")); + silc_free(auth_data); protocol->state = SILC_PROTOCOL_STATE_ERROR; - protocol->execute(server->timeout_queue, 0, - protocol, fd, 0, 300000); - return; - break; - - case SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY: - /* Public key authentication */ - SILC_LOG_DEBUG(("Public key authentication")); - ret = silc_server_public_key_authentication(server, - serv->auth_data, - auth_data, - payload_len, - ctx->ske); - - if (ret) { - memset(auth_data, 0, payload_len); - silc_free(auth_data); - auth_data = NULL; - break; - } - - SILC_LOG_ERROR(("Authentication failed")); - SILC_LOG_DEBUG(("Authentication failed")); - protocol->state = SILC_PROTOCOL_STATE_ERROR; - protocol->execute(server->timeout_queue, 0, - protocol, fd, 0, 300000); + silc_protocol_execute(protocol, server->schedule, 0, 300000); return; } } else { - SILC_LOG_DEBUG(("No configuration for remote connection")); - SILC_LOG_ERROR(("Remote connection not configured")); + SILC_LOG_ERROR(("Remote server connection not configured")); SILC_LOG_ERROR(("Authentication failed")); - memset(auth_data, 0, payload_len); - silc_free(auth_data); - auth_data = NULL; protocol->state = SILC_PROTOCOL_STATE_ERROR; - protocol->execute(server->timeout_queue, 0, - protocol, fd, 0, 300000); + silc_protocol_execute(protocol, server->schedule, + 0, 300000); + silc_free(auth_data); return; } } - + /* Remote end is router */ if (conn_type == SILC_SOCKET_TYPE_ROUTER) { - SilcConfigServerSectionServerConnection *serv = NULL; - serv = - silc_config_server_find_router_conn(server->config, - ctx->sock->ip, - ctx->sock->port); - if (!serv) - serv = - silc_config_server_find_router_conn(server->config, - ctx->sock->hostname, - ctx->sock->port); - + SilcServerConfigRouter *serv = ctx->rconfig.ref_ptr; + if (serv) { - switch(serv->auth_meth) { - case SILC_PROTOCOL_CONN_AUTH_NONE: - /* No authentication required */ - SILC_LOG_DEBUG(("No authentication required")); - break; - - case SILC_PROTOCOL_CONN_AUTH_PASSWORD: - /* Password authentication */ - SILC_LOG_DEBUG(("Password authentication")); - ret = silc_server_password_authentication(server, auth_data, - serv->auth_data); - - if (ret) { - memset(auth_data, 0, payload_len); - silc_free(auth_data); - auth_data = NULL; - break; - } - + ret = silc_server_get_authentication(ctx, serv->passphrase, + serv->publickeys, + auth_data, payload_len); + if (!ret) { /* Authentication failed */ SILC_LOG_ERROR(("Authentication failed")); - SILC_LOG_DEBUG(("Authentication failed")); + silc_free(auth_data); protocol->state = SILC_PROTOCOL_STATE_ERROR; - protocol->execute(server->timeout_queue, 0, - protocol, fd, 0, 300000); - return; - break; - - case SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY: - /* Public key authentication */ - SILC_LOG_DEBUG(("Public key authentication")); - ret = silc_server_public_key_authentication(server, - serv->auth_data, - auth_data, - payload_len, - ctx->ske); - - if (ret) { - memset(auth_data, 0, payload_len); - silc_free(auth_data); - auth_data = NULL; - break; - } - - SILC_LOG_ERROR(("Authentication failed")); - SILC_LOG_DEBUG(("Authentication failed")); - protocol->state = SILC_PROTOCOL_STATE_ERROR; - protocol->execute(server->timeout_queue, 0, - protocol, fd, 0, 300000); + silc_protocol_execute(protocol, server->schedule, 0, 300000); return; } } else { - SILC_LOG_DEBUG(("No configuration for remote connection")); - SILC_LOG_ERROR(("Remote connection not configured")); + SILC_LOG_ERROR(("Remote router connection not configured")); SILC_LOG_ERROR(("Authentication failed")); - memset(auth_data, 0, payload_len); silc_free(auth_data); - auth_data = NULL; protocol->state = SILC_PROTOCOL_STATE_ERROR; - protocol->execute(server->timeout_queue, 0, - protocol, fd, 0, 300000); + silc_protocol_execute(protocol, server->schedule, + 0, 300000); return; } } - - if (auth_data) { - memset(auth_data, 0, payload_len); - silc_free(auth_data); - } - + + silc_free(auth_data); + /* Save connection type. This is later used to create the ID for the connection. */ ctx->conn_type = conn_type; - + /* Advance protocol state. */ protocol->state = SILC_PROTOCOL_STATE_END; - protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 0); + silc_protocol_execute(protocol, server->schedule, 0, 0); } else { - /* + /* * We are initiator. We are authenticating ourselves to a * remote server. We will send the authentication data to the - * other end for verify. + * other end for verify. */ SilcBuffer packet; int payload_len = 0; unsigned char *auth_data = NULL; - unsigned int auth_data_len = 0; - + SilcUInt32 auth_data_len = 0; + switch(ctx->auth_meth) { - case SILC_PROTOCOL_CONN_AUTH_NONE: + case SILC_AUTH_NONE: /* No authentication required */ break; - - case SILC_PROTOCOL_CONN_AUTH_PASSWORD: + + case SILC_AUTH_PASSWORD: /* Password authentication */ if (ctx->auth_data && ctx->auth_data_len) { - auth_data = ctx->auth_data; + auth_data = strdup(ctx->auth_data); auth_data_len = ctx->auth_data_len; break; } - - /* No authentication data exits. Ask interactively from user. */ - /* XXX */ - - break; - - case SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY: - /* Public key authentication */ - /* XXX TODO */ break; + + case SILC_AUTH_PUBLIC_KEY: + { + /* Public key authentication */ + silc_server_get_public_key_auth(server, &auth_data, &auth_data_len, + ctx->ske); + break; + } } - + payload_len = 4 + auth_data_len; packet = silc_buffer_alloc(payload_len); silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet)); silc_buffer_format(packet, SILC_STR_UI_SHORT(payload_len), - SILC_STR_UI_SHORT(server->server_type + SILC_STR_UI_SHORT(server->server_type == SILC_SERVER ? SILC_SOCKET_TYPE_SERVER : SILC_SOCKET_TYPE_ROUTER), SILC_STR_UI_XNSTRING(auth_data, auth_data_len), SILC_STR_END); - + /* Send the packet to server */ silc_server_packet_send(server, ctx->sock, - SILC_PACKET_CONNECTION_AUTH, 0, + SILC_PACKET_CONNECTION_AUTH, 0, packet->data, packet->len, TRUE); - + if (auth_data) { memset(auth_data, 0, auth_data_len); silc_free(auth_data); } silc_buffer_free(packet); - + /* Next state is end of protocol */ protocol->state = SILC_PROTOCOL_STATE_END; } @@ -869,23 +1102,31 @@ SILC_TASK_CALLBACK(silc_server_protocol_connection_auth) case SILC_PROTOCOL_STATE_END: { - /* + /* * End protocol */ + unsigned char ok[4]; + + SILC_PUT32_MSB(SILC_AUTH_OK, ok); - /* Succesfully authenticated */ - silc_server_packet_send(server, ctx->sock, SILC_PACKET_SUCCESS, - 0, NULL, 0, TRUE); + /* Authentication successful */ + silc_server_packet_send(server, ctx->sock, SILC_PACKET_SUCCESS, + 0, ok, 4, TRUE); - /* Unregister the timeout task since the protocol has ended. + /* Unregister the timeout task since the protocol has ended. This was the timeout task to be executed if the protocol is not completed fast enough. */ if (ctx->timeout_task) - silc_task_unregister(server->timeout_queue, ctx->timeout_task); + silc_schedule_task_del(server->schedule, ctx->timeout_task); + + /* Assure that after calling final callback there cannot be pending + executions for this protocol anymore. This just unregisters any + timeout callbacks for this protocol. */ + silc_protocol_cancel(protocol, server->schedule); /* Protocol has ended, call the final callback */ if (protocol->final_callback) - protocol->execute_final(server->timeout_queue, 0, protocol, fd); + silc_protocol_execute_final(protocol, server->schedule); else silc_protocol_free(protocol); } @@ -893,31 +1134,489 @@ SILC_TASK_CALLBACK(silc_server_protocol_connection_auth) case SILC_PROTOCOL_STATE_ERROR: { /* - * Error + * Error. Send notify to remote. */ + unsigned char error[4]; /* Authentication failed */ + SILC_PUT32_MSB(SILC_AUTH_FAILED, error); silc_server_packet_send(server, ctx->sock, SILC_PACKET_FAILURE, - 0, NULL, 0, TRUE); + 0, error, 4, TRUE); - /* Unregister the timeout task since the protocol has ended. + /* Unregister the timeout task since the protocol has ended. This was the timeout task to be executed if the protocol is not completed fast enough. */ if (ctx->timeout_task) - silc_task_unregister(server->timeout_queue, ctx->timeout_task); + silc_schedule_task_del(server->schedule, ctx->timeout_task); + + /* Assure that after calling final callback there cannot be pending + executions for this protocol anymore. This just unregisters any + timeout callbacks for this protocol. */ + silc_protocol_cancel(protocol, server->schedule); /* On error the final callback is always called. */ if (protocol->final_callback) - protocol->execute_final(server->timeout_queue, 0, protocol, fd); + silc_protocol_execute_final(protocol, server->schedule); else silc_protocol_free(protocol); } break; + + case SILC_PROTOCOL_STATE_FAILURE: + /* + * We have received failure from remote + */ + + SILC_LOG_ERROR(("Received Authentication Failure")); + + /* Unregister the timeout task since the protocol has ended. + This was the timeout task to be executed if the protocol is + not completed fast enough. */ + if (ctx->timeout_task) + silc_schedule_task_del(server->schedule, ctx->timeout_task); + + /* Assure that after calling final callback there cannot be pending + executions for this protocol anymore. This just unregisters any + timeout callbacks for this protocol. */ + silc_protocol_cancel(protocol, server->schedule); + + /* On error the final callback is always called. */ + if (protocol->final_callback) + silc_protocol_execute_final(protocol, server->schedule); + else + silc_protocol_free(protocol); + break; + case SILC_PROTOCOL_STATE_UNKNOWN: break; } } +/* + * Re-key protocol routines + */ + +/* Actually takes the new keys into use. */ + +static void +silc_server_protocol_rekey_validate(SilcServer server, + SilcServerRekeyInternalContext *ctx, + SilcIDListData idata, + SilcSKEKeyMaterial *keymat, + bool send) +{ + if (ctx->responder == TRUE) { + if (send) { + silc_cipher_set_key(idata->send_key, keymat->receive_enc_key, + keymat->enc_key_len); + silc_cipher_set_iv(idata->send_key, keymat->receive_iv); + silc_hmac_set_key(idata->hmac_send, keymat->receive_hmac_key, + keymat->hmac_key_len); + } else { + silc_cipher_set_key(idata->receive_key, keymat->send_enc_key, + keymat->enc_key_len); + silc_cipher_set_iv(idata->receive_key, keymat->send_iv); + silc_hmac_set_key(idata->hmac_receive, keymat->send_hmac_key, + keymat->hmac_key_len); + } + } else { + if (send) { + silc_cipher_set_key(idata->send_key, keymat->send_enc_key, + keymat->enc_key_len); + silc_cipher_set_iv(idata->send_key, keymat->send_iv); + silc_hmac_set_key(idata->hmac_send, keymat->send_hmac_key, + keymat->hmac_key_len); + } else { + silc_cipher_set_key(idata->receive_key, keymat->receive_enc_key, + keymat->enc_key_len); + silc_cipher_set_iv(idata->receive_key, keymat->receive_iv); + silc_hmac_set_key(idata->hmac_receive, keymat->receive_hmac_key, + keymat->hmac_key_len); + } + } + + /* Save the current sending encryption key */ + if (!send) { + memset(idata->rekey->send_enc_key, 0, idata->rekey->enc_key_len); + silc_free(idata->rekey->send_enc_key); + idata->rekey->send_enc_key = silc_memdup(keymat->send_enc_key, + keymat->enc_key_len / 8); + idata->rekey->enc_key_len = keymat->enc_key_len / 8; + } +} + +/* This function actually re-generates (when not using PFS) the keys and + takes them into use. */ + +void silc_server_protocol_rekey_generate(SilcServer server, + SilcServerRekeyInternalContext *ctx, + bool send) +{ + SilcIDListData idata = (SilcIDListData)ctx->sock->user_data; + SilcSKEKeyMaterial *keymat; + SilcUInt32 key_len = silc_cipher_get_key_len(idata->send_key); + SilcUInt32 hash_len = silc_hash_len(idata->hash); + + SILC_LOG_DEBUG(("Generating new %s session keys (no PFS)", + send ? "sending" : "receiving")); + + /* Generate the new key */ + keymat = silc_calloc(1, sizeof(*keymat)); + silc_ske_process_key_material_data(idata->rekey->send_enc_key, + idata->rekey->enc_key_len, + 16, key_len, hash_len, + idata->hash, keymat); + + /* Set the keys into use */ + silc_server_protocol_rekey_validate(server, ctx, idata, keymat, send); + + silc_ske_free_key_material(keymat); +} + +/* This function actually re-generates (with PFS) the keys and + takes them into use. */ + +void +silc_server_protocol_rekey_generate_pfs(SilcServer server, + SilcServerRekeyInternalContext *ctx, + bool send) +{ + SilcIDListData idata = (SilcIDListData)ctx->sock->user_data; + SilcSKEKeyMaterial *keymat; + SilcUInt32 key_len = silc_cipher_get_key_len(idata->send_key); + SilcUInt32 hash_len = silc_hash_len(idata->hash); + unsigned char *tmpbuf; + SilcUInt32 klen; + + SILC_LOG_DEBUG(("Generating new %s session keys (with PFS)", + send ? "sending" : "receiving")); + + /* Encode KEY to binary data */ + tmpbuf = silc_mp_mp2bin(ctx->ske->KEY, 0, &klen); + + /* Generate the new key */ + keymat = silc_calloc(1, sizeof(*keymat)); + silc_ske_process_key_material_data(tmpbuf, klen, 16, key_len, hash_len, + idata->hash, keymat); + + /* Set the keys into use */ + silc_server_protocol_rekey_validate(server, ctx, idata, keymat, send); + + memset(tmpbuf, 0, klen); + silc_free(tmpbuf); + silc_ske_free_key_material(keymat); +} + +/* Packet sending callback. This function is provided as packet sending + routine to the Key Exchange functions. */ + +static void +silc_server_protocol_rekey_send_packet(SilcSKE ske, + SilcBuffer packet, + SilcPacketType type, + void *context) +{ + SilcProtocol protocol = (SilcProtocol)context; + SilcServerRekeyInternalContext *ctx = + (SilcServerRekeyInternalContext *)protocol->context; + SilcServer server = (SilcServer)ctx->server; + + /* Send the packet immediately */ + silc_server_packet_send(server, ctx->sock, + type, 0, packet->data, packet->len, FALSE); +} + +/* Performs re-key as defined in the SILC protocol specification. */ + +SILC_TASK_CALLBACK(silc_server_protocol_rekey) +{ + SilcProtocol protocol = (SilcProtocol)context; + SilcServerRekeyInternalContext *ctx = + (SilcServerRekeyInternalContext *)protocol->context; + SilcServer server = (SilcServer)ctx->server; + SilcIDListData idata = (SilcIDListData)ctx->sock->user_data; + SilcSKEStatus status; + + if (protocol->state == SILC_PROTOCOL_STATE_UNKNOWN) + protocol->state = SILC_PROTOCOL_STATE_START; + + SILC_LOG_DEBUG(("Current protocol state %d", protocol->state)); + + switch(protocol->state) { + case SILC_PROTOCOL_STATE_START: + { + /* + * Start protocol. + */ + + if (ctx->responder == TRUE) { + /* + * We are receiving party + */ + + if (ctx->pfs == TRUE) { + /* + * Use Perfect Forward Secrecy, ie. negotiate the key material + * using the SKE protocol. + */ + + if (ctx->packet->type != SILC_PACKET_KEY_EXCHANGE_1) { + SILC_LOG_ERROR(("Error during Re-key (R PFS): re-key state is " + "incorrect (received %d, expected %d packet), " + "with %s (%s)", ctx->packet->type, + SILC_PACKET_KEY_EXCHANGE_1, ctx->sock->hostname, + ctx->sock->ip)); + protocol->state = SILC_PROTOCOL_STATE_ERROR; + silc_protocol_execute(protocol, server->schedule, 0, 300000); + return; + } + + ctx->ske = silc_ske_alloc(server->rng, server); + ctx->ske->prop = silc_calloc(1, sizeof(*ctx->ske->prop)); + silc_ske_group_get_by_number(idata->rekey->ske_group, + &ctx->ske->prop->group); + + silc_ske_set_callbacks(ctx->ske, + silc_server_protocol_rekey_send_packet, + NULL, NULL, NULL, silc_ske_check_version, + context); + + status = silc_ske_responder_phase_2(ctx->ske, ctx->packet->buffer); + if (status != SILC_SKE_STATUS_OK) { + SILC_LOG_ERROR(("Error (%s) during Re-key (R PFS), with %s (%s)", + silc_ske_map_status(status), ctx->sock->hostname, + ctx->sock->ip)); + protocol->state = SILC_PROTOCOL_STATE_ERROR; + silc_protocol_execute(protocol, server->schedule, 0, 300000); + return; + } + + /* Advance the protocol state */ + protocol->state++; + silc_protocol_execute(protocol, server->schedule, 0, 0); + } else { + /* + * Do normal and simple re-key. + */ + + /* Send the REKEY_DONE to indicate we will take new keys into use */ + silc_server_packet_send(server, ctx->sock, SILC_PACKET_REKEY_DONE, + 0, NULL, 0, FALSE); + + /* After we send REKEY_DONE we must set the sending encryption + key to the new key since all packets after this packet must + encrypted with the new key. */ + silc_server_protocol_rekey_generate(server, ctx, TRUE); + silc_server_packet_queue_purge(server, ctx->sock); + + /* The protocol ends in next stage. */ + protocol->state = SILC_PROTOCOL_STATE_END; + } + + } else { + /* + * We are the initiator of this protocol + */ + + /* Start the re-key by sending the REKEY packet */ + silc_server_packet_send(server, ctx->sock, SILC_PACKET_REKEY, + 0, NULL, 0, FALSE); + + if (ctx->pfs == TRUE) { + /* + * Use Perfect Forward Secrecy, ie. negotiate the key material + * using the SKE protocol. + */ + ctx->ske = silc_ske_alloc(server->rng, server); + ctx->ske->prop = silc_calloc(1, sizeof(*ctx->ske->prop)); + silc_ske_group_get_by_number(idata->rekey->ske_group, + &ctx->ske->prop->group); + + silc_ske_set_callbacks(ctx->ske, + silc_server_protocol_rekey_send_packet, + NULL, NULL, NULL, silc_ske_check_version, + context); + + status = silc_ske_initiator_phase_2(ctx->ske, NULL, NULL, 0); + if (status != SILC_SKE_STATUS_OK) { + SILC_LOG_ERROR(("Error (%s) during Re-key (I PFS), with %s (%s)", + silc_ske_map_status(status), ctx->sock->hostname, + ctx->sock->ip)); + protocol->state = SILC_PROTOCOL_STATE_ERROR; + silc_protocol_execute(protocol, server->schedule, 0, 300000); + return; + } + + /* Advance the protocol state */ + protocol->state++; + } else { + /* + * Do normal and simple re-key. + */ + + /* Send the REKEY_DONE to indicate we will take new keys into use + now. */ + silc_server_packet_send(server, ctx->sock, SILC_PACKET_REKEY_DONE, + 0, NULL, 0, FALSE); + + /* After we send REKEY_DONE we must set the sending encryption + key to the new key since all packets after this packet must + encrypted with the new key. */ + silc_server_protocol_rekey_generate(server, ctx, TRUE); + silc_server_packet_queue_purge(server, ctx->sock); + + /* The protocol ends in next stage. */ + protocol->state = SILC_PROTOCOL_STATE_END; + } + } + } + break; + + case 2: + /* + * Second state, used only when doing re-key with PFS. + */ + if (ctx->responder == TRUE) { + if (ctx->pfs == TRUE) { + /* + * Send our KE packet to the initiator now that we've processed + * the initiator's KE packet. + */ + status = silc_ske_responder_finish(ctx->ske, NULL, NULL, + SILC_SKE_PK_TYPE_SILC); + if (status != SILC_SKE_STATUS_OK) { + SILC_LOG_ERROR(("Error (%s) during Re-key (R PFS), with %s (%s)", + silc_ske_map_status(status), ctx->sock->hostname, + ctx->sock->ip)); + protocol->state = SILC_PROTOCOL_STATE_ERROR; + silc_protocol_execute(protocol, server->schedule, 0, 300000); + return; + } + } + + } else { + if (ctx->pfs == TRUE) { + /* + * The packet type must be KE packet + */ + if (ctx->packet->type != SILC_PACKET_KEY_EXCHANGE_2) { + SILC_LOG_ERROR(("Error during Re-key (I PFS): re-key state is " + "incorrect (received %d, expected %d packet), " + "with %s (%s)", ctx->packet->type, + SILC_PACKET_KEY_EXCHANGE_2, ctx->sock->hostname, + ctx->sock->ip)); + protocol->state = SILC_PROTOCOL_STATE_ERROR; + silc_protocol_execute(protocol, server->schedule, 0, 300000); + return; + } + + status = silc_ske_initiator_finish(ctx->ske, ctx->packet->buffer); + if (status != SILC_SKE_STATUS_OK) { + SILC_LOG_ERROR(("Error (%s) during Re-key (I PFS), with %s (%s)", + silc_ske_map_status(status), ctx->sock->hostname, + ctx->sock->ip)); + protocol->state = SILC_PROTOCOL_STATE_ERROR; + silc_protocol_execute(protocol, server->schedule, 0, 300000); + return; + } + } + } + + /* Send the REKEY_DONE to indicate we will take new keys into use + now. */ + silc_server_packet_send(server, ctx->sock, SILC_PACKET_REKEY_DONE, + 0, NULL, 0, FALSE); + + /* After we send REKEY_DONE we must set the sending encryption + key to the new key since all packets after this packet must + encrypted with the new key. */ + silc_server_protocol_rekey_generate_pfs(server, ctx, TRUE); + + /* The protocol ends in next stage. */ + protocol->state = SILC_PROTOCOL_STATE_END; + break; + + case SILC_PROTOCOL_STATE_END: + /* + * End protocol + */ + + if (ctx->packet->type != SILC_PACKET_REKEY_DONE) { + SILC_LOG_ERROR(("Error during Re-key (%s PFS): re-key state is " + "incorrect (received %d, expected %d packet), " + "with %s (%s)", ctx->responder ? "R" : "I", + ctx->packet->type, SILC_PACKET_REKEY_DONE, + ctx->sock->hostname, ctx->sock->ip)); + protocol->state = SILC_PROTOCOL_STATE_ERROR; + silc_protocol_execute(protocol, server->schedule, 0, 300000); + return; + } + + /* We received the REKEY_DONE packet and all packets after this is + encrypted with the new key so set the decryption key to the new key */ + if (ctx->pfs == TRUE) + silc_server_protocol_rekey_generate_pfs(server, ctx, FALSE); + else + silc_server_protocol_rekey_generate(server, ctx, FALSE); + + /* Assure that after calling final callback there cannot be pending + executions for this protocol anymore. This just unregisters any + timeout callbacks for this protocol. */ + silc_protocol_cancel(protocol, server->schedule); + + /* Protocol has ended, call the final callback */ + if (protocol->final_callback) + silc_protocol_execute_final(protocol, server->schedule); + else + silc_protocol_free(protocol); + break; + + case SILC_PROTOCOL_STATE_ERROR: + /* + * Error occured + */ + + if (ctx->pfs == TRUE) + /* Send abort notification */ + silc_ske_abort(ctx->ske, ctx->ske->status); + + /* Assure that after calling final callback there cannot be pending + executions for this protocol anymore. This just unregisters any + timeout callbacks for this protocol. */ + silc_protocol_cancel(protocol, server->schedule); + + /* On error the final callback is always called. */ + if (protocol->final_callback) + silc_protocol_execute_final(protocol, server->schedule); + else + silc_protocol_free(protocol); + break; + + case SILC_PROTOCOL_STATE_FAILURE: + /* + * We have received failure from remote + */ + + SILC_LOG_ERROR(("Error during Re-Key: received Failure")); + + /* Assure that after calling final callback there cannot be pending + executions for this protocol anymore. This just unregisters any + timeout callbacks for this protocol. */ + silc_protocol_cancel(protocol, server->schedule); + + /* On error the final callback is always called. */ + if (protocol->final_callback) + silc_protocol_execute_final(protocol, server->schedule); + else + silc_protocol_free(protocol); + break; + + case SILC_PROTOCOL_STATE_UNKNOWN: + break; + } + +} + /* Registers protocols used in server. */ void silc_server_protocols_register(void) @@ -926,6 +1625,10 @@ void silc_server_protocols_register(void) silc_server_protocol_connection_auth); silc_protocol_register(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, silc_server_protocol_key_exchange); + silc_protocol_register(SILC_PROTOCOL_SERVER_REKEY, + silc_server_protocol_rekey); + silc_protocol_register(SILC_PROTOCOL_SERVER_BACKUP, + silc_server_protocol_backup); } /* Unregisters protocols */ @@ -936,4 +1639,8 @@ void silc_server_protocols_unregister(void) silc_server_protocol_connection_auth); silc_protocol_unregister(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, silc_server_protocol_key_exchange); + silc_protocol_unregister(SILC_PROTOCOL_SERVER_REKEY, + silc_server_protocol_rekey); + silc_protocol_unregister(SILC_PROTOCOL_SERVER_BACKUP, + silc_server_protocol_backup); }