Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 1997 - 2006 Pekka Riikonen
+ Copyright (C) 1997 - 2007 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
{
SilcBuffer buffer;
SilcBool ret;
+ SilcID sid, rid;
- SILC_LOG_DEBUG(("Sending private message"));
-
- if (!client || !conn || !client_entry)
+ if (silc_unlikely(!client || !conn || !client_entry))
return FALSE;
- if (flags & SILC_MESSAGE_FLAG_SIGNED && !hash)
+ if (silc_unlikely(flags & SILC_MESSAGE_FLAG_SIGNED && !hash))
return FALSE;
+ if (silc_unlikely(conn->internal->disconnected))
+ return FALSE;
+
+ SILC_LOG_DEBUG(("Sending private message"));
+
+ sid.type = SILC_ID_CLIENT;
+ sid.u.client_id = conn->local_entry->id;
+ rid.type = SILC_ID_CLIENT;
+ rid.u.client_id = client_entry->id;
/* Encode private message payload */
buffer =
TRUE, client_entry->internal.send_key,
client_entry->internal.hmac_send,
client->rng, NULL, conn->private_key,
- hash, NULL);
- if (!buffer) {
+ hash, &sid, &rid, NULL);
+ if (silc_unlikely(!buffer)) {
SILC_LOG_ERROR(("Error encoding private message"));
return FALSE;
}
/************************* Private Message Receive **************************/
-/* Private message waiting context */
-typedef struct {
- SilcMutex wait_lock;
- SilcCond wait_cond;
- SilcDList message_queue;
- unsigned int stopped : 1;
-} *SilcClientPrivateMessageWait;
-
/* Client resolving callback. Continues with the private message processing */
static void silc_client_private_message_resolved(SilcClient client,
SilcMessageFlags flags;
unsigned char *message;
SilcUInt32 message_len;
- SilcClientPrivateMessageWait pmw;
SILC_LOG_DEBUG(("Received private message"));
- if (packet->src_id_type != SILC_ID_CLIENT) {
+ if (silc_unlikely(packet->src_id_type != SILC_ID_CLIENT)) {
/** Invalid packet */
silc_fsm_next(fsm, silc_client_private_message_error);
return SILC_FSM_CONTINUE;
}
- if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
- &remote_id, sizeof(remote_id))) {
+ if (silc_unlikely(!silc_id_str2id(packet->src_id, packet->src_id_len,
+ SILC_ID_CLIENT, &remote_id,
+ sizeof(remote_id)))) {
/** Invalid source ID */
silc_fsm_next(fsm, silc_client_private_message_error);
return SILC_FSM_CONTINUE;
/* NOT REACHED */
}
- if (packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY &&
- !remote_client->internal.receive_key &&
- !remote_client->internal.hmac_receive)
+ if (silc_unlikely(packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY &&
+ !remote_client->internal.receive_key &&
+ !remote_client->internal.hmac_receive))
goto out;
/* Parse the payload and decrypt it also if private message key is set */
TRUE, !remote_client->internal.generated,
remote_client->internal.receive_key,
remote_client->internal.hmac_receive,
+ packet->src_id, packet->src_id_len,
+ packet->dst_id, packet->dst_id_len,
NULL, FALSE, NULL);
- if (!payload)
+ if (silc_unlikely(!payload))
goto out;
-#if 0 /* We need to rethink this. This doesn't work with multiple
- waiters, and performance is suboptimal. */
- /* Check if some thread is waiting for this private message */
- silc_mutex_lock(conn->internal->lock);
- if (conn->internal->privmsg_wait &&
- silc_hash_table_find_ext(conn->internal->privmsg_wait,
- &remote_client->id, NULL, (void **)&pmw,
- NULL, NULL, silc_hash_id_compare_full,
- SILC_32_TO_PTR(SILC_ID_CLIENT))) {
- /* Signal that message was received */
- silc_mutex_unlock(conn->internal->lock);
- silc_mutex_lock(pmw->wait_lock);
- if (!pmw->stopped) {
- silc_dlist_add(pmw->message_queue, payload);
- silc_cond_broadcast(pmw->wait_cond);
- silc_mutex_unlock(pmw->wait_lock);
- silc_packet_free(packet);
- goto out;
- }
- silc_mutex_unlock(pmw->wait_lock);
- } else
- silc_mutex_unlock(conn->internal->lock);
-#endif /* 0 */
-
/* Pass the private message to application */
flags = silc_message_get_flags(payload);
message = silc_message_get_data(payload, &message_len);
/* See if we are away (gone). If we are away we will reply to the
sender with the set away message. */
- if (conn->internal->away && conn->internal->away->away &&
+ if (conn->internal->away_message &&
!(flags & SILC_MESSAGE_FLAG_NOREPLY)) {
/* If it's me, ignore */
if (SILC_ID_CLIENT_COMPARE(&remote_id, conn->local_id))
silc_client_send_private_message(client, conn, remote_client,
SILC_MESSAGE_FLAG_AUTOREPLY |
SILC_MESSAGE_FLAG_NOREPLY, NULL,
- conn->internal->away->away,
- strlen(conn->internal->away->away));
+ conn->internal->away_message,
+ strlen(conn->internal->away_message));
}
out:
return SILC_FSM_FINISH;
}
-#if 0 /* XXX we need to rethink this */
-/* Initialize private message waiting in a thread. */
+/* Initialize private message waiter for the `conn' connection. */
-void *silc_client_private_message_wait_init(SilcClientConnection conn,
- SilcClientEntry client_entry)
+SilcBool silc_client_private_message_wait_init(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry client_entry)
{
- SilcClientPrivateMessageWait pmw;
-
- pmw = silc_calloc(1, sizeof(*pmw));
- if (!pmw)
- return NULL;
-
- pmw->message_queue = silc_dlist_init();
- if (!pmw->message_queue) {
- silc_free(pmw);
- return NULL;
- }
-
- /* Allocate mutex and conditional variable */
- if (!silc_mutex_alloc(&pmw->wait_lock)) {
- silc_dlist_uninit(pmw->message_queue);
- silc_free(pmw);
- return NULL;
- }
- if (!silc_cond_alloc(&pmw->wait_cond)) {
- silc_dlist_uninit(pmw->message_queue);
- silc_mutex_free(pmw->wait_lock);
- silc_free(pmw);
- return NULL;
- }
+ SilcID id;
- silc_mutex_lock(conn->internal->lock);
-
- /* Allocate waiting hash table */
- if (!conn->internal->privmsg_wait) {
- conn->internal->privmsg_wait =
- silc_hash_table_alloc(0, silc_hash_id,
- SILC_32_TO_PTR(SILC_ID_CLIENT),
- silc_hash_id_compare,
- SILC_32_TO_PTR(SILC_ID_CLIENT), NULL, NULL, TRUE);
- if (!conn->internal->privmsg_wait) {
- silc_mutex_unlock(conn->internal->lock);
- silc_dlist_uninit(pmw->message_queue);
- silc_mutex_free(pmw->wait_lock);
- silc_cond_free(pmw->wait_cond);
- silc_free(pmw);
- return NULL;
- }
- }
+ if (client_entry->internal.prv_waiter)
+ return TRUE;
- /* Add to waiting hash table */
- silc_hash_table_add(conn->internal->privmsg_wait, client_entry->id, pmw);
+ /* We want SILC_PACKET_PRIVATE_MESSAGE packets from this source ID. */
+ id.type = SILC_ID_CLIENT;
+ id.u.client_id = client_entry->id;
- silc_mutex_unlock(conn->internal->lock);
+ client_entry->internal.prv_waiter =
+ silc_packet_wait_init(conn->stream, &id, SILC_PACKET_PRIVATE_MESSAGE, -1);
+ if (!client_entry->internal.prv_waiter)
+ return FALSE;
- return (void *)pmw;
+ return TRUE;
}
-/* Uninitialize private message waiting. */
+/* Uninitializes private message waiter. */
-void silc_client_private_message_wait_uninit(SilcClientConnection conn,
- SilcClientEntry client_entry,
- void *waiter)
+void silc_client_private_message_wait_uninit(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry client_entry)
{
- SilcClientPrivateMessageWait pmw = waiter;
- SilcMessagePayload payload;
-
- /* Signal any threads to stop waiting */
- silc_mutex_lock(pmw->wait_lock);
- pmw->stopped = TRUE;
- silc_cond_broadcast(pmw->wait_cond);
- silc_mutex_unlock(pmw->wait_lock);
-
- /* Re-acquire lock and free resources */
- silc_mutex_lock(pmw->wait_lock);
-
- /* Free any remaining message */
- silc_dlist_start(pmw->message_queue);
- while ((payload = silc_dlist_get(pmw->message_queue)))
- silc_message_payload_free(payload);
-
- silc_dlist_uninit(pmw->message_queue);
- silc_cond_free(pmw->wait_cond);
- silc_mutex_unlock(pmw->wait_lock);
- silc_mutex_free(pmw->wait_lock);
-
- silc_mutex_lock(conn->internal->lock);
- silc_hash_table_del_by_context(conn->internal->privmsg_wait,
- client_entry->id, pmw);
- silc_mutex_unlock(conn->internal->lock);
-
- silc_free(pmw);
+ if (!client_entry->internal.prv_waiter)
+ return;
+ silc_packet_wait_uninit(client_entry->internal.prv_waiter, conn->stream);
+ client_entry->internal.prv_waiter = NULL;
}
-/* Blocks the calling process or thread until a private message has been
+/* Blocks the calling process or thread until private message has been
received from the specified client. */
-SilcBool silc_client_private_message_wait(SilcClientConnection conn,
+SilcBool silc_client_private_message_wait(SilcClient client,
+ SilcClientConnection conn,
SilcClientEntry client_entry,
- void *waiter,
SilcMessagePayload *payload)
{
- SilcClientPrivateMessageWait pmw = waiter;
SilcPacket packet;
- silc_mutex_lock(pmw->wait_lock);
+ if (!client_entry->internal.prv_waiter)
+ return FALSE;
- /* Wait here until private message has been received */
- while (silc_dlist_count(pmw->message_queue) == 0) {
- if (pmw->stopped) {
- silc_mutex_unlock(pmw->wait_lock);
+ /* Block until private message arrives */
+ do {
+ if ((silc_packet_wait(client_entry->internal.prv_waiter, 0, &packet)) < 0)
return FALSE;
- }
- silc_cond_wait(pmw->wait_cond, pmw->wait_lock);
- }
- /* Return message */
- silc_dlist_start(pmw->message_queue);
- *payload = silc_dlist_get(pmw->message_queue);
- silc_dlist_del(pmw->message_queue, *payload);
+ /* Parse the payload and decrypt it also if private message key is set */
+ *payload =
+ silc_message_payload_parse(silc_buffer_data(&packet->buffer),
+ silc_buffer_len(&packet->buffer),
+ TRUE, !client_entry->internal.generated,
+ client_entry->internal.receive_key,
+ client_entry->internal.hmac_receive,
+ packet->src_id, packet->src_id_len,
+ packet->dst_id, packet->dst_id_len,
+ NULL, FALSE, NULL);
+ if (!(*payload)) {
+ silc_packet_free(packet);
+ continue;
+ }
- silc_mutex_unlock(pmw->wait_lock);
+ break;
+ } while (1);
+ silc_packet_free(packet);
return TRUE;
}
-#endif /* 0 */
/*************************** Private Message Key ****************************/
+/* Sends private message key request. Sender of this packet is initiator
+ when setting the private message key. */
+
+static SilcBool
+silc_client_send_private_message_key_request(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry client_entry)
+{
+ const char *cipher, *hmac;
+
+ SILC_LOG_DEBUG(("Sending private message key request"));
+
+ cipher = silc_cipher_get_name(client_entry->internal.send_key);
+ hmac = silc_hmac_get_name(client_entry->internal.hmac_send);
+
+ /* Send the packet */
+ return silc_packet_send_va_ext(conn->stream,
+ SILC_PACKET_PRIVATE_MESSAGE_KEY,
+ 0, 0, NULL, SILC_ID_CLIENT,
+ &client_entry->id, NULL, NULL,
+ SILC_STR_UI_SHORT(strlen(cipher)),
+ SILC_STR_DATA(cipher, strlen(cipher)),
+ SILC_STR_UI_SHORT(strlen(hmac)),
+ SILC_STR_DATA(hmac, strlen(hmac)),
+ SILC_STR_END);
+}
+
/* Client resolving callback. Here we simply mark that we are the responder
side of this private message key request. */
SilcDList clients,
void *context)
{
- SilcPacket packet = context;
+ SilcFSMThread thread = context;
+ SilcPacket packet = silc_fsm_get_state_context(thread);
unsigned char *cipher = NULL, *hmac = NULL;
SilcClientEntry client_entry;
int ret;
if (!clients) {
silc_packet_free(packet);
+ silc_fsm_finish(thread);
return;
}
client_entry->internal.prv_resp = TRUE;
/* XXX we should notify application that remote wants to set up the
- static key */
+ static key. And we should tell if we already have key with remote.
+ Application should return status telling whether to delete the key
+ or not. */
out:
silc_free(cipher);
silc_free(hmac);
silc_packet_free(packet);
+ silc_fsm_finish(thread);
}
/* Processes incoming Private Message Key payload to indicate that the
fsm));
}
-/* Adds private message key to the client library. The key will be used to
- encrypt all private message between the client and the remote client
- indicated by the `client_entry'. If the `key' is NULL and the boolean
- value `generate_key' is TRUE the library will generate random key.
- The `key' maybe for example pre-shared-key, passphrase or similar.
- The `cipher' and `hmac' MAY be provided but SHOULD be NULL to assure
- that the requirements of the SILC protocol are met. The API, however,
- allows to allocate any cipher and HMAC.
-
- If `responder' is TRUE then the sending and receiving keys will be
- set according the client being the receiver of the private key. If
- FALSE the client is being the sender (or negotiator) of the private
- key.
-
- It is not necessary to set key for normal private message usage. If the
- key is not set then the private messages are encrypted using normal
- session keys. Setting the private key, however, increases the security.
-
- Returns FALSE if the key is already set for the `client_entry', TRUE
- otherwise. */
+/* Adds new private message key to `client_entry'. If we are setting this
+ before receiving request for it from `client_entry' we will send the
+ request to the client. Otherwise, we are responder side. */
SilcBool silc_client_add_private_message_key(SilcClient client,
SilcClientConnection conn,
const char *cipher,
const char *hmac,
unsigned char *key,
- SilcUInt32 key_len,
- SilcBool generate_key,
- SilcBool responder)
+ SilcUInt32 key_len)
{
- unsigned char private_key[32];
- SilcUInt32 len;
SilcSKEKeyMaterial keymat;
SilcBool ret;
- int i;
if (!client || !client_entry)
return FALSE;
if (!silc_hmac_is_supported(hmac))
return FALSE;
- /* Generate key if not provided */
- if (generate_key == TRUE) {
- len = 32;
- for (i = 0; i < len; i++)
- private_key[i] = silc_rng_get_byte_fast(client->rng);
- key = private_key;
- key_len = len;
- }
-
/* Save the key */
client_entry->internal.key = silc_memdup(key, key_len);
client_entry->internal.key_len = key_len;
/* Set the key into use */
ret = silc_client_add_private_message_key_ske(client, conn, client_entry,
- cipher, hmac, keymat,
- responder);
-
- if (!generate_key)
- client_entry->internal.generated = FALSE;
+ cipher, hmac, keymat);
+ client_entry->internal.generated = FALSE;
/* Free the key material */
silc_ske_free_key_material(keymat);
+ /* If we are setting the key without a request from the remote client,
+ we will send request to remote. */
+ if (!client_entry->internal.prv_resp)
+ silc_client_send_private_message_key_request(client, conn, client_entry);
+
return ret;
}
/* Same as above but takes the key material from the SKE key material
- structure. This structure is received if the application uses the
- silc_client_send_key_agreement to negotiate the key material. The
- `cipher' and `hmac' SHOULD be provided as it is negotiated also in
- the SKE protocol. */
+ structure. */
SilcBool silc_client_add_private_message_key_ske(SilcClient client,
SilcClientConnection conn,
SilcClientEntry client_entry,
const char *cipher,
const char *hmac,
- SilcSKEKeyMaterial keymat,
- SilcBool responder)
+ SilcSKEKeyMaterial keymat)
{
if (!client || !client_entry)
return FALSE;
return FALSE;
/* Set the keys */
- if (responder == TRUE) {
+ if (client_entry->internal.prv_resp) {
silc_cipher_set_key(client_entry->internal.send_key,
keymat->receive_enc_key,
- keymat->enc_key_len);
+ keymat->enc_key_len, TRUE);
silc_cipher_set_iv(client_entry->internal.send_key,
keymat->receive_iv);
silc_cipher_set_key(client_entry->internal.receive_key,
keymat->send_enc_key,
- keymat->enc_key_len);
+ keymat->enc_key_len, FALSE);
silc_cipher_set_iv(client_entry->internal.receive_key, keymat->send_iv);
silc_hmac_set_key(client_entry->internal.hmac_send,
keymat->receive_hmac_key,
} else {
silc_cipher_set_key(client_entry->internal.send_key,
keymat->send_enc_key,
- keymat->enc_key_len);
+ keymat->enc_key_len, TRUE);
silc_cipher_set_iv(client_entry->internal.send_key,
keymat->send_iv);
silc_cipher_set_key(client_entry->internal.receive_key,
keymat->receive_enc_key,
- keymat->enc_key_len);
+ keymat->enc_key_len, FALSE);
silc_cipher_set_iv(client_entry->internal.receive_key, keymat->receive_iv);
silc_hmac_set_key(client_entry->internal.hmac_send,
keymat->send_hmac_key,
return TRUE;
}
-/* Sends private message key indicator. The sender of this packet is
- going to be the initiator, if and when, the users set up a static
- private message key (not Key Agreement). */
-
-SilcBool
-silc_client_send_private_message_key_request(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry client_entry)
-{
- const char *cipher, *hmac;
-
- if (!client || !conn || !client_entry)
- return FALSE;
-
- if (!client_entry->internal.send_key && !client_entry->internal.receive_key)
- return FALSE;
-
- SILC_LOG_DEBUG(("Sending private message key indicator"));
-
- cipher = silc_cipher_get_name(client_entry->internal.send_key);
- hmac = silc_hmac_get_name(client_entry->internal.hmac_send);
-
- /* Send the packet */
- return silc_packet_send_va_ext(conn->stream,
- SILC_PACKET_PRIVATE_MESSAGE_KEY,
- 0, 0, NULL, SILC_ID_CLIENT,
- &client_entry->id, NULL, NULL,
- SILC_STR_UI_SHORT(strlen(cipher)),
- SILC_STR_DATA(cipher, strlen(cipher)),
- SILC_STR_UI_SHORT(strlen(hmac)),
- SILC_STR_DATA(hmac, strlen(hmac)),
- SILC_STR_END);
-}
-
/* Removes the private message from the library. The key won't be used
after this to protect the private messages with the remote `client_entry'
client. Returns FALSE on error, TRUE otherwise. */
client_entry->internal.send_key = NULL;
client_entry->internal.receive_key = NULL;
client_entry->internal.key = NULL;
+ client_entry->internal.prv_resp = FALSE;
return TRUE;
}
silc_free(keys);
}
+/* Return private message key from the client entry. */
+
+SilcBool
+silc_client_private_message_key_is_set(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry client_entry)
+{
+ return client_entry->internal.send_key != NULL;
+}
+
/* 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
new one. If `message' is NULL the old away message is removed.
The sender may freely free the memory of the `message'. */
-void silc_client_set_away_message(SilcClient client,
- SilcClientConnection conn,
- char *message)
+SilcBool silc_client_set_away_message(SilcClient client,
+ SilcClientConnection conn,
+ char *message)
{
- assert(client && conn);
+ if (!client || !conn)
+ return FALSE;
- if (!message && conn->internal->away) {
- silc_free(conn->internal->away->away);
- silc_free(conn->internal->away);
- conn->internal->away = NULL;
+ if (!message) {
+ silc_free(conn->internal->away_message);
+ conn->internal->away_message = NULL;
+ return TRUE;
}
- if (message) {
- if (!conn->internal->away)
- conn->internal->away = silc_calloc(1, sizeof(*conn->internal->away));
- if (conn->internal->away->away)
- silc_free(conn->internal->away->away);
- conn->internal->away->away = strdup(message);
- }
+ if (conn->internal->away_message)
+ silc_free(conn->internal->away_message);
+
+ conn->internal->away_message = strdup(message);
+ if (!conn->internal->away_message)
+ return FALSE;
+
+ return TRUE;
}