5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2006 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
22 #include "silcclient.h"
23 #include "client_internal.h"
25 /************************** Private Message Send ****************************/
27 /* Sends private message to remote client. */
29 SilcBool silc_client_send_private_message(SilcClient client,
30 SilcClientConnection conn,
31 SilcClientEntry client_entry,
32 SilcMessageFlags flags,
40 if (!client || !conn || !client_entry)
42 if (flags & SILC_MESSAGE_FLAG_SIGNED && !hash)
45 SILC_LOG_DEBUG(("Sending private message"));
47 /* Encode private message payload */
49 silc_message_payload_encode(flags, data, data_len,
50 (!client_entry->internal.send_key ? FALSE :
51 !client_entry->internal.generated),
52 TRUE, client_entry->internal.send_key,
53 client_entry->internal.hmac_send,
54 client->rng, NULL, conn->private_key,
57 SILC_LOG_ERROR(("Error encoding private message"));
61 /* Send the private message packet */
62 ret = silc_packet_send_ext(conn->stream, SILC_PACKET_PRIVATE_MESSAGE,
63 client_entry->internal.send_key ?
64 SILC_PACKET_FLAG_PRIVMSG_KEY : 0,
65 0, NULL, SILC_ID_CLIENT, &client_entry->id,
66 silc_buffer_datalen(buffer), NULL, NULL);
68 silc_buffer_free(buffer);
72 /************************* Private Message Receive **************************/
74 /* Private message waiting context */
78 SilcDList message_queue;
79 unsigned int stopped : 1;
80 } *SilcClientPrivateMessageWait;
82 /* Client resolving callback. Continues with the private message processing */
84 static void silc_client_private_message_resolved(SilcClient client,
85 SilcClientConnection conn,
90 /* If no client found, ignore the private message, a silent error */
92 silc_fsm_next(context, silc_client_private_message_error);
94 /* Continue processing the private message packet */
95 SILC_FSM_CALL_CONTINUE(context);
98 /* Private message received. */
100 SILC_FSM_STATE(silc_client_private_message)
102 SilcClientConnection conn = fsm_context;
103 SilcClient client = conn->client;
104 SilcPacket packet = state_context;
105 SilcMessagePayload payload = NULL;
106 SilcClientID remote_id;
107 SilcClientEntry remote_client = NULL;
108 SilcMessageFlags flags;
109 unsigned char *message;
110 SilcUInt32 message_len;
111 SilcClientPrivateMessageWait pmw;
113 SILC_LOG_DEBUG(("Received private message"));
115 if (packet->src_id_type != SILC_ID_CLIENT) {
116 /** Invalid packet */
117 silc_fsm_next(fsm, silc_client_private_message_error);
118 return SILC_FSM_CONTINUE;
121 if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
122 &remote_id, sizeof(remote_id))) {
123 /** Invalid source ID */
124 silc_fsm_next(fsm, silc_client_private_message_error);
125 return SILC_FSM_CONTINUE;
128 /* Check whether we know this client already */
129 remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
130 if (!remote_client || !remote_client->nickname[0]) {
131 /** Resolve client info */
132 silc_client_unref_client(client, conn, remote_client);
133 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
134 client, conn, &remote_id, NULL,
135 silc_client_private_message_resolved,
140 if (packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY &&
141 !remote_client->internal.receive_key &&
142 !remote_client->internal.hmac_receive)
145 /* Parse the payload and decrypt it also if private message key is set */
147 silc_message_payload_parse(silc_buffer_datalen(&packet->buffer),
148 TRUE, !remote_client->internal.generated,
149 remote_client->internal.receive_key,
150 remote_client->internal.hmac_receive,
155 #if 0 /* We need to rethink this. This doesn't work with multiple
156 waiters, and performance is suboptimal. */
157 /* Check if some thread is waiting for this private message */
158 silc_mutex_lock(conn->internal->lock);
159 if (conn->internal->privmsg_wait &&
160 silc_hash_table_find_ext(conn->internal->privmsg_wait,
161 &remote_client->id, NULL, (void **)&pmw,
162 NULL, NULL, silc_hash_id_compare_full,
163 SILC_32_TO_PTR(SILC_ID_CLIENT))) {
164 /* Signal that message was received */
165 silc_mutex_unlock(conn->internal->lock);
166 silc_mutex_lock(pmw->wait_lock);
168 silc_dlist_add(pmw->message_queue, payload);
169 silc_cond_broadcast(pmw->wait_cond);
170 silc_mutex_unlock(pmw->wait_lock);
171 silc_packet_free(packet);
174 silc_mutex_unlock(pmw->wait_lock);
176 silc_mutex_unlock(conn->internal->lock);
179 /* Pass the private message to application */
180 flags = silc_message_get_flags(payload);
181 message = silc_message_get_data(payload, &message_len);
182 client->internal->ops->private_message(client, conn, remote_client, payload,
183 flags, message, message_len);
185 /* See if we are away (gone). If we are away we will reply to the
186 sender with the set away message. */
187 if (conn->internal->away && conn->internal->away->away &&
188 !(flags & SILC_MESSAGE_FLAG_NOREPLY)) {
189 /* If it's me, ignore */
190 if (SILC_ID_CLIENT_COMPARE(&remote_id, conn->local_id))
193 /* Send the away message */
194 silc_client_send_private_message(client, conn, remote_client,
195 SILC_MESSAGE_FLAG_AUTOREPLY |
196 SILC_MESSAGE_FLAG_NOREPLY, NULL,
197 conn->internal->away->away,
198 strlen(conn->internal->away->away));
202 /** Packet processed */
203 silc_packet_free(packet);
204 silc_client_unref_client(client, conn, remote_client);
206 silc_message_payload_free(payload);
207 return SILC_FSM_FINISH;
210 /* Private message error. */
212 SILC_FSM_STATE(silc_client_private_message_error)
214 SilcPacket packet = state_context;
215 silc_packet_free(packet);
216 return SILC_FSM_FINISH;
219 #if 0 /* XXX we need to rethink this */
220 /* Initialize private message waiting in a thread. */
222 void *silc_client_private_message_wait_init(SilcClientConnection conn,
223 SilcClientEntry client_entry)
225 SilcClientPrivateMessageWait pmw;
227 pmw = silc_calloc(1, sizeof(*pmw));
231 pmw->message_queue = silc_dlist_init();
232 if (!pmw->message_queue) {
237 /* Allocate mutex and conditional variable */
238 if (!silc_mutex_alloc(&pmw->wait_lock)) {
239 silc_dlist_uninit(pmw->message_queue);
243 if (!silc_cond_alloc(&pmw->wait_cond)) {
244 silc_dlist_uninit(pmw->message_queue);
245 silc_mutex_free(pmw->wait_lock);
250 silc_mutex_lock(conn->internal->lock);
252 /* Allocate waiting hash table */
253 if (!conn->internal->privmsg_wait) {
254 conn->internal->privmsg_wait =
255 silc_hash_table_alloc(0, silc_hash_id,
256 SILC_32_TO_PTR(SILC_ID_CLIENT),
257 silc_hash_id_compare,
258 SILC_32_TO_PTR(SILC_ID_CLIENT), NULL, NULL, TRUE);
259 if (!conn->internal->privmsg_wait) {
260 silc_mutex_unlock(conn->internal->lock);
261 silc_dlist_uninit(pmw->message_queue);
262 silc_mutex_free(pmw->wait_lock);
263 silc_cond_free(pmw->wait_cond);
269 /* Add to waiting hash table */
270 silc_hash_table_add(conn->internal->privmsg_wait, client_entry->id, pmw);
272 silc_mutex_unlock(conn->internal->lock);
277 /* Uninitialize private message waiting. */
279 void silc_client_private_message_wait_uninit(SilcClientConnection conn,
280 SilcClientEntry client_entry,
283 SilcClientPrivateMessageWait pmw = waiter;
284 SilcMessagePayload payload;
286 /* Signal any threads to stop waiting */
287 silc_mutex_lock(pmw->wait_lock);
289 silc_cond_broadcast(pmw->wait_cond);
290 silc_mutex_unlock(pmw->wait_lock);
292 /* Re-acquire lock and free resources */
293 silc_mutex_lock(pmw->wait_lock);
295 /* Free any remaining message */
296 silc_dlist_start(pmw->message_queue);
297 while ((payload = silc_dlist_get(pmw->message_queue)))
298 silc_message_payload_free(payload);
300 silc_dlist_uninit(pmw->message_queue);
301 silc_cond_free(pmw->wait_cond);
302 silc_mutex_unlock(pmw->wait_lock);
303 silc_mutex_free(pmw->wait_lock);
305 silc_mutex_lock(conn->internal->lock);
306 silc_hash_table_del_by_context(conn->internal->privmsg_wait,
307 client_entry->id, pmw);
308 silc_mutex_unlock(conn->internal->lock);
313 /* Blocks the calling process or thread until a private message has been
314 received from the specified client. */
316 SilcBool silc_client_private_message_wait(SilcClientConnection conn,
317 SilcClientEntry client_entry,
319 SilcMessagePayload *payload)
321 SilcClientPrivateMessageWait pmw = waiter;
324 silc_mutex_lock(pmw->wait_lock);
326 /* Wait here until private message has been received */
327 while (silc_dlist_count(pmw->message_queue) == 0) {
329 silc_mutex_unlock(pmw->wait_lock);
332 silc_cond_wait(pmw->wait_cond, pmw->wait_lock);
336 silc_dlist_start(pmw->message_queue);
337 *payload = silc_dlist_get(pmw->message_queue);
338 silc_dlist_del(pmw->message_queue, *payload);
340 silc_mutex_unlock(pmw->wait_lock);
346 /*************************** Private Message Key ****************************/
348 /* Sends private message key request. Sender of this packet is initiator
349 when setting the private message key. */
352 silc_client_send_private_message_key_request(SilcClient client,
353 SilcClientConnection conn,
354 SilcClientEntry client_entry)
356 const char *cipher, *hmac;
358 SILC_LOG_DEBUG(("Sending private message key request"));
360 cipher = silc_cipher_get_name(client_entry->internal.send_key);
361 hmac = silc_hmac_get_name(client_entry->internal.hmac_send);
363 /* Send the packet */
364 return silc_packet_send_va_ext(conn->stream,
365 SILC_PACKET_PRIVATE_MESSAGE_KEY,
366 0, 0, NULL, SILC_ID_CLIENT,
367 &client_entry->id, NULL, NULL,
368 SILC_STR_UI_SHORT(strlen(cipher)),
369 SILC_STR_DATA(cipher, strlen(cipher)),
370 SILC_STR_UI_SHORT(strlen(hmac)),
371 SILC_STR_DATA(hmac, strlen(hmac)),
375 /* Client resolving callback. Here we simply mark that we are the responder
376 side of this private message key request. */
378 static void silc_client_private_message_key_cb(SilcClient client,
379 SilcClientConnection conn,
384 SilcPacket packet = context;
385 unsigned char *cipher = NULL, *hmac = NULL;
386 SilcClientEntry client_entry;
390 silc_packet_free(packet);
394 /* Parse the private message key payload */
395 ret = silc_buffer_unformat(&packet->buffer,
396 SILC_STR_UI16_STRING_ALLOC(&cipher),
397 SILC_STR_UI16_STRING_ALLOC(&hmac),
402 /* Mark that we are responder */
403 client_entry = silc_dlist_get(clients);
404 client_entry->internal.prv_resp = TRUE;
406 /* XXX we should notify application that remote wants to set up the
407 static key. And we should tell if we already have key with remote.
408 Application should return status telling whether to delete the key
414 silc_packet_free(packet);
417 /* Processes incoming Private Message Key payload to indicate that the
418 sender whishes to set up a static private message key. */
420 SILC_FSM_STATE(silc_client_private_message_key)
422 SilcClientConnection conn = fsm_context;
423 SilcClient client = conn->client;
424 SilcPacket packet = state_context;
425 SilcClientID remote_id;
427 if (packet->src_id_type != SILC_ID_CLIENT) {
428 silc_packet_free(packet);
429 return SILC_FSM_FINISH;
432 if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
433 &remote_id, sizeof(remote_id))) {
434 silc_packet_free(packet);
435 return SILC_FSM_FINISH;
438 /* Always resolve the remote client. The actual packet is processed
439 in the resolving callback. */
440 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
441 client, conn, &remote_id, NULL,
442 silc_client_private_message_key_cb,
446 /* Adds new private message key to `client_entry'. If we are setting this
447 before receiving request for it from `client_entry' we will send the
448 request to the client. Otherwise, we are responder side. */
450 SilcBool silc_client_add_private_message_key(SilcClient client,
451 SilcClientConnection conn,
452 SilcClientEntry client_entry,
458 SilcSKEKeyMaterial keymat;
461 if (!client || !client_entry)
464 /* Return FALSE if key already set */
465 if (client_entry->internal.send_key && client_entry->internal.receive_key)
469 cipher = SILC_DEFAULT_CIPHER;
471 hmac = SILC_DEFAULT_HMAC;
473 /* Check the requested cipher and HMAC */
474 if (!silc_cipher_is_supported(cipher))
476 if (!silc_hmac_is_supported(hmac))
480 client_entry->internal.key = silc_memdup(key, key_len);
481 client_entry->internal.key_len = key_len;
483 /* Produce the key material as the protocol defines */
484 keymat = silc_ske_process_key_material_data(key, key_len, 16, 256, 16,
485 conn->internal->sha1hash);
489 /* Set the key into use */
490 ret = silc_client_add_private_message_key_ske(client, conn, client_entry,
491 cipher, hmac, keymat);
492 client_entry->internal.generated = FALSE;
494 /* Free the key material */
495 silc_ske_free_key_material(keymat);
497 /* If we are setting the key without a request from the remote client,
498 we will send request to remote. */
499 if (!client_entry->internal.prv_resp)
500 silc_client_send_private_message_key_request(client, conn, client_entry);
505 /* Same as above but takes the key material from the SKE key material
508 SilcBool silc_client_add_private_message_key_ske(SilcClient client,
509 SilcClientConnection conn,
510 SilcClientEntry client_entry,
513 SilcSKEKeyMaterial keymat)
515 if (!client || !client_entry)
518 /* Return FALSE if key already set */
519 if (client_entry->internal.send_key && client_entry->internal.receive_key)
523 cipher = SILC_DEFAULT_CIPHER;
525 hmac = SILC_DEFAULT_HMAC;
527 /* Check the requested cipher and HMAC */
528 if (!silc_cipher_is_supported(cipher))
530 if (!silc_hmac_is_supported(hmac))
533 client_entry->internal.generated = TRUE;
535 /* Allocate the cipher and HMAC */
536 if (!silc_cipher_alloc(cipher, &client_entry->internal.send_key))
538 if (!silc_cipher_alloc(cipher, &client_entry->internal.receive_key))
540 if (!silc_hmac_alloc(hmac, NULL, &client_entry->internal.hmac_send))
542 if (!silc_hmac_alloc(hmac, NULL, &client_entry->internal.hmac_receive))
546 if (client_entry->internal.prv_resp) {
547 silc_cipher_set_key(client_entry->internal.send_key,
548 keymat->receive_enc_key,
549 keymat->enc_key_len);
550 silc_cipher_set_iv(client_entry->internal.send_key,
552 silc_cipher_set_key(client_entry->internal.receive_key,
553 keymat->send_enc_key,
554 keymat->enc_key_len);
555 silc_cipher_set_iv(client_entry->internal.receive_key, keymat->send_iv);
556 silc_hmac_set_key(client_entry->internal.hmac_send,
557 keymat->receive_hmac_key,
558 keymat->hmac_key_len);
559 silc_hmac_set_key(client_entry->internal.hmac_receive,
560 keymat->send_hmac_key,
561 keymat->hmac_key_len);
563 silc_cipher_set_key(client_entry->internal.send_key,
564 keymat->send_enc_key,
565 keymat->enc_key_len);
566 silc_cipher_set_iv(client_entry->internal.send_key,
568 silc_cipher_set_key(client_entry->internal.receive_key,
569 keymat->receive_enc_key,
570 keymat->enc_key_len);
571 silc_cipher_set_iv(client_entry->internal.receive_key, keymat->receive_iv);
572 silc_hmac_set_key(client_entry->internal.hmac_send,
573 keymat->send_hmac_key,
574 keymat->hmac_key_len);
575 silc_hmac_set_key(client_entry->internal.hmac_receive,
576 keymat->receive_hmac_key,
577 keymat->hmac_key_len);
583 /* Removes the private message from the library. The key won't be used
584 after this to protect the private messages with the remote `client_entry'
585 client. Returns FALSE on error, TRUE otherwise. */
587 SilcBool silc_client_del_private_message_key(SilcClient client,
588 SilcClientConnection conn,
589 SilcClientEntry client_entry)
591 if (!client || !client_entry)
594 if (!client_entry->internal.send_key && !client_entry->internal.receive_key)
597 silc_cipher_free(client_entry->internal.send_key);
598 silc_cipher_free(client_entry->internal.receive_key);
600 if (client_entry->internal.key) {
601 memset(client_entry->internal.key, 0, client_entry->internal.key_len);
602 silc_free(client_entry->internal.key);
605 client_entry->internal.send_key = NULL;
606 client_entry->internal.receive_key = NULL;
607 client_entry->internal.key = NULL;
608 client_entry->internal.prv_resp = FALSE;
613 /* Returns array of set private message keys associated to the connection
614 `conn'. Returns allocated SilcPrivateMessageKeys array and the array
615 count to the `key_count' argument. The array must be freed by the caller
616 by calling the silc_client_free_private_message_keys function. Note:
617 the keys returned in the array is in raw format. It might not be desired
618 to show the keys as is. The application might choose not to show the keys
619 at all or to show the fingerprints of the keys. */
621 SilcPrivateMessageKeys
622 silc_client_list_private_message_keys(SilcClient client,
623 SilcClientConnection conn,
624 SilcUInt32 *key_count)
626 SilcPrivateMessageKeys keys;
627 SilcUInt32 count = 0;
629 SilcIDCacheEntry id_cache;
630 SilcClientEntry entry;
632 if (!client || !conn)
635 silc_mutex_lock(conn->internal->lock);
636 if (!silc_idcache_get_all(conn->internal->client_cache, &list)) {
637 silc_mutex_unlock(conn->internal->lock);
641 keys = silc_calloc(silc_list_count(list), sizeof(*keys));
643 silc_mutex_unlock(conn->internal->lock);
647 silc_list_start(list);
648 while ((id_cache = silc_list_get(list))) {
649 entry = id_cache->context;
650 if (entry->internal.send_key) {
651 keys[count].client_entry = entry;
652 keys[count].cipher = (char *)silc_cipher_get_name(entry->internal.
654 keys[count].key = (entry->internal.generated == FALSE ?
655 entry->internal.key : NULL);
656 keys[count].key_len = (entry->internal.generated == FALSE ?
657 entry->internal.key_len : 0);
662 silc_mutex_unlock(conn->internal->lock);
670 /* Frees the SilcPrivateMessageKeys array returned by the function
671 silc_client_list_private_message_keys. */
673 void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
674 SilcUInt32 key_count)
679 /* Sets away `message'. The away message may be set when the client's
680 mode is changed to SILC_UMODE_GONE and the client whishes to reply
681 to anyone who sends private message. The `message' will be sent
682 automatically back to the the client who send private message. If
683 away message is already set this replaces the old message with the
684 new one. If `message' is NULL the old away message is removed.
685 The sender may freely free the memory of the `message'. */
687 void silc_client_set_away_message(SilcClient client,
688 SilcClientConnection conn,
691 assert(client && conn);
693 if (!message && conn->internal->away) {
694 silc_free(conn->internal->away->away);
695 silc_free(conn->internal->away);
696 conn->internal->away = NULL;
700 if (!conn->internal->away)
701 conn->internal->away = silc_calloc(1, sizeof(*conn->internal->away));
702 if (conn->internal->away->away)
703 silc_free(conn->internal->away->away);
704 conn->internal->away->away = strdup(message);