X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilcclient%2Fclient_prvmsg.c;h=01d80c61125243091338826e65034555a43d32b6;hb=80d10dbf48785c2163551a7f94a46f6f5849c4a7;hp=5dd74141ceea07547d09b7fe95b737e7efb86afc;hpb=d7f1e81fea0d1da2ac870b8dfa600669aa280cd5;p=silc.git diff --git a/lib/silcclient/client_prvmsg.c b/lib/silcclient/client_prvmsg.c index 5dd74141..01d80c61 100644 --- a/lib/silcclient/client_prvmsg.c +++ b/lib/silcclient/client_prvmsg.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2007 Pekka Riikonen + Copyright (C) 1997 - 2014 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 @@ -47,8 +47,21 @@ SilcBool silc_client_send_private_message(SilcClient client, SILC_LOG_DEBUG(("Sending private message")); + /* Auto-negotiate private message key (AKE) if there is no key or + it's time to rekey. */ + if (!client->internal->params->dont_autoneg_prvmsg_keys && + !client_entry->internal.no_ake && client_entry != conn->local_entry && + (!client_entry->internal.send_key || + (client_entry->internal.ake_rekey <= silc_time() || + client_entry->internal.ake_generation != + conn->internal->ake_generation))) { + return silc_client_autoneg_private_message_key( + client, conn, client_entry, NULL, + flags, hash, data, data_len); + } + sid.type = SILC_ID_CLIENT; - sid.u.client_id = conn->local_entry->id; + sid.u.client_id = *conn->local_id; rid.type = SILC_ID_CLIENT; rid.u.client_id = client_entry->id; @@ -139,8 +152,26 @@ SILC_FSM_STATE(silc_client_private_message) if (silc_unlikely(packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY && !remote_client->internal.receive_key && - !remote_client->internal.hmac_receive)) + !remote_client->internal.hmac_receive)) { +#if 1 + /* Kludge to check if the message has SKE packet inside, and then start + key exchange protocol. Remove this once AKE support is everywhere. */ + payload = silc_message_payload_parse(silc_buffer_datalen(&packet->buffer), + TRUE, FALSE, NULL, NULL, + packet->src_id, packet->src_id_len, + packet->dst_id, packet->dst_id_len, + NULL, FALSE, NULL); + if (!payload) + goto out; + + flags = silc_message_get_flags(payload); + if (flags & SILC_MESSAGE_FLAG_PACKET && + silc_client_autoneg_private_message_key(client, conn, remote_client, + packet, 0, NULL, NULL, 0)) + packet = NULL; +#endif /* 0 */ goto out; + } /* Parse the payload and decrypt it also if private message key is set */ payload = @@ -151,12 +182,35 @@ SILC_FSM_STATE(silc_client_private_message) packet->src_id, packet->src_id_len, packet->dst_id, packet->dst_id_len, NULL, FALSE, NULL); - if (silc_unlikely(!payload)) + if (silc_unlikely(!payload)) { + /* Private message key is set but the sender may have removed it, + try to parse without it. */ + if (remote_client->internal.receive_key) { + SILC_LOG_DEBUG(("Parse payload without using private message key")); + payload = + silc_message_payload_parse(silc_buffer_datalen(&packet->buffer), + TRUE, FALSE, NULL, NULL, + packet->src_id, packet->src_id_len, + packet->dst_id, packet->dst_id_len, + NULL, FALSE, NULL); + } + } + if (!payload) goto out; - /* Pass the private message to application */ flags = silc_message_get_flags(payload); + + /* If message contains SILC packet, process the packet here */ + if (flags & SILC_MESSAGE_FLAG_PACKET) { + if (silc_client_autoneg_private_message_key(client, conn, remote_client, + packet, 0, NULL, NULL, 0)) + packet = NULL; + goto out; + } + message = silc_message_get_data(payload, &message_len); + + /* Pass the private message to application */ client->internal->ops->private_message(client, conn, remote_client, payload, flags, message, message_len); @@ -178,7 +232,8 @@ SILC_FSM_STATE(silc_client_private_message) out: /** Packet processed */ - silc_packet_free(packet); + if (packet) + silc_packet_free(packet); silc_client_unref_client(client, conn, remote_client); if (payload) silc_message_payload_free(payload); @@ -461,6 +516,7 @@ SilcBool silc_client_add_private_message_key_ske(SilcClient client, return FALSE; client_entry->internal.generated = TRUE; + client_entry->internal.no_ake = TRUE; /* Allocate the cipher and HMAC */ if (!silc_cipher_alloc(cipher, &client_entry->internal.send_key)) @@ -616,6 +672,623 @@ silc_client_private_message_key_is_set(SilcClient client, return client_entry->internal.send_key != NULL; } +/********************* Private Message Key Autoneg (AKE) ********************/ + +/* Private message key auto-negotiation context */ +struct SilcClientAutonegMessageKeyStruct { + SilcClientConnection conn; /* Connection to server */ + SilcSKE ske; /* SKE with remote client */ + SilcAsyncOperation ske_op; /* SKE operation */ + SilcStream stream; /* PRIVATE_MESSAGE stream */ + SilcPacketStream ske_stream; /* Packet stream for SKE (inside + the PRIVATE_MESSAGE stream) */ + SilcDList messages; /* Message queue */ + SilcHash hash; /* Initial message hash */ + SilcPublicKey public_key; /* Remote client public key */ + SilcVerifyKeyContext verify; + SilcUInt32 generation; /* Starting AKE generation */ +}; + +static SilcBool +silc_client_autoneg_key_recv_ske(SilcPacketEngine engine, + SilcPacketStream stream, + SilcPacket packet, + void *callback_context, + void *stream_context); + +static const SilcPacketCallbacks autoneg_key_ske_cbs = +{ + silc_client_autoneg_key_recv_ske, NULL, NULL +}; + +/* Destroy auto-negotiation context */ + +static void silc_client_autoneg_key_free(SilcClient client, + SilcClientConnection conn, + SilcClientEntry client_entry) +{ + SilcClientAutonegMessageKey ake = client_entry->internal.ake; + SilcBuffer m; + + if (ake->ske_op) + silc_async_abort(ake->ske_op, NULL, NULL); + + silc_ske_free(ake->ske); + silc_packet_stream_unlink(ake->ske_stream, &autoneg_key_ske_cbs, NULL); + silc_packet_stream_destroy(ake->ske_stream); + if (ake->hash) + silc_hash_free(ake->hash); + + silc_dlist_start(ake->messages); + while ((m = silc_dlist_get(ake->messages)) != SILC_LIST_END) { + silc_dlist_del(ake->messages, m); + silc_buffer_free(m); + } + silc_dlist_uninit(ake->messages); + + client_entry->internal.op = NULL; + client_entry->internal.ake = NULL; + silc_client_unref_client(client, conn, client_entry); + + if (ake->verify) + ake->verify->aborted = TRUE; + else if (ake->public_key) + silc_pkcs_public_key_free(ake->public_key); + + silc_free(ake); +} + +/* Destroy auto-negotiation context */ + +SILC_TASK_CALLBACK(silc_client_autoneg_key_finish) +{ + SilcClientEntry client_entry = context; + SilcClientAutonegMessageKey ake = client_entry->internal.ake; + SilcClientConnection conn = ake->conn; + SilcClient client = conn->client; + + silc_client_autoneg_key_free(client, conn, client_entry); +} + +/* Abort callback. This aborts the auto-negotiation and the SKE */ + +static void +silc_client_autoneg_key_abort(SilcAsyncOperation op, void *context) +{ + SilcClientEntry client_entry = context; + SilcClientAutonegMessageKey ake = client_entry->internal.ake; + SilcClientConnection conn = ake->conn; + SilcClient client = conn->client; + + if (!ake) + return; + + silc_client_autoneg_key_free(client, conn, client_entry); +} + +/* SKE packet stream callback. Here we verify that the packets we got + from the private message are actually SKE packets for us. */ + +static SilcBool +silc_client_autoneg_key_recv_ske(SilcPacketEngine engine, + SilcPacketStream stream, + SilcPacket packet, + void *callback_context, + void *stream_context) +{ + SilcClientEntry client_entry = stream_context; + SilcClientID remote_id; + + SILC_LOG_DEBUG(("Packet %p type %d inside private message", packet, + packet->type)); + + /* Take only SKE packets, drop others, no support for anything else */ + if (packet->type != SILC_PACKET_KEY_EXCHANGE && + packet->type != SILC_PACKET_KEY_EXCHANGE_1 && + packet->type != SILC_PACKET_KEY_EXCHANGE_2 && + packet->type != SILC_PACKET_FAILURE) + goto drop; + + /* Must be from client to client */ + if (packet->dst_id_type != SILC_ID_CLIENT || + packet->src_id_type != SILC_ID_CLIENT) + goto drop; + + if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT, + &remote_id, sizeof(remote_id))) + goto drop; + + if (!SILC_ID_CLIENT_COMPARE(&client_entry->id, &remote_id)) { + /* The packet is not for this client, but it must be */ + SILC_LOG_DEBUG(("Client ids do not match")); + goto drop; + } + + /* Packet is ok and is for us, let it pass to SKE */ + SILC_LOG_DEBUG(("Pass packet %p type %d", packet, packet->type)); + return FALSE; + + drop: + silc_packet_free(packet); + return TRUE; +} + +/* Coder callback for actually encoding/decoding the SKE packets inside + private messages. */ + +static SilcBool silc_client_autoneg_key_coder(SilcStream stream, + SilcStreamStatus status, + SilcBuffer buffer, + void *context) +{ + SilcBool ret = FALSE; + SilcBuffer message; + SilcMessagePayload payload = NULL; + SilcMessageFlags flags; + unsigned char *msg; + SilcUInt32 message_len; + + switch (status) { + case SILC_STREAM_CAN_READ: + /* Decode private message. We get all private messages here from + the remote client while we're doing SKE, so we must take the + correct messages. */ + SILC_LOG_DEBUG(("Decode packet inside private message")); + + payload = silc_message_payload_parse(silc_buffer_datalen(buffer), + TRUE, FALSE, NULL, NULL, NULL, 0, + NULL, 0, NULL, FALSE, NULL); + if (!payload) { + SILC_LOG_DEBUG(("Error decoding private message payload")); + goto out; + } + + /* Ignore this message if it's not packet */ + flags = silc_message_get_flags(payload); + if (!(flags & SILC_MESSAGE_FLAG_PACKET)) { + SILC_LOG_DEBUG(("Private message doesn't contain packet")); + silc_message_payload_free(payload); + goto out; + } + + /* Take the packet */ + ret = TRUE; + + msg = silc_message_get_data(payload, &message_len); + silc_buffer_reset(buffer); + if (!silc_buffer_enlarge(buffer, message_len)) { + silc_message_payload_free(payload); + goto out; + } + silc_buffer_put(buffer, msg, message_len); + + silc_message_payload_free(payload); + break; + + case SILC_STREAM_CAN_WRITE: + /* Encode private message */ + SILC_LOG_DEBUG(("Encode packet inside private message")); + + ret = TRUE; + + message = + silc_message_payload_encode(SILC_MESSAGE_FLAG_PACKET, + silc_buffer_datalen(buffer), + FALSE, TRUE, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL); + if (!message) { + SILC_LOG_DEBUG(("Error encoding private message payload")); + goto out; + } + + silc_buffer_reset(buffer); + if (!silc_buffer_enlarge(buffer, silc_buffer_len(message))) + goto out; + silc_buffer_put(buffer, silc_buffer_datalen(message)); + + break; + + default: + break; + } + + out: + return ret; +} + +/* Called after application has verified remote client's public key */ + +static void +silc_client_autoneg_key_verify_pubkey_cb(SilcBool success, void *context) +{ + SilcVerifyKeyContext verify = context; + SilcClientAutonegMessageKey ake = verify->context; + + SILC_LOG_DEBUG(("Start")); + + /* Call the completion callback back to the SKE */ + if (!verify->aborted) { + ake->verify = NULL; + verify->completion(verify->ske, success ? SILC_SKE_STATUS_OK : + SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY, + verify->completion_context); + } else { + silc_pkcs_public_key_free(verify->public_key); + } + + silc_free(verify); +} + +/* Remote client's public key verification callback */ + +static void +silc_client_autoneg_key_verify_pubkey(SilcSKE ske, + SilcPublicKey public_key, + void *context, + SilcSKEVerifyCbCompletion completion, + void *completion_context) +{ + SilcClientEntry client_entry = context; + SilcClientAutonegMessageKey ake = client_entry->internal.ake; + SilcClientConnection conn = ake->conn; + SilcClient client = conn->client; + SilcVerifyKeyContext verify; + + /* Use public key we cached earlier in AKE for direction verification */ + if (client_entry->internal.send_key && client_entry->public_key && + silc_pkcs_public_key_compare(public_key, client_entry->public_key)) { + SILC_LOG_DEBUG(("Client's cached public key matches")); + completion(ske, SILC_SKE_STATUS_OK, completion_context); + return; + } + + /* If we provided repository for SKE and we got here the key was not + found from the repository. */ + if (conn->internal->params.repository && + !conn->internal->params.verify_notfound) { + completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY, + completion_context); + return; + } + + SILC_LOG_DEBUG(("Verify remote client public key")); + + ake->public_key = silc_pkcs_public_key_copy(public_key); + if (!ake->public_key) { + completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY, + completion_context); + return; + } + + verify = silc_calloc(1, sizeof(*verify)); + if (!verify) { + completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY, + completion_context); + return; + } + verify->public_key = ake->public_key; + verify->ske = ske; + verify->completion = completion; + verify->completion_context = completion_context; + verify->context = ake; + ake->verify = verify; + + /* Verify public key in application */ + client->internal->ops->verify_public_key( + client, conn, + SILC_CONN_CLIENT, ake->public_key, + silc_client_autoneg_key_verify_pubkey_cb, + verify); +} + +/* Key exchange protocol completion callback */ + +static void silc_client_autoneg_key_done(SilcSKE ske, + SilcSKEStatus status, + SilcSKESecurityProperties prop, + SilcSKEKeyMaterial keymat, + SilcSKERekeyMaterial rekey, + void *context) +{ + SilcClientEntry client_entry = context; + SilcClientAutonegMessageKey ake = client_entry->internal.ake; + SilcClientConnection conn = ake->conn; + SilcClient client = conn->client; + SilcBool initiator = !client_entry->internal.prv_resp; + SilcMessageFlags flags; + SilcBuffer m; + + ake->ske_op = NULL; + + conn->context_type = SILC_ID_CLIENT; + conn->client_entry = client_entry; + + if (status != SILC_SKE_STATUS_OK) { + /* Key exchange failed */ + SILC_LOG_DEBUG(("Error during key exchange: %s (%d)", + silc_ske_map_status(status), status)); + + if (initiator) { + if (status != SILC_SKE_STATUS_PROBE_TIMEOUT) + client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Cannot send private message to %s (%s)", + client_entry->nickname, + silc_ske_map_status(status)); + else if (client_entry->mode & SILC_UMODE_DETACHED) + client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Cannot send private message to detached " + "client %s", client_entry->nickname); + } else if (status != SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY) { + client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Private message key exchange failed " + "with %s (%s)", client_entry->nickname, + silc_ske_map_status(status)); + } + + /* Errors that occur due to user not responding or deciding not to + trust the public key will not cause us to stop trying AKE next time. + Other errors disable AKE to allow communication with other means. */ + if (initiator && status != SILC_SKE_STATUS_TIMEOUT && + status != SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY && + !(client_entry->mode & SILC_UMODE_DETACHED)) { + client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO, + "Cannot auto-negotiate key with %s, " + "messages will be protected with " + "session key", client_entry->nickname); + + /* Don't try this again with this client */ + client_entry->internal.no_ake = TRUE; + } + goto out; + } + + /* Set the new private message key into use */ + silc_client_del_private_message_key(client, conn, client_entry); + client_entry->internal.prv_resp = !initiator; + if (!silc_client_add_private_message_key_ske( + client, conn, client_entry, + silc_cipher_get_name(prop->cipher), + silc_hmac_get_name(prop->hmac), + keymat)) { + SILC_LOG_DEBUG(("Error adding private message key")); + + client->internal->ops->say(client, conn, + SILC_CLIENT_MESSAGE_ERROR, + "Private message key exchange error: " + "cannot use keys"); + + /* Don't try this again with this client */ + client_entry->internal.no_ake = TRUE; + goto out; + } + + /* Save the public key to client entry */ + if (!client_entry->public_key) { + client_entry->public_key = ake->public_key; + ake->public_key = NULL; + } + + /* Rekey periodically */ + client_entry->internal.ake_rekey = silc_time() + 300; + if (initiator) + client_entry->internal.ake_rekey -= 30; + client_entry->internal.ake_generation = conn->internal->ake_generation; + client_entry->internal.no_ake = FALSE; + + SILC_LOG_DEBUG(("AKE completed as %s with %s, rekey in %u secs, " + "generation %u", initiator ? "initiator" : "responder", + client_entry->nickname, 300, + conn->internal->ake_generation)); + + /* Send queued messages */ + silc_dlist_start(ake->messages); + while ((m = silc_dlist_get(ake->messages)) != SILC_LIST_END) { + SILC_GET16_MSB(flags, m->data - 2); + silc_client_send_private_message(client, conn, client_entry, + flags, ake->hash, + silc_buffer_datalen(m)); + } + + out: + conn->context_type = SILC_ID_NONE; + conn->client_entry = NULL; + silc_schedule_task_add_timeout(client->schedule, + silc_client_autoneg_key_finish, + client_entry, 0, 1); +} + +/* Auto-negotiate private message key with the remote client using the + SKE protocol, which is tunneled through the SILC network inside private + messages shared between the us and the remote client. + + This operation is naturally asynchronous and will involve exchanging + multiple messages back and forth. Despite this, we don't run this + operation in own FSM thread here, but instead will use the SKE library + to do the asynchronous operation which we can abort at any time in + case user disconnects. + + Messages and packets we receive during this operation will be processed + in the normal connection thread. */ + +SilcBool +silc_client_autoneg_private_message_key(SilcClient client, + SilcClientConnection conn, + SilcClientEntry client_entry, + SilcPacket initiator_packet, + SilcMessageFlags flags, + SilcHash hash, + unsigned char *data, + SilcUInt32 data_len) +{ + SilcClientAutonegMessageKey ake; + SilcSKEParamsStruct params = {}; + SilcBool initiator = initiator_packet == NULL; + SilcBuffer m; + + SILC_LOG_DEBUG(("Start private message AKE as %s with %s", + initiator ? "initiator" : "responder", + client_entry->nickname)); + + if (client_entry->internal.op) { + ake = client_entry->internal.ake; + if (ake && data) { + /* If generation has changed, we must abort this exchange and + start a new one. */ + if (ake->generation != conn->internal->ake_generation) { + SILC_LOG_DEBUG(("Abort ongoing AKE and start new one")); + silc_async_abort(client_entry->internal.op, NULL, NULL); + } else { + SILC_LOG_DEBUG(("AKE is ongoing, queue the message")); + + m = silc_buffer_alloc_size(data_len + 2); + if (!m) + return FALSE; + SILC_PUT16_MSB(flags, m->data); + silc_buffer_pull(m, 2); + silc_buffer_put(m, data, data_len); + silc_dlist_add(ake->messages, m); + return TRUE; + } + } else { + SILC_LOG_DEBUG(("Cannot start AKE, operation %p is ongoing", + client_entry->internal.op)); + return FALSE; + } + } + + ake = silc_calloc(1, sizeof(*ake)); + if (!ake) + return FALSE; + ake->conn = conn; + ake->generation = conn->internal->ake_generation; + + ake->messages = silc_dlist_init(); + if (!ake->messages) + goto err; + + /* Wrap our packet stream to a generic stream for the private messages + we are going to exchange. We send the packets with packet flag + SILC_PACKET_FLAG_PRIVMSG_KEY which is a lie, but is a way to get + clients which do not support this protocol to ignore these messages. + This kludge should be removed once support is everywhere and + responder should look only for the SILC_MESSAGE_FLAG_PACKET. */ + ake->stream = silc_packet_stream_wrap(conn->stream, + SILC_PACKET_PRIVATE_MESSAGE, + SILC_PACKET_FLAG_PRIVMSG_KEY, FALSE, + SILC_ID_NONE, NULL, + SILC_ID_CLIENT, &client_entry->id, + silc_client_autoneg_key_coder, + client_entry); + if (!ake->stream) + goto err; + + /* Create a new packet stream for the SKE library using the wrapped + stream as the underlaying stream, in effect creating a tunnel to + send SKE packets inside private message packets. */ + ake->ske_stream = silc_packet_stream_create(client->internal->packet_engine, + conn->internal->schedule, + ake->stream); + if (!ake->ske_stream) + goto err; + + silc_packet_set_context(ake->ske_stream, client_entry); + silc_packet_set_ids(ake->ske_stream, SILC_ID_CLIENT, conn->local_id, + SILC_ID_CLIENT, &client_entry->id); + + /* Link to the new packet stream to intercept the packets before they + go to SKE library so that we can do additional checks and decide if + we really want to process the packets. */ + if (!silc_packet_stream_link(ake->ske_stream, &autoneg_key_ske_cbs, NULL, + 1000001, SILC_PACKET_ANY, -1)) + goto err; + + /* Create SKE */ + ake->ske = silc_ske_alloc(client->rng, conn->internal->schedule, + conn->internal->params.repository, + conn->public_key, conn->private_key, + client_entry); + if (!ake->ske) + goto err; + + silc_ske_set_callbacks(ake->ske, silc_client_autoneg_key_verify_pubkey, + silc_client_autoneg_key_done, client_entry); + params.version = client->internal->silc_client_version; + params.probe_timeout_secs = 5; + params.timeout_secs = 120; + params.flags = SILC_SKE_SP_FLAG_MUTUAL | SILC_SKE_SP_FLAG_PFS; + params.small_proposal = TRUE; + params.no_acks = TRUE; + + if (client_entry->internal.send_key && + client_entry->internal.ake_generation == ake->generation) { + /* Security properties for rekey */ + SilcSKESecurityProperties prop = silc_calloc(1, sizeof(*prop)); + if (!prop) + goto err; + silc_cipher_alloc(silc_cipher_get_name(client_entry->internal.send_key), + &prop->cipher); + silc_hmac_alloc(silc_hmac_get_name(client_entry->internal.hmac_send), + NULL, &prop->hmac); + silc_hash_alloc(silc_hash_get_name(silc_hmac_get_hash( + client_entry->internal.hmac_send)), &prop->hash); + prop->public_key = silc_pkcs_public_key_copy(client_entry->public_key); + silc_ske_group_get_by_number(2, &prop->group); + prop->flags = params.flags; + params.prop = prop; + } + + /* Start key exchange */ + if (initiator) + ake->ske_op = silc_ske_initiator(ake->ske, ake->ske_stream, ¶ms, NULL); + else + ake->ske_op = silc_ske_responder(ake->ske, ake->ske_stream, ¶ms); + if (!ake->ske_op) + goto err; + + /* Finally, set up the client entry */ + client_entry->internal.op = silc_async_alloc(silc_client_autoneg_key_abort, + NULL, client_entry); + if (!client_entry->internal.op) + goto err; + client_entry->internal.ake = ake; + client_entry->internal.no_ake = FALSE; + client_entry->internal.prv_resp = !initiator; + silc_client_ref_client(client, conn, client_entry); + + /* As responder, re-inject the initiator's private message back to the + stream so that the new SKE gets it. */ + if (initiator_packet) + silc_packet_stream_inject(conn->stream, initiator_packet); + + /* Save the initial message, it will be sent after the key has been + negotiated. */ + if (data && data_len) { + m = silc_buffer_alloc_size(data_len + 2); + if (m) { + SILC_PUT16_MSB(flags, m->data); + silc_buffer_pull(m, 2); + silc_buffer_put(m, data, data_len); + silc_dlist_add(ake->messages, m); + } + if (hash) + silc_hash_alloc(silc_hash_get_name(hash), &ake->hash); + } + + return TRUE; + + err: + if (ake->ske) + silc_ske_free(ake->ske); + if (ake->ske_stream) { + silc_packet_stream_unlink(ake->ske_stream, &autoneg_key_ske_cbs, NULL); + silc_packet_stream_destroy(ake->ske_stream); + } else if (ake->stream) + silc_stream_destroy(ake->stream); + silc_dlist_uninit(ake->messages); + silc_free(ake); + return FALSE; +} + /* Sets away `message'. The away message may be set when the client's mode is changed to SILC_UMODE_GONE and the client whishes to reply to anyone who sends private message. The `message' will be sent