/* protocol.c Author: Pekka Riikonen Copyright (C) 1997 - 2001 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 GNU General Public License for more details. */ /* * Server side of the protocols. */ /* $Id$ */ #include "serverincludes.h" #include "server_internal.h" SILC_TASK_CALLBACK(silc_server_protocol_connection_auth); SILC_TASK_CALLBACK(silc_server_protocol_key_exchange); SILC_TASK_CALLBACK(silc_server_protocol_rekey); extern char *silc_version_string; /* * Key Exhange protocol functions */ /* Packet sending callback. This function is provided as packet sending routine to the Key Exchange functions. */ static void silc_server_protocol_ke_send_packet(SilcSKE ske, SilcBuffer packet, SilcPacketType type, void *context) { SilcProtocol protocol = (SilcProtocol)context; SilcServerKEInternalContext *ctx = (SilcServerKEInternalContext *)protocol->context; SilcServer server = (SilcServer)ctx->server; /* Send the packet immediately */ silc_server_packet_send(server, ske->sock, type, 0, packet->data, packet->len, TRUE); } /* Sets the negotiated key material into use for particular connection. */ int silc_server_protocol_ke_set_keys(SilcSKE ske, SilcSocketConnection sock, SilcSKEKeyMaterial *keymat, SilcCipher cipher, SilcPKCS pkcs, SilcHash hash, SilcHmac hmac, SilcSKEDiffieHellmanGroup group, bool is_responder) { SilcUnknownEntry conn_data; SilcIDListData idata; SILC_LOG_DEBUG(("Setting new key into use")); conn_data = silc_calloc(1, sizeof(*conn_data)); idata = (SilcIDListData)conn_data; /* Allocate cipher to be used in the communication */ if (!silc_cipher_alloc(cipher->cipher->name, &idata->send_key)) { silc_free(conn_data); return FALSE; } if (!silc_cipher_alloc(cipher->cipher->name, &idata->receive_key)) { silc_free(conn_data); return FALSE; } if (is_responder == TRUE) { 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); } else { 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); } idata->rekey = silc_calloc(1, sizeof(*idata->rekey)); idata->rekey->send_enc_key = silc_calloc(keymat->enc_key_len / 8, sizeof(*idata->rekey->send_enc_key)); memcpy(idata->rekey->send_enc_key, keymat->send_enc_key, keymat->enc_key_len / 8); idata->rekey->enc_key_len = keymat->enc_key_len / 8; if (ske->start_payload->flags & SILC_SKE_SP_FLAG_PFS) idata->rekey->pfs = TRUE; idata->rekey->ske_group = silc_ske_group_get_number(group); /* 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); /* Save the hash */ if (!silc_hash_alloc(hash->hash->name, &idata->hash)) { silc_cipher_free(idata->send_key); silc_cipher_free(idata->receive_key); silc_free(conn_data); return FALSE; } /* Save HMAC key to be used in the communication. */ if (!silc_hmac_alloc(hmac->hmac->name, NULL, &idata->hmac_send)) { silc_cipher_free(idata->send_key); silc_cipher_free(idata->receive_key); silc_hash_free(idata->hash); silc_free(conn_data); return FALSE; } silc_hmac_set_key(idata->hmac_send, keymat->hmac_key, keymat->hmac_key_len); idata->hmac_receive = idata->hmac_send; sock->user_data = (void *)conn_data; return TRUE; } /* Check remote host version string */ SilcSKEStatus silc_ske_check_version(SilcSKE ske, unsigned char *version, uint32 len) { SilcSKEStatus status = SILC_SKE_STATUS_OK; char *cp; int maj = 0, min = 0, build = 0, maj2 = 0, min2 = 0, build2 = 0; SILC_LOG_INFO(("%s (%s) is version %s", ske->sock->hostname, ske->sock->ip, version)); /* Check for initial version string */ if (!strstr(version, "SILC-1.0-")) status = SILC_SKE_STATUS_BAD_VERSION; /* Check software version */ cp = version + 9; if (!cp) status = SILC_SKE_STATUS_BAD_VERSION; maj = atoi(cp); cp = strchr(cp, '.'); if (cp) { min = atoi(cp + 1); cp++; } if (cp) { cp = strchr(cp, '.'); if (cp) build = atoi(cp + 1); } cp = silc_version_string + 9; if (!cp) status = SILC_SKE_STATUS_BAD_VERSION; maj2 = atoi(cp); cp = strchr(cp, '.'); if (cp) { min2 = atoi(cp + 1); cp++; } if (cp) { cp = strchr(cp, '.'); if (cp) build2 = atoi(cp + 1); } if (maj != maj2) status = SILC_SKE_STATUS_BAD_VERSION; if (min < min2) status = SILC_SKE_STATUS_BAD_VERSION; return status; } /* 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 function. 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_finish(SilcSKE ske, void *context) { SilcProtocol protocol = (SilcProtocol)context; SilcServerKEInternalContext *ctx = (SilcServerKEInternalContext *)protocol->context; SilcServer server = (SilcServer)ctx->server; SILC_LOG_DEBUG(("Start")); if (ske->status != SILC_SKE_STATUS_OK) { SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol", ske->status)); SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol", ske->status)); protocol->state = SILC_PROTOCOL_STATE_ERROR; silc_protocol_execute(protocol, server->timeout_queue, 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; } /* Performs key exchange protocol. This is used for both initiator and responder key exchange. This is performed always when accepting new connection to the server. This may be called recursively. */ SILC_TASK_CALLBACK(silc_server_protocol_key_exchange) { SilcProtocol protocol = (SilcProtocol)context; SilcServerKEInternalContext *ctx = (SilcServerKEInternalContext *)protocol->context; SilcServer server = (SilcServer)ctx->server; SilcSKEStatus status = SILC_SKE_STATUS_OK; SILC_LOG_DEBUG(("Start")); if (protocol->state == SILC_PROTOCOL_STATE_UNKNOWN) protocol->state = SILC_PROTOCOL_STATE_START; SILC_LOG_DEBUG(("State=%d", protocol->state)); switch(protocol->state) { case SILC_PROTOCOL_STATE_START: { /* * Start protocol */ SilcSKE ske; /* Allocate Key Exchange object */ ske = silc_ske_alloc(); ctx->ske = ske; ske->rng = server->rng; if (ctx->responder == TRUE) { /* Start the key exchange by processing the received security properties packet from initiator. */ status = silc_ske_responder_start(ske, ctx->rng, ctx->sock, silc_version_string, ctx->packet->buffer, FALSE, NULL, NULL); } else { SilcSKEStartPayload *start_payload; /* Assemble security properties. */ silc_ske_assemble_security_properties(ske, SILC_SKE_SP_FLAG_NONE, 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); } /* 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)); protocol->state = SILC_PROTOCOL_STATE_ERROR; silc_protocol_execute(protocol, server->timeout_queue, 0, 300000); return; } /* Advance protocol state and call the next state if we are responder */ protocol->state++; if (ctx->responder == TRUE) silc_protocol_execute(protocol, server->timeout_queue, 0, 100000); } break; case 2: { /* * 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); } 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->buffer, NULL, NULL); } /* 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)); protocol->state = SILC_PROTOCOL_STATE_ERROR; silc_protocol_execute(protocol, server->timeout_queue, 0, 300000); return; } /* Advance protocol state and call next state if we are initiator */ protocol->state++; if (ctx->responder == FALSE) silc_protocol_execute(protocol, server->timeout_queue, 0, 100000); } break; case 3: { /* * 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. */ /* XXX TODO: If mutual authentication flag is set then the verify_key callback should be set to verify the remote ends public key!! */ /* XXX TODO: when the verify_key is set then the `callback' must be set as well as the verify_key is asynchronous (take a look to silc_ske_initiator_finish for example. */ status = silc_ske_responder_phase_2(ctx->ske, ctx->packet->buffer, NULL, NULL, NULL, NULL); } 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, server->private_key, silc_server_protocol_ke_send_packet, context); } /* 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)); protocol->state = SILC_PROTOCOL_STATE_ERROR; silc_protocol_execute(protocol, server->timeout_queue, 0, 300000); return; } /* Advance protocol state and call the next state if we are responder */ protocol->state++; if (ctx->responder == TRUE) silc_protocol_execute(protocol, server->timeout_queue, 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); /* 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. */ /* XXX TODO: the verify_key callback is not set!!! */ status = silc_ske_initiator_finish(ctx->ske, ctx->packet->buffer, NULL, NULL, silc_server_protocol_ke_finish, context); } /* 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)); protocol->state = SILC_PROTOCOL_STATE_ERROR; silc_protocol_execute(protocol, server->timeout_queue, 0, 300000); return; } } 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 = ctx->ske->prop->hash->hash->hash_len; /* Process the key material */ keymat = silc_calloc(1, sizeof(*keymat)); status = silc_ske_process_key_material(ctx->ske, 16, key_len, hash_len, keymat); if (status != SILC_SKE_STATUS_OK) { protocol->state = SILC_PROTOCOL_STATE_ERROR; silc_protocol_execute(protocol, server->timeout_queue, 0, 300000); silc_ske_free_key_material(keymat); return; } ctx->keymat = keymat; /* 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); /* 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); /* Call the final callback */ if (protocol->final_callback) silc_protocol_execute_final(protocol, server->timeout_queue); else silc_protocol_free(protocol); } break; case SILC_PROTOCOL_STATE_ERROR: /* * Error occured */ /* Send abort notification */ silc_ske_abort(ctx->ske, ctx->ske->status, silc_server_protocol_ke_send_packet, context); /* 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); /* On error the final callback is always called. */ if (protocol->final_callback) silc_protocol_execute_final(protocol, server->timeout_queue); 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); /* On error the final callback is always called. */ if (protocol->final_callback) silc_protocol_execute_final(protocol, server->timeout_queue); else silc_protocol_free(protocol); break; case SILC_PROTOCOL_STATE_UNKNOWN: break; } } /* * Connection Authentication protocol functions */ static int silc_server_password_authentication(SilcServer server, char *auth1, char *auth2) { if (!auth1 || !auth2) return FALSE; if (!memcmp(auth1, auth2, strlen(auth1))) return TRUE; return FALSE; } static int silc_server_public_key_authentication(SilcServer server, SilcPublicKey pub_key, unsigned char *sign, uint32 sign_len, SilcSKE ske) { SilcPKCS pkcs; int len; SilcBuffer auth; if (!pub_key || !sign) return FALSE; silc_pkcs_alloc(pub_key->name, &pkcs); if (!silc_pkcs_public_key_set(pkcs, pub_key)) { silc_pkcs_free(pkcs); return FALSE; } /* 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); /* Verify signature */ if (silc_pkcs_verify(pkcs, sign, sign_len, auth->data, auth->len)) { silc_pkcs_free(pkcs); silc_buffer_free(auth); return TRUE; } silc_pkcs_free(pkcs); silc_buffer_free(auth); return FALSE; } static int silc_server_get_public_key_auth(SilcServer server, SilcPublicKey pub_key, unsigned char *auth_data, uint32 *auth_data_len, SilcSKE ske) { int len; SilcPKCS pkcs; SilcBuffer auth; if (!pub_key) return FALSE; silc_pkcs_alloc(pub_key->name, &pkcs); if (!silc_pkcs_public_key_set(pkcs, pub_key)) { silc_pkcs_free(pkcs); return FALSE; } /* 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); if (silc_pkcs_sign(pkcs, auth->data, auth->len, auth_data, auth_data_len)) { silc_pkcs_free(pkcs); silc_buffer_free(auth); return TRUE; } silc_pkcs_free(pkcs); silc_buffer_free(auth); return FALSE; } /* 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 *)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)); switch(protocol->state) { case SILC_PROTOCOL_STATE_START: { /* * Start protocol. */ if (ctx->responder == TRUE) { /* * We are receiving party */ int ret; uint16 payload_len; uint16 conn_type; unsigned char *auth_data = NULL; 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. */ 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_DEBUG(("Bad payload in authentication packet")); protocol->state = SILC_PROTOCOL_STATE_ERROR; silc_protocol_execute(protocol, server->timeout_queue, 0, 300000); return; } if (payload_len != ctx->packet->buffer->len) { SILC_LOG_DEBUG(("Bad payload in authentication packet")); protocol->state = SILC_PROTOCOL_STATE_ERROR; silc_protocol_execute(protocol, server->timeout_queue, 0, 300000); return; } payload_len -= 4; if (conn_type < SILC_SOCKET_TYPE_CLIENT || conn_type > SILC_SOCKET_TYPE_ROUTER) { SILC_LOG_ERROR(("Bad connection type %d", conn_type)); protocol->state = SILC_PROTOCOL_STATE_ERROR; silc_protocol_execute(protocol, server->timeout_queue, 0, 300000); return; } if (payload_len > 0) { /* Get authentication data */ 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 packet")); protocol->state = SILC_PROTOCOL_STATE_ERROR; silc_protocol_execute(protocol, server->timeout_queue, 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. */ SILC_LOG_DEBUG(("Remote connection type %d", conn_type)); /* Remote end is client */ if (conn_type == SILC_SOCKET_TYPE_CLIENT) { SilcServerConfigSectionClientConnection *client = ctx->cconfig; if (client) { switch(client->auth_meth) { case SILC_AUTH_NONE: /* No authentication required */ SILC_LOG_DEBUG(("No authentication required")); break; case SILC_AUTH_PASSWORD: /* Password authentication */ SILC_LOG_DEBUG(("Password authentication")); ret = silc_server_password_authentication(server, auth_data, client->auth_data); if (ret) break; /* Authentication failed */ SILC_LOG_ERROR(("Authentication failed")); SILC_LOG_DEBUG(("Authentication failed")); silc_free(auth_data); protocol->state = SILC_PROTOCOL_STATE_ERROR; silc_protocol_execute(protocol, server->timeout_queue, 0, 300000); return; break; case SILC_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) break; SILC_LOG_ERROR(("Authentication failed")); SILC_LOG_DEBUG(("Authentication failed")); silc_free(auth_data); protocol->state = SILC_PROTOCOL_STATE_ERROR; silc_protocol_execute(protocol, server->timeout_queue, 0, 300000); return; } } else { SILC_LOG_DEBUG(("No configuration for remote connection")); SILC_LOG_ERROR(("Remote connection not configured")); SILC_LOG_ERROR(("Authentication failed")); silc_free(auth_data); protocol->state = SILC_PROTOCOL_STATE_ERROR; silc_protocol_execute(protocol, server->timeout_queue, 0, 300000); return; } } /* Remote end is server */ if (conn_type == SILC_SOCKET_TYPE_SERVER) { SilcServerConfigSectionServerConnection *serv = ctx->sconfig; if (serv) { switch(serv->auth_meth) { case SILC_AUTH_NONE: /* No authentication required */ SILC_LOG_DEBUG(("No authentication required")); break; case SILC_AUTH_PASSWORD: /* Password authentication */ SILC_LOG_DEBUG(("Password authentication")); ret = silc_server_password_authentication(server, auth_data, serv->auth_data); if (ret) break; /* Authentication failed */ SILC_LOG_ERROR(("Authentication failed")); SILC_LOG_DEBUG(("Authentication failed")); silc_free(auth_data); protocol->state = SILC_PROTOCOL_STATE_ERROR; silc_protocol_execute(protocol, server->timeout_queue, 0, 300000); return; break; case SILC_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) break; SILC_LOG_ERROR(("Authentication failed")); SILC_LOG_DEBUG(("Authentication failed")); silc_free(auth_data); protocol->state = SILC_PROTOCOL_STATE_ERROR; silc_protocol_execute(protocol, server->timeout_queue, 0, 300000); return; } } else { SILC_LOG_DEBUG(("No configuration for remote connection")); SILC_LOG_ERROR(("Remote connection not configured")); SILC_LOG_ERROR(("Authentication failed")); protocol->state = SILC_PROTOCOL_STATE_ERROR; silc_protocol_execute(protocol, server->timeout_queue, 0, 300000); silc_free(auth_data); return; } } /* Remote end is router */ if (conn_type == SILC_SOCKET_TYPE_ROUTER) { SilcServerConfigSectionServerConnection *serv = ctx->rconfig; if (serv) { switch(serv->auth_meth) { case SILC_AUTH_NONE: /* No authentication required */ SILC_LOG_DEBUG(("No authentication required")); break; case SILC_AUTH_PASSWORD: /* Password authentication */ SILC_LOG_DEBUG(("Password authentication")); ret = silc_server_password_authentication(server, auth_data, serv->auth_data); if (ret) break; /* Authentication failed */ SILC_LOG_ERROR(("Authentication failed")); SILC_LOG_DEBUG(("Authentication failed")); silc_free(auth_data); protocol->state = SILC_PROTOCOL_STATE_ERROR; silc_protocol_execute(protocol, server->timeout_queue, 0, 300000); return; break; case SILC_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) break; SILC_LOG_ERROR(("Authentication failed")); SILC_LOG_DEBUG(("Authentication failed")); silc_free(auth_data); protocol->state = SILC_PROTOCOL_STATE_ERROR; silc_protocol_execute(protocol, server->timeout_queue, 0, 300000); return; } } else { SILC_LOG_DEBUG(("No configuration for remote connection")); SILC_LOG_ERROR(("Remote connection not configured")); SILC_LOG_ERROR(("Authentication failed")); silc_free(auth_data); protocol->state = SILC_PROTOCOL_STATE_ERROR; silc_protocol_execute(protocol, server->timeout_queue, 0, 300000); return; } } 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; silc_protocol_execute(protocol, server->timeout_queue, 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. */ SilcBuffer packet; int payload_len = 0; unsigned char *auth_data = NULL; uint32 auth_data_len = 0; switch(ctx->auth_meth) { case SILC_AUTH_NONE: /* No authentication required */ break; case SILC_AUTH_PASSWORD: /* Password authentication */ if (ctx->auth_data && ctx->auth_data_len) { auth_data = strdup(ctx->auth_data); auth_data_len = ctx->auth_data_len; break; } break; case SILC_AUTH_PUBLIC_KEY: { unsigned char sign[1024]; /* Public key authentication */ silc_server_get_public_key_auth(server, ctx->auth_data, sign, &auth_data_len, ctx->ske); auth_data = silc_calloc(auth_data_len, sizeof(*auth_data)); memcpy(auth_data, sign, auth_data_len); 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_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, 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; } } break; case SILC_PROTOCOL_STATE_END: { /* * End protocol */ unsigned char ok[4]; SILC_PUT32_MSB(SILC_AUTH_OK, ok); /* 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. 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); /* Protocol has ended, call the final callback */ if (protocol->final_callback) silc_protocol_execute_final(protocol, server->timeout_queue); else silc_protocol_free(protocol); } break; case SILC_PROTOCOL_STATE_ERROR: { /* * Error. Send notify to remote. */ unsigned char error[4]; SILC_PUT32_MSB(SILC_AUTH_FAILED, error); /* Authentication failed */ silc_server_packet_send(server, ctx->sock, SILC_PACKET_FAILURE, 0, error, 4, TRUE); /* 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); /* On error the final callback is always called. */ if (protocol->final_callback) silc_protocol_execute_final(protocol, server->timeout_queue); 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); /* On error the final callback is always called. */ if (protocol->final_callback) silc_protocol_execute_final(protocol, server->timeout_queue); 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); } 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); } } 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); } 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); } } if (send) { silc_hmac_alloc(idata->hmac_send->hmac->name, NULL, &idata->hmac_send); silc_hmac_set_key(idata->hmac_send, keymat->hmac_key, keymat->hmac_key_len); } else { silc_hmac_free(idata->hmac_receive); idata->hmac_receive = idata->hmac_send; } /* 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_calloc(keymat->enc_key_len / 8, sizeof(*idata->rekey->send_enc_key)); memcpy(idata->rekey->send_enc_key, 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; uint32 key_len = silc_cipher_get_key_len(idata->send_key); uint32 hash_len = idata->hash->hash->hash_len; 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; uint32 key_len = silc_cipher_get_key_len(idata->send_key); uint32 hash_len = idata->hash->hash->hash_len; unsigned char *tmpbuf; uint32 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; SILC_LOG_DEBUG(("Start")); if (protocol->state == SILC_PROTOCOL_STATE_UNKNOWN) protocol->state = SILC_PROTOCOL_STATE_START; SILC_LOG_DEBUG(("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) { /* Error in protocol */ protocol->state = SILC_PROTOCOL_STATE_ERROR; silc_protocol_execute(protocol, server->timeout_queue, 0, 300000); } ctx->ske = silc_ske_alloc(); ctx->ske->rng = server->rng; ctx->ske->prop = silc_calloc(1, sizeof(*ctx->ske->prop)); silc_ske_get_group_by_number(idata->rekey->ske_group, &ctx->ske->prop->group); status = silc_ske_responder_phase_2(ctx->ske, ctx->packet->buffer, NULL, NULL, NULL, NULL); if (status != SILC_SKE_STATUS_OK) { SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)", status)); protocol->state = SILC_PROTOCOL_STATE_ERROR; silc_protocol_execute(protocol, server->timeout_queue, 0, 300000); return; } /* Advance the protocol state */ protocol->state++; silc_protocol_execute(protocol, server->timeout_queue, 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); /* 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(); ctx->ske->rng = server->rng; ctx->ske->prop = silc_calloc(1, sizeof(*ctx->ske->prop)); silc_ske_get_group_by_number(idata->rekey->ske_group, &ctx->ske->prop->group); status = silc_ske_initiator_phase_2(ctx->ske, NULL, NULL, silc_server_protocol_rekey_send_packet, context); if (status != SILC_SKE_STATUS_OK) { SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)", status)); protocol->state = SILC_PROTOCOL_STATE_ERROR; silc_protocol_execute(protocol, server->timeout_queue, 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); /* The protocol ends in next stage. */ protocol->state = SILC_PROTOCOL_STATE_END; } } } break; case 2: /* * Second state, used only when oding re-key with PFS. */ if (ctx->responder == TRUE) { if (ctx->pfs == TRUE) { /* * Send our KE packe 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, silc_server_protocol_rekey_send_packet, context); if (status != SILC_SKE_STATUS_OK) { SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)", status)); protocol->state = SILC_PROTOCOL_STATE_ERROR; silc_protocol_execute(protocol, server->timeout_queue, 0, 300000); return; } } } else { if (ctx->pfs == TRUE) { /* * The packet type must be KE packet */ if (ctx->packet->type != SILC_PACKET_KEY_EXCHANGE_2) { /* Error in protocol */ protocol->state = SILC_PROTOCOL_STATE_ERROR; silc_protocol_execute(protocol, server->timeout_queue, 0, 300000); } status = silc_ske_initiator_finish(ctx->ske, ctx->packet->buffer, NULL, NULL, NULL, NULL); if (status != SILC_SKE_STATUS_OK) { SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)", status)); protocol->state = SILC_PROTOCOL_STATE_ERROR; silc_protocol_execute(protocol, server->timeout_queue, 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) { /* Error in protocol */ protocol->state = SILC_PROTOCOL_STATE_ERROR; silc_protocol_execute(protocol, server->timeout_queue, 0, 0); } /* 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 */ silc_server_protocol_rekey_generate(server, ctx, FALSE); /* Protocol has ended, call the final callback */ if (protocol->final_callback) silc_protocol_execute_final(protocol, server->timeout_queue); 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, silc_server_protocol_ke_send_packet, context); } /* On error the final callback is always called. */ if (protocol->final_callback) silc_protocol_execute_final(protocol, server->timeout_queue); else silc_protocol_free(protocol); break; case SILC_PROTOCOL_STATE_FAILURE: /* * We have received failure from remote */ /* On error the final callback is always called. */ if (protocol->final_callback) silc_protocol_execute_final(protocol, server->timeout_queue); else silc_protocol_free(protocol); break; case SILC_PROTOCOL_STATE_UNKNOWN: break; } } /* Registers protocols used in server. */ void silc_server_protocols_register(void) { silc_protocol_register(SILC_PROTOCOL_SERVER_CONNECTION_AUTH, 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); } /* Unregisters protocols */ void silc_server_protocols_unregister(void) { silc_protocol_unregister(SILC_PROTOCOL_SERVER_CONNECTION_AUTH, 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); }