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)
44 if (conn->internal->disconnected)
47 SILC_LOG_DEBUG(("Sending private message"));
49 /* Encode private message payload */
51 silc_message_payload_encode(flags, data, data_len,
52 (!client_entry->internal.send_key ? FALSE :
53 !client_entry->internal.generated),
54 TRUE, client_entry->internal.send_key,
55 client_entry->internal.hmac_send,
56 client->rng, NULL, conn->private_key,
59 SILC_LOG_ERROR(("Error encoding private message"));
63 /* Send the private message packet */
64 ret = silc_packet_send_ext(conn->stream, SILC_PACKET_PRIVATE_MESSAGE,
65 client_entry->internal.send_key ?
66 SILC_PACKET_FLAG_PRIVMSG_KEY : 0,
67 0, NULL, SILC_ID_CLIENT, &client_entry->id,
68 silc_buffer_datalen(buffer), NULL, NULL);
70 silc_buffer_free(buffer);
74 /************************* Private Message Receive **************************/
76 /* Private message waiting context */
80 SilcDList message_queue;
81 unsigned int stopped : 1;
82 } *SilcClientPrivateMessageWait;
84 /* Client resolving callback. Continues with the private message processing */
86 static void silc_client_private_message_resolved(SilcClient client,
87 SilcClientConnection conn,
92 /* If no client found, ignore the private message, a silent error */
94 silc_fsm_next(context, silc_client_private_message_error);
96 /* Continue processing the private message packet */
97 SILC_FSM_CALL_CONTINUE(context);
100 /* Private message received. */
102 SILC_FSM_STATE(silc_client_private_message)
104 SilcClientConnection conn = fsm_context;
105 SilcClient client = conn->client;
106 SilcPacket packet = state_context;
107 SilcMessagePayload payload = NULL;
108 SilcClientID remote_id;
109 SilcClientEntry remote_client = NULL;
110 SilcMessageFlags flags;
111 unsigned char *message;
112 SilcUInt32 message_len;
113 SilcClientPrivateMessageWait pmw;
115 SILC_LOG_DEBUG(("Received private message"));
117 if (packet->src_id_type != SILC_ID_CLIENT) {
118 /** Invalid packet */
119 silc_fsm_next(fsm, silc_client_private_message_error);
120 return SILC_FSM_CONTINUE;
123 if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
124 &remote_id, sizeof(remote_id))) {
125 /** Invalid source ID */
126 silc_fsm_next(fsm, silc_client_private_message_error);
127 return SILC_FSM_CONTINUE;
130 /* Check whether we know this client already */
131 remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
132 if (!remote_client || !remote_client->nickname[0]) {
133 /** Resolve client info */
134 silc_client_unref_client(client, conn, remote_client);
135 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
136 client, conn, &remote_id, NULL,
137 silc_client_private_message_resolved,
142 if (packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY &&
143 !remote_client->internal.receive_key &&
144 !remote_client->internal.hmac_receive)
147 /* Parse the payload and decrypt it also if private message key is set */
149 silc_message_payload_parse(silc_buffer_datalen(&packet->buffer),
150 TRUE, !remote_client->internal.generated,
151 remote_client->internal.receive_key,
152 remote_client->internal.hmac_receive,
157 #if 0 /* We need to rethink this. This doesn't work with multiple
158 waiters, and performance is suboptimal. */
159 /* Check if some thread is waiting for this private message */
160 silc_mutex_lock(conn->internal->lock);
161 if (conn->internal->privmsg_wait &&
162 silc_hash_table_find_ext(conn->internal->privmsg_wait,
163 &remote_client->id, NULL, (void **)&pmw,
164 NULL, NULL, silc_hash_id_compare_full,
165 SILC_32_TO_PTR(SILC_ID_CLIENT))) {
166 /* Signal that message was received */
167 silc_mutex_unlock(conn->internal->lock);
168 silc_mutex_lock(pmw->wait_lock);
170 silc_dlist_add(pmw->message_queue, payload);
171 silc_cond_broadcast(pmw->wait_cond);
172 silc_mutex_unlock(pmw->wait_lock);
173 silc_packet_free(packet);
176 silc_mutex_unlock(pmw->wait_lock);
178 silc_mutex_unlock(conn->internal->lock);
181 /* Pass the private message to application */
182 flags = silc_message_get_flags(payload);
183 message = silc_message_get_data(payload, &message_len);
184 client->internal->ops->private_message(client, conn, remote_client, payload,
185 flags, message, message_len);
187 /* See if we are away (gone). If we are away we will reply to the
188 sender with the set away message. */
189 if (conn->internal->away && conn->internal->away->away &&
190 !(flags & SILC_MESSAGE_FLAG_NOREPLY)) {
191 /* If it's me, ignore */
192 if (SILC_ID_CLIENT_COMPARE(&remote_id, conn->local_id))
195 /* Send the away message */
196 silc_client_send_private_message(client, conn, remote_client,
197 SILC_MESSAGE_FLAG_AUTOREPLY |
198 SILC_MESSAGE_FLAG_NOREPLY, NULL,
199 conn->internal->away->away,
200 strlen(conn->internal->away->away));
204 /** Packet processed */
205 silc_packet_free(packet);
206 silc_client_unref_client(client, conn, remote_client);
208 silc_message_payload_free(payload);
209 return SILC_FSM_FINISH;
212 /* Private message error. */
214 SILC_FSM_STATE(silc_client_private_message_error)
216 SilcPacket packet = state_context;
217 silc_packet_free(packet);
218 return SILC_FSM_FINISH;
221 #if 0 /* XXX we need to rethink this */
222 /* Initialize private message waiting in a thread. */
224 void *silc_client_private_message_wait_init(SilcClientConnection conn,
225 SilcClientEntry client_entry)
227 SilcClientPrivateMessageWait pmw;
229 pmw = silc_calloc(1, sizeof(*pmw));
233 pmw->message_queue = silc_dlist_init();
234 if (!pmw->message_queue) {
239 /* Allocate mutex and conditional variable */
240 if (!silc_mutex_alloc(&pmw->wait_lock)) {
241 silc_dlist_uninit(pmw->message_queue);
245 if (!silc_cond_alloc(&pmw->wait_cond)) {
246 silc_dlist_uninit(pmw->message_queue);
247 silc_mutex_free(pmw->wait_lock);
252 silc_mutex_lock(conn->internal->lock);
254 /* Allocate waiting hash table */
255 if (!conn->internal->privmsg_wait) {
256 conn->internal->privmsg_wait =
257 silc_hash_table_alloc(0, silc_hash_id,
258 SILC_32_TO_PTR(SILC_ID_CLIENT),
259 silc_hash_id_compare,
260 SILC_32_TO_PTR(SILC_ID_CLIENT), NULL, NULL, TRUE);
261 if (!conn->internal->privmsg_wait) {
262 silc_mutex_unlock(conn->internal->lock);
263 silc_dlist_uninit(pmw->message_queue);
264 silc_mutex_free(pmw->wait_lock);
265 silc_cond_free(pmw->wait_cond);
271 /* Add to waiting hash table */
272 silc_hash_table_add(conn->internal->privmsg_wait, client_entry->id, pmw);
274 silc_mutex_unlock(conn->internal->lock);
279 /* Uninitialize private message waiting. */
281 void silc_client_private_message_wait_uninit(SilcClientConnection conn,
282 SilcClientEntry client_entry,
285 SilcClientPrivateMessageWait pmw = waiter;
286 SilcMessagePayload payload;
288 /* Signal any threads to stop waiting */
289 silc_mutex_lock(pmw->wait_lock);
291 silc_cond_broadcast(pmw->wait_cond);
292 silc_mutex_unlock(pmw->wait_lock);
294 /* Re-acquire lock and free resources */
295 silc_mutex_lock(pmw->wait_lock);
297 /* Free any remaining message */
298 silc_dlist_start(pmw->message_queue);
299 while ((payload = silc_dlist_get(pmw->message_queue)))
300 silc_message_payload_free(payload);
302 silc_dlist_uninit(pmw->message_queue);
303 silc_cond_free(pmw->wait_cond);
304 silc_mutex_unlock(pmw->wait_lock);
305 silc_mutex_free(pmw->wait_lock);
307 silc_mutex_lock(conn->internal->lock);
308 silc_hash_table_del_by_context(conn->internal->privmsg_wait,
309 client_entry->id, pmw);
310 silc_mutex_unlock(conn->internal->lock);
315 /* Blocks the calling process or thread until a private message has been
316 received from the specified client. */
318 SilcBool silc_client_private_message_wait(SilcClientConnection conn,
319 SilcClientEntry client_entry,
321 SilcMessagePayload *payload)
323 SilcClientPrivateMessageWait pmw = waiter;
326 silc_mutex_lock(pmw->wait_lock);
328 /* Wait here until private message has been received */
329 while (silc_dlist_count(pmw->message_queue) == 0) {
331 silc_mutex_unlock(pmw->wait_lock);
334 silc_cond_wait(pmw->wait_cond, pmw->wait_lock);
338 silc_dlist_start(pmw->message_queue);
339 *payload = silc_dlist_get(pmw->message_queue);
340 silc_dlist_del(pmw->message_queue, *payload);
342 silc_mutex_unlock(pmw->wait_lock);
348 /*************************** Private Message Key ****************************/
350 /* Sends private message key request. Sender of this packet is initiator
351 when setting the private message key. */
354 silc_client_send_private_message_key_request(SilcClient client,
355 SilcClientConnection conn,
356 SilcClientEntry client_entry)
358 const char *cipher, *hmac;
360 SILC_LOG_DEBUG(("Sending private message key request"));
362 cipher = silc_cipher_get_name(client_entry->internal.send_key);
363 hmac = silc_hmac_get_name(client_entry->internal.hmac_send);
365 /* Send the packet */
366 return silc_packet_send_va_ext(conn->stream,
367 SILC_PACKET_PRIVATE_MESSAGE_KEY,
368 0, 0, NULL, SILC_ID_CLIENT,
369 &client_entry->id, NULL, NULL,
370 SILC_STR_UI_SHORT(strlen(cipher)),
371 SILC_STR_DATA(cipher, strlen(cipher)),
372 SILC_STR_UI_SHORT(strlen(hmac)),
373 SILC_STR_DATA(hmac, strlen(hmac)),
377 /* Client resolving callback. Here we simply mark that we are the responder
378 side of this private message key request. */
380 static void silc_client_private_message_key_cb(SilcClient client,
381 SilcClientConnection conn,
386 SilcPacket packet = context;
387 unsigned char *cipher = NULL, *hmac = NULL;
388 SilcClientEntry client_entry;
392 silc_packet_free(packet);
396 /* Parse the private message key payload */
397 ret = silc_buffer_unformat(&packet->buffer,
398 SILC_STR_UI16_STRING_ALLOC(&cipher),
399 SILC_STR_UI16_STRING_ALLOC(&hmac),
404 /* Mark that we are responder */
405 client_entry = silc_dlist_get(clients);
406 client_entry->internal.prv_resp = TRUE;
408 /* XXX we should notify application that remote wants to set up the
409 static key. And we should tell if we already have key with remote.
410 Application should return status telling whether to delete the key
416 silc_packet_free(packet);
419 /* Processes incoming Private Message Key payload to indicate that the
420 sender whishes to set up a static private message key. */
422 SILC_FSM_STATE(silc_client_private_message_key)
424 SilcClientConnection conn = fsm_context;
425 SilcClient client = conn->client;
426 SilcPacket packet = state_context;
427 SilcClientID remote_id;
429 if (packet->src_id_type != SILC_ID_CLIENT) {
430 silc_packet_free(packet);
431 return SILC_FSM_FINISH;
434 if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
435 &remote_id, sizeof(remote_id))) {
436 silc_packet_free(packet);
437 return SILC_FSM_FINISH;
440 /* Always resolve the remote client. The actual packet is processed
441 in the resolving callback. */
442 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
443 client, conn, &remote_id, NULL,
444 silc_client_private_message_key_cb,
448 /* Adds new private message key to `client_entry'. If we are setting this
449 before receiving request for it from `client_entry' we will send the
450 request to the client. Otherwise, we are responder side. */
452 SilcBool silc_client_add_private_message_key(SilcClient client,
453 SilcClientConnection conn,
454 SilcClientEntry client_entry,
460 SilcSKEKeyMaterial keymat;
463 if (!client || !client_entry)
466 /* Return FALSE if key already set */
467 if (client_entry->internal.send_key && client_entry->internal.receive_key)
471 cipher = SILC_DEFAULT_CIPHER;
473 hmac = SILC_DEFAULT_HMAC;
475 /* Check the requested cipher and HMAC */
476 if (!silc_cipher_is_supported(cipher))
478 if (!silc_hmac_is_supported(hmac))
482 client_entry->internal.key = silc_memdup(key, key_len);
483 client_entry->internal.key_len = key_len;
485 /* Produce the key material as the protocol defines */
486 keymat = silc_ske_process_key_material_data(key, key_len, 16, 256, 16,
487 conn->internal->sha1hash);
491 /* Set the key into use */
492 ret = silc_client_add_private_message_key_ske(client, conn, client_entry,
493 cipher, hmac, keymat);
494 client_entry->internal.generated = FALSE;
496 /* Free the key material */
497 silc_ske_free_key_material(keymat);
499 /* If we are setting the key without a request from the remote client,
500 we will send request to remote. */
501 if (!client_entry->internal.prv_resp)
502 silc_client_send_private_message_key_request(client, conn, client_entry);
507 /* Same as above but takes the key material from the SKE key material
510 SilcBool silc_client_add_private_message_key_ske(SilcClient client,
511 SilcClientConnection conn,
512 SilcClientEntry client_entry,
515 SilcSKEKeyMaterial keymat)
517 if (!client || !client_entry)
520 /* Return FALSE if key already set */
521 if (client_entry->internal.send_key && client_entry->internal.receive_key)
525 cipher = SILC_DEFAULT_CIPHER;
527 hmac = SILC_DEFAULT_HMAC;
529 /* Check the requested cipher and HMAC */
530 if (!silc_cipher_is_supported(cipher))
532 if (!silc_hmac_is_supported(hmac))
535 client_entry->internal.generated = TRUE;
537 /* Allocate the cipher and HMAC */
538 if (!silc_cipher_alloc(cipher, &client_entry->internal.send_key))
540 if (!silc_cipher_alloc(cipher, &client_entry->internal.receive_key))
542 if (!silc_hmac_alloc(hmac, NULL, &client_entry->internal.hmac_send))
544 if (!silc_hmac_alloc(hmac, NULL, &client_entry->internal.hmac_receive))
548 if (client_entry->internal.prv_resp) {
549 silc_cipher_set_key(client_entry->internal.send_key,
550 keymat->receive_enc_key,
551 keymat->enc_key_len);
552 silc_cipher_set_iv(client_entry->internal.send_key,
554 silc_cipher_set_key(client_entry->internal.receive_key,
555 keymat->send_enc_key,
556 keymat->enc_key_len);
557 silc_cipher_set_iv(client_entry->internal.receive_key, keymat->send_iv);
558 silc_hmac_set_key(client_entry->internal.hmac_send,
559 keymat->receive_hmac_key,
560 keymat->hmac_key_len);
561 silc_hmac_set_key(client_entry->internal.hmac_receive,
562 keymat->send_hmac_key,
563 keymat->hmac_key_len);
565 silc_cipher_set_key(client_entry->internal.send_key,
566 keymat->send_enc_key,
567 keymat->enc_key_len);
568 silc_cipher_set_iv(client_entry->internal.send_key,
570 silc_cipher_set_key(client_entry->internal.receive_key,
571 keymat->receive_enc_key,
572 keymat->enc_key_len);
573 silc_cipher_set_iv(client_entry->internal.receive_key, keymat->receive_iv);
574 silc_hmac_set_key(client_entry->internal.hmac_send,
575 keymat->send_hmac_key,
576 keymat->hmac_key_len);
577 silc_hmac_set_key(client_entry->internal.hmac_receive,
578 keymat->receive_hmac_key,
579 keymat->hmac_key_len);
585 /* Removes the private message from the library. The key won't be used
586 after this to protect the private messages with the remote `client_entry'
587 client. Returns FALSE on error, TRUE otherwise. */
589 SilcBool silc_client_del_private_message_key(SilcClient client,
590 SilcClientConnection conn,
591 SilcClientEntry client_entry)
593 if (!client || !client_entry)
596 if (!client_entry->internal.send_key && !client_entry->internal.receive_key)
599 silc_cipher_free(client_entry->internal.send_key);
600 silc_cipher_free(client_entry->internal.receive_key);
602 if (client_entry->internal.key) {
603 memset(client_entry->internal.key, 0, client_entry->internal.key_len);
604 silc_free(client_entry->internal.key);
607 client_entry->internal.send_key = NULL;
608 client_entry->internal.receive_key = NULL;
609 client_entry->internal.key = NULL;
610 client_entry->internal.prv_resp = FALSE;
615 /* Returns array of set private message keys associated to the connection
616 `conn'. Returns allocated SilcPrivateMessageKeys array and the array
617 count to the `key_count' argument. The array must be freed by the caller
618 by calling the silc_client_free_private_message_keys function. Note:
619 the keys returned in the array is in raw format. It might not be desired
620 to show the keys as is. The application might choose not to show the keys
621 at all or to show the fingerprints of the keys. */
623 SilcPrivateMessageKeys
624 silc_client_list_private_message_keys(SilcClient client,
625 SilcClientConnection conn,
626 SilcUInt32 *key_count)
628 SilcPrivateMessageKeys keys;
629 SilcUInt32 count = 0;
631 SilcIDCacheEntry id_cache;
632 SilcClientEntry entry;
634 if (!client || !conn)
637 silc_mutex_lock(conn->internal->lock);
638 if (!silc_idcache_get_all(conn->internal->client_cache, &list)) {
639 silc_mutex_unlock(conn->internal->lock);
643 keys = silc_calloc(silc_list_count(list), sizeof(*keys));
645 silc_mutex_unlock(conn->internal->lock);
649 silc_list_start(list);
650 while ((id_cache = silc_list_get(list))) {
651 entry = id_cache->context;
652 if (entry->internal.send_key) {
653 keys[count].client_entry = entry;
654 keys[count].cipher = (char *)silc_cipher_get_name(entry->internal.
656 keys[count].key = (entry->internal.generated == FALSE ?
657 entry->internal.key : NULL);
658 keys[count].key_len = (entry->internal.generated == FALSE ?
659 entry->internal.key_len : 0);
664 silc_mutex_unlock(conn->internal->lock);
672 /* Frees the SilcPrivateMessageKeys array returned by the function
673 silc_client_list_private_message_keys. */
675 void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
676 SilcUInt32 key_count)
681 /* Sets away `message'. The away message may be set when the client's
682 mode is changed to SILC_UMODE_GONE and the client whishes to reply
683 to anyone who sends private message. The `message' will be sent
684 automatically back to the the client who send private message. If
685 away message is already set this replaces the old message with the
686 new one. If `message' is NULL the old away message is removed.
687 The sender may freely free the memory of the `message'. */
689 void silc_client_set_away_message(SilcClient client,
690 SilcClientConnection conn,
693 assert(client && conn);
695 if (!message && conn->internal->away) {
696 silc_free(conn->internal->away->away);
697 silc_free(conn->internal->away);
698 conn->internal->away = NULL;
702 if (!conn->internal->away)
703 conn->internal->away = silc_calloc(1, sizeof(*conn->internal->away));
704 if (conn->internal->away->away)
705 silc_free(conn->internal->away->away);
706 conn->internal->away->away = strdup(message);