SilcClientConnection conn,
SilcClientEntry client_entry,
SilcMessageFlags flags,
+ SilcHash hash,
unsigned char *data,
SilcUInt32 data_len)
{
SilcBuffer buffer;
SilcBool ret;
- SILC_LOG_DEBUG(("Sending private message"));
-
if (!client || !conn || !client_entry)
return FALSE;
+ if (flags & SILC_MESSAGE_FLAG_SIGNED && !hash)
+ return FALSE;
+ if (conn->internal->disconnected)
+ return FALSE;
+
+ SILC_LOG_DEBUG(("Sending private message"));
/* Encode private message payload */
buffer =
TRUE, client_entry->internal.send_key,
client_entry->internal.hmac_send,
client->rng, NULL, conn->private_key,
- client->sha1hash, NULL);
+ hash, NULL);
if (!buffer) {
SILC_LOG_ERROR(("Error encoding private message"));
return FALSE;
unsigned int stopped : 1;
} *SilcClientPrivateMessageWait;
-/* Client resolving callback. This continues the private message packet
- processing in the packet processor thread, which is in waiting state
- (for incoming packets) when we get here. We can safely continue in
- the thread and then return back to waiting when we do it synchronously. */
+/* Client resolving callback. Continues with the private message processing */
static void silc_client_private_message_resolved(SilcClient client,
SilcClientConnection conn,
SilcDList clients,
void *context)
{
- if (!clients) {
- silc_packet_free(context);
- return;
- }
+ /* If no client found, ignore the private message, a silent error */
+ if (!clients)
+ silc_fsm_next(context, silc_client_private_message_error);
/* Continue processing the private message packet */
- silc_fsm_set_state_context(&conn->internal->packet_thread, context);
- silc_fsm_next(&conn->internal->packet_thread, silc_client_private_message);
- silc_fsm_continue_sync(&conn->internal->packet_thread);
+ SILC_FSM_CALL_CONTINUE(context);
}
/* Private message received. */
SilcPacket packet = state_context;
SilcMessagePayload payload = NULL;
SilcClientID remote_id;
- SilcClientEntry remote_client;
+ SilcClientEntry remote_client = NULL;
SilcMessageFlags flags;
unsigned char *message;
SilcUInt32 message_len;
SilcClientPrivateMessageWait pmw;
- if (packet->src_id_type != SILC_ID_CLIENT)
- goto out;
+ SILC_LOG_DEBUG(("Received private message"));
+
+ if (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)))
- goto out;
+ &remote_id, sizeof(remote_id))) {
+ /** Invalid source ID */
+ silc_fsm_next(fsm, silc_client_private_message_error);
+ return SILC_FSM_CONTINUE;
+ }
/* Check whether we know this client already */
remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
- if (!remote_client || !remote_client->nickname) {
- /* Resolve the client info. We return back to packet thread to receive
- other packets while we wait for the resolving to finish. */
- silc_client_get_client_by_id_resolve(client, conn, &remote_id, NULL,
+ if (!remote_client || !remote_client->nickname[0]) {
+ /** Resolve client info */
+ silc_client_unref_client(client, conn, remote_client);
+ SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
+ client, conn, &remote_id, NULL,
silc_client_private_message_resolved,
- packet);
- silc_fsm_next(fsm, silc_client_connection_st_packet);
- return SILC_FSM_CONTINUE;
+ fsm));
+ /* NOT REACHED */
}
if (packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY &&
/* Send the away message */
silc_client_send_private_message(client, conn, remote_client,
SILC_MESSAGE_FLAG_AUTOREPLY |
- SILC_MESSAGE_FLAG_NOREPLY,
+ SILC_MESSAGE_FLAG_NOREPLY, NULL,
conn->internal->away->away,
strlen(conn->internal->away->away));
}
out:
/** Packet processed */
+ silc_packet_free(packet);
+ silc_client_unref_client(client, conn, remote_client);
if (payload)
silc_message_payload_free(payload);
+ return SILC_FSM_FINISH;
+}
+
+/* Private message error. */
+
+SILC_FSM_STATE(silc_client_private_message_error)
+{
+ SilcPacket packet = state_context;
silc_packet_free(packet);
- silc_fsm_next(fsm, silc_client_connection_st_packet);
- return SILC_FSM_CONTINUE;
+ return SILC_FSM_FINISH;
}
#if 0 /* XXX we need to rethink this */
/*************************** 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. */
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);
if (packet->src_id_type != SILC_ID_CLIENT) {
silc_packet_free(packet);
- goto out;
+ return SILC_FSM_FINISH;
}
if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
&remote_id, sizeof(remote_id))) {
silc_packet_free(packet);
- goto out;
+ return SILC_FSM_FINISH;
}
/* Always resolve the remote client. The actual packet is processed
in the resolving callback. */
- silc_client_get_client_by_id_resolve(client, conn, &remote_id, NULL,
+ SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
+ client, conn, &remote_id, NULL,
silc_client_private_message_key_cb,
- packet);
-
- out:
- silc_fsm_next(fsm, silc_client_connection_st_packet);
- return SILC_FSM_CONTINUE;
+ 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;
/* Produce the key material as the protocol defines */
keymat = silc_ske_process_key_material_data(key, key_len, 16, 256, 16,
- client->sha1hash);
+ conn->internal->sha1hash);
if (!keymat)
return FALSE;
/* 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);
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)
-{
- SilcBufferStruct buffer;
- int cipher_len, hmac_len;
- const char *cipher, *hmac;
- SilcBool ret;
-
- 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);
- cipher_len = strlen(cipher);
- hmac = silc_hmac_get_name(client_entry->internal.hmac_send);
- hmac_len = strlen(hmac);
-
- /* Create private message key payload */
- memset(&buffer, 0, sizeof(buffer));
- if (silc_buffer_format(&buffer,
- SILC_STR_UI_SHORT(cipher_len),
- SILC_STR_UI_XNSTRING(cipher,
- cipher_len),
- SILC_STR_UI_SHORT(hmac_len),
- SILC_STR_UI_XNSTRING(hmac,
- hmac_len),
- SILC_STR_END) < 0)
- return FALSE;
-
- /* Send the packet */
- ret = silc_packet_send_ext(conn->stream, SILC_PACKET_PRIVATE_MESSAGE_KEY,
- 0, 0, NULL, SILC_ID_CLIENT, &client_entry->id,
- silc_buffer_datalen(&buffer), NULL, NULL);
- silc_buffer_purge(&buffer);
-
- return ret;
-}
-
/* 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;
}
if (!client || !conn)
return NULL;
- if (!silc_idcache_get_all(conn->internal->client_cache, &list))
+ silc_mutex_lock(conn->internal->lock);
+ if (!silc_idcache_get_all(conn->internal->client_cache, &list)) {
+ silc_mutex_unlock(conn->internal->lock);
return NULL;
+ }
keys = silc_calloc(silc_list_count(list), sizeof(*keys));
- if (!keys)
+ if (!keys) {
+ silc_mutex_unlock(conn->internal->lock);
return NULL;
+ }
silc_list_start(list);
while ((id_cache = silc_list_get(list))) {
}
}
+ silc_mutex_unlock(conn->internal->lock);
+
if (key_count)
*key_count = count;