+ 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_queue_purge(server, ctx->sock);
+ 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_queue_purge(server, ctx->sock);
+ 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_queue_purge(server, ctx->sock);
+ 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);
+ silc_server_packet_queue_purge(server, ctx->sock);
+
+ /* 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);
+ silc_server_packet_queue_purge(server, ctx->sock);
+
+ /* 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);