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,
39 SILC_LOG_DEBUG(("Sending private message"));
41 if (!client || !conn || !client_entry)
44 /* Encode private message payload */
46 silc_message_payload_encode(flags, data, data_len,
47 (!client_entry->internal.send_key ? FALSE :
48 !client_entry->internal.generated),
49 TRUE, client_entry->internal.send_key,
50 client_entry->internal.hmac_send,
51 client->rng, NULL, conn->private_key,
52 client->sha1hash, NULL);
54 SILC_LOG_ERROR(("Error encoding private message"));
58 /* Send the private message packet */
59 ret = silc_packet_send_ext(conn->stream, SILC_PACKET_PRIVATE_MESSAGE,
60 client_entry->internal.send_key ?
61 SILC_PACKET_FLAG_PRIVMSG_KEY : 0,
62 0, NULL, SILC_ID_CLIENT, &client_entry->id,
63 silc_buffer_datalen(buffer), NULL, NULL);
65 silc_buffer_free(buffer);
69 /************************* Private Message Receive **************************/
71 /* Private message waiting context */
75 SilcDList message_queue;
76 unsigned int stopped : 1;
77 } *SilcClientPrivateMessageWait;
79 /* Client resolving callback. This continues the private message packet
80 processing in the packet processor thread, which is in waiting state
81 (for incoming packets) when we get here. We can safely continue in
82 the thread and then return back to waiting when we do it synchronously. */
84 static void silc_client_private_message_resolved(SilcClient client,
85 SilcClientConnection conn,
91 silc_packet_free(context);
95 /* Continue processing the private message packet */
96 silc_fsm_set_state_context(&conn->internal->packet_thread, context);
97 silc_fsm_next(&conn->internal->packet_thread, silc_client_private_message);
98 silc_fsm_continue_sync(&conn->internal->packet_thread);
101 /* Private message received. */
103 SILC_FSM_STATE(silc_client_private_message)
105 SilcClientConnection conn = fsm_context;
106 SilcClient client = conn->client;
107 SilcPacket packet = state_context;
108 SilcMessagePayload payload = NULL;
109 SilcClientID remote_id;
110 SilcClientEntry remote_client;
111 SilcMessageFlags flags;
112 unsigned char *message;
113 SilcUInt32 message_len;
114 SilcClientPrivateMessageWait pmw;
116 if (packet->src_id_type != SILC_ID_CLIENT)
119 if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
120 &remote_id, sizeof(remote_id)))
123 /* Check whether we know this client already */
124 remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
125 if (!remote_client || !remote_client->nickname) {
126 /* Resolve the client info. We return back to packet thread to receive
127 other packets while we wait for the resolving to finish. */
128 silc_client_get_client_by_id_resolve(client, conn, &remote_id, NULL,
129 silc_client_private_message_resolved,
131 silc_fsm_next(fsm, silc_client_connection_st_packet);
132 return SILC_FSM_CONTINUE;
135 if (packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY &&
136 !remote_client->internal.receive_key &&
137 !remote_client->internal.hmac_receive)
140 /* Parse the payload and decrypt it also if private message key is set */
142 silc_message_payload_parse(silc_buffer_datalen(&packet->buffer),
143 TRUE, !remote_client->internal.generated,
144 remote_client->internal.receive_key,
145 remote_client->internal.hmac_receive,
150 #if 0 /* We need to rethink this. This doesn't work with multiple
151 waiters, and performance is suboptimal. */
152 /* Check if some thread is waiting for this private message */
153 silc_mutex_lock(conn->internal->lock);
154 if (conn->internal->privmsg_wait &&
155 silc_hash_table_find_ext(conn->internal->privmsg_wait,
156 &remote_client->id, NULL, (void **)&pmw,
157 NULL, NULL, silc_hash_id_compare_full,
158 SILC_32_TO_PTR(SILC_ID_CLIENT))) {
159 /* Signal that message was received */
160 silc_mutex_unlock(conn->internal->lock);
161 silc_mutex_lock(pmw->wait_lock);
163 silc_dlist_add(pmw->message_queue, payload);
164 silc_cond_broadcast(pmw->wait_cond);
165 silc_mutex_unlock(pmw->wait_lock);
166 silc_packet_free(packet);
169 silc_mutex_unlock(pmw->wait_lock);
171 silc_mutex_unlock(conn->internal->lock);
174 /* Pass the private message to application */
175 flags = silc_message_get_flags(payload);
176 message = silc_message_get_data(payload, &message_len);
177 client->internal->ops->private_message(client, conn, remote_client, payload,
178 flags, message, message_len);
180 /* See if we are away (gone). If we are away we will reply to the
181 sender with the set away message. */
182 if (conn->internal->away && conn->internal->away->away &&
183 !(flags & SILC_MESSAGE_FLAG_NOREPLY)) {
184 /* If it's me, ignore */
185 if (SILC_ID_CLIENT_COMPARE(&remote_id, conn->local_id))
188 /* Send the away message */
189 silc_client_send_private_message(client, conn, remote_client,
190 SILC_MESSAGE_FLAG_AUTOREPLY |
191 SILC_MESSAGE_FLAG_NOREPLY,
192 conn->internal->away->away,
193 strlen(conn->internal->away->away));
197 /** Packet processed */
199 silc_message_payload_free(payload);
200 silc_packet_free(packet);
201 silc_fsm_next(fsm, silc_client_connection_st_packet);
202 return SILC_FSM_CONTINUE;
205 #if 0 /* XXX we need to rethink this */
206 /* Initialize private message waiting in a thread. */
208 void *silc_client_private_message_wait_init(SilcClientConnection conn,
209 SilcClientEntry client_entry)
211 SilcClientPrivateMessageWait pmw;
213 pmw = silc_calloc(1, sizeof(*pmw));
217 pmw->message_queue = silc_dlist_init();
218 if (!pmw->message_queue) {
223 /* Allocate mutex and conditional variable */
224 if (!silc_mutex_alloc(&pmw->wait_lock)) {
225 silc_dlist_uninit(pmw->message_queue);
229 if (!silc_cond_alloc(&pmw->wait_cond)) {
230 silc_dlist_uninit(pmw->message_queue);
231 silc_mutex_free(pmw->wait_lock);
236 silc_mutex_lock(conn->internal->lock);
238 /* Allocate waiting hash table */
239 if (!conn->internal->privmsg_wait) {
240 conn->internal->privmsg_wait =
241 silc_hash_table_alloc(0, silc_hash_id,
242 SILC_32_TO_PTR(SILC_ID_CLIENT),
243 silc_hash_id_compare,
244 SILC_32_TO_PTR(SILC_ID_CLIENT), NULL, NULL, TRUE);
245 if (!conn->internal->privmsg_wait) {
246 silc_mutex_unlock(conn->internal->lock);
247 silc_dlist_uninit(pmw->message_queue);
248 silc_mutex_free(pmw->wait_lock);
249 silc_cond_free(pmw->wait_cond);
255 /* Add to waiting hash table */
256 silc_hash_table_add(conn->internal->privmsg_wait, client_entry->id, pmw);
258 silc_mutex_unlock(conn->internal->lock);
263 /* Uninitialize private message waiting. */
265 void silc_client_private_message_wait_uninit(SilcClientConnection conn,
266 SilcClientEntry client_entry,
269 SilcClientPrivateMessageWait pmw = waiter;
270 SilcMessagePayload payload;
272 /* Signal any threads to stop waiting */
273 silc_mutex_lock(pmw->wait_lock);
275 silc_cond_broadcast(pmw->wait_cond);
276 silc_mutex_unlock(pmw->wait_lock);
278 /* Re-acquire lock and free resources */
279 silc_mutex_lock(pmw->wait_lock);
281 /* Free any remaining message */
282 silc_dlist_start(pmw->message_queue);
283 while ((payload = silc_dlist_get(pmw->message_queue)))
284 silc_message_payload_free(payload);
286 silc_dlist_uninit(pmw->message_queue);
287 silc_cond_free(pmw->wait_cond);
288 silc_mutex_unlock(pmw->wait_lock);
289 silc_mutex_free(pmw->wait_lock);
291 silc_mutex_lock(conn->internal->lock);
292 silc_hash_table_del_by_context(conn->internal->privmsg_wait,
293 client_entry->id, pmw);
294 silc_mutex_unlock(conn->internal->lock);
299 /* Blocks the calling process or thread until a private message has been
300 received from the specified client. */
302 SilcBool silc_client_private_message_wait(SilcClientConnection conn,
303 SilcClientEntry client_entry,
305 SilcMessagePayload *payload)
307 SilcClientPrivateMessageWait pmw = waiter;
310 silc_mutex_lock(pmw->wait_lock);
312 /* Wait here until private message has been received */
313 while (silc_dlist_count(pmw->message_queue) == 0) {
315 silc_mutex_unlock(pmw->wait_lock);
318 silc_cond_wait(pmw->wait_cond, pmw->wait_lock);
322 silc_dlist_start(pmw->message_queue);
323 *payload = silc_dlist_get(pmw->message_queue);
324 silc_dlist_del(pmw->message_queue, *payload);
326 silc_mutex_unlock(pmw->wait_lock);
332 /*************************** Private Message Key ****************************/
334 /* Client resolving callback. Here we simply mark that we are the responder
335 side of this private message key request. */
337 static void silc_client_private_message_key_cb(SilcClient client,
338 SilcClientConnection conn,
343 SilcPacket packet = context;
344 unsigned char *cipher = NULL, *hmac = NULL;
345 SilcClientEntry client_entry;
349 silc_packet_free(packet);
353 /* Parse the private message key payload */
354 ret = silc_buffer_unformat(&packet->buffer,
355 SILC_STR_UI16_STRING_ALLOC(&cipher),
356 SILC_STR_UI16_STRING_ALLOC(&hmac),
361 /* Mark that we are responder */
362 client_entry = silc_dlist_get(clients);
363 client_entry->internal.prv_resp = TRUE;
365 /* XXX we should notify application that remote wants to set up the
371 silc_packet_free(packet);
374 /* Processes incoming Private Message Key payload to indicate that the
375 sender whishes to set up a static private message key. */
377 SILC_FSM_STATE(silc_client_private_message_key)
379 SilcClientConnection conn = fsm_context;
380 SilcClient client = conn->client;
381 SilcPacket packet = state_context;
382 SilcClientID remote_id;
384 if (packet->src_id_type != SILC_ID_CLIENT) {
385 silc_packet_free(packet);
389 if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
390 &remote_id, sizeof(remote_id))) {
391 silc_packet_free(packet);
395 /* Always resolve the remote client. The actual packet is processed
396 in the resolving callback. */
397 silc_client_get_client_by_id_resolve(client, conn, &remote_id, NULL,
398 silc_client_private_message_key_cb,
402 silc_fsm_next(fsm, silc_client_connection_st_packet);
403 return SILC_FSM_CONTINUE;
406 /* Adds private message key to the client library. The key will be used to
407 encrypt all private message between the client and the remote client
408 indicated by the `client_entry'. If the `key' is NULL and the boolean
409 value `generate_key' is TRUE the library will generate random key.
410 The `key' maybe for example pre-shared-key, passphrase or similar.
411 The `cipher' and `hmac' MAY be provided but SHOULD be NULL to assure
412 that the requirements of the SILC protocol are met. The API, however,
413 allows to allocate any cipher and HMAC.
415 If `responder' is TRUE then the sending and receiving keys will be
416 set according the client being the receiver of the private key. If
417 FALSE the client is being the sender (or negotiator) of the private
420 It is not necessary to set key for normal private message usage. If the
421 key is not set then the private messages are encrypted using normal
422 session keys. Setting the private key, however, increases the security.
424 Returns FALSE if the key is already set for the `client_entry', TRUE
427 SilcBool silc_client_add_private_message_key(SilcClient client,
428 SilcClientConnection conn,
429 SilcClientEntry client_entry,
434 SilcBool generate_key,
437 unsigned char private_key[32];
439 SilcSKEKeyMaterial keymat;
443 if (!client || !client_entry)
446 /* Return FALSE if key already set */
447 if (client_entry->internal.send_key && client_entry->internal.receive_key)
451 cipher = SILC_DEFAULT_CIPHER;
453 hmac = SILC_DEFAULT_HMAC;
455 /* Check the requested cipher and HMAC */
456 if (!silc_cipher_is_supported(cipher))
458 if (!silc_hmac_is_supported(hmac))
461 /* Generate key if not provided */
462 if (generate_key == TRUE) {
464 for (i = 0; i < len; i++)
465 private_key[i] = silc_rng_get_byte_fast(client->rng);
471 client_entry->internal.key = silc_memdup(key, key_len);
472 client_entry->internal.key_len = key_len;
474 /* Produce the key material as the protocol defines */
475 keymat = silc_ske_process_key_material_data(key, key_len, 16, 256, 16,
480 /* Set the key into use */
481 ret = silc_client_add_private_message_key_ske(client, conn, client_entry,
482 cipher, hmac, keymat,
486 client_entry->internal.generated = FALSE;
488 /* Free the key material */
489 silc_ske_free_key_material(keymat);
494 /* Same as above but takes the key material from the SKE key material
495 structure. This structure is received if the application uses the
496 silc_client_send_key_agreement to negotiate the key material. The
497 `cipher' and `hmac' SHOULD be provided as it is negotiated also in
500 SilcBool silc_client_add_private_message_key_ske(SilcClient client,
501 SilcClientConnection conn,
502 SilcClientEntry client_entry,
505 SilcSKEKeyMaterial keymat,
508 if (!client || !client_entry)
511 /* Return FALSE if key already set */
512 if (client_entry->internal.send_key && client_entry->internal.receive_key)
516 cipher = SILC_DEFAULT_CIPHER;
518 hmac = SILC_DEFAULT_HMAC;
520 /* Check the requested cipher and HMAC */
521 if (!silc_cipher_is_supported(cipher))
523 if (!silc_hmac_is_supported(hmac))
526 client_entry->internal.generated = TRUE;
528 /* Allocate the cipher and HMAC */
529 if (!silc_cipher_alloc(cipher, &client_entry->internal.send_key))
531 if (!silc_cipher_alloc(cipher, &client_entry->internal.receive_key))
533 if (!silc_hmac_alloc(hmac, NULL, &client_entry->internal.hmac_send))
535 if (!silc_hmac_alloc(hmac, NULL, &client_entry->internal.hmac_receive))
539 if (responder == TRUE) {
540 silc_cipher_set_key(client_entry->internal.send_key,
541 keymat->receive_enc_key,
542 keymat->enc_key_len);
543 silc_cipher_set_iv(client_entry->internal.send_key,
545 silc_cipher_set_key(client_entry->internal.receive_key,
546 keymat->send_enc_key,
547 keymat->enc_key_len);
548 silc_cipher_set_iv(client_entry->internal.receive_key, keymat->send_iv);
549 silc_hmac_set_key(client_entry->internal.hmac_send,
550 keymat->receive_hmac_key,
551 keymat->hmac_key_len);
552 silc_hmac_set_key(client_entry->internal.hmac_receive,
553 keymat->send_hmac_key,
554 keymat->hmac_key_len);
556 silc_cipher_set_key(client_entry->internal.send_key,
557 keymat->send_enc_key,
558 keymat->enc_key_len);
559 silc_cipher_set_iv(client_entry->internal.send_key,
561 silc_cipher_set_key(client_entry->internal.receive_key,
562 keymat->receive_enc_key,
563 keymat->enc_key_len);
564 silc_cipher_set_iv(client_entry->internal.receive_key, keymat->receive_iv);
565 silc_hmac_set_key(client_entry->internal.hmac_send,
566 keymat->send_hmac_key,
567 keymat->hmac_key_len);
568 silc_hmac_set_key(client_entry->internal.hmac_receive,
569 keymat->receive_hmac_key,
570 keymat->hmac_key_len);
576 /* Sends private message key indicator. The sender of this packet is
577 going to be the initiator, if and when, the users set up a static
578 private message key (not Key Agreement). */
581 silc_client_send_private_message_key_request(SilcClient client,
582 SilcClientConnection conn,
583 SilcClientEntry client_entry)
585 SilcBufferStruct buffer;
586 int cipher_len, hmac_len;
587 const char *cipher, *hmac;
590 if (!client || !conn || !client_entry)
593 if (!client_entry->internal.send_key && !client_entry->internal.receive_key)
596 SILC_LOG_DEBUG(("Sending private message key indicator"));
598 cipher = silc_cipher_get_name(client_entry->internal.send_key);
599 cipher_len = strlen(cipher);
600 hmac = silc_hmac_get_name(client_entry->internal.hmac_send);
601 hmac_len = strlen(hmac);
603 /* Create private message key payload */
604 memset(&buffer, 0, sizeof(buffer));
605 if (silc_buffer_format(&buffer,
606 SILC_STR_UI_SHORT(cipher_len),
607 SILC_STR_UI_XNSTRING(cipher,
609 SILC_STR_UI_SHORT(hmac_len),
610 SILC_STR_UI_XNSTRING(hmac,
615 /* Send the packet */
616 ret = silc_packet_send_ext(conn->stream, SILC_PACKET_PRIVATE_MESSAGE_KEY,
617 0, 0, NULL, SILC_ID_CLIENT, &client_entry->id,
618 silc_buffer_datalen(&buffer), NULL, NULL);
619 silc_buffer_purge(&buffer);
624 /* Removes the private message from the library. The key won't be used
625 after this to protect the private messages with the remote `client_entry'
626 client. Returns FALSE on error, TRUE otherwise. */
628 SilcBool silc_client_del_private_message_key(SilcClient client,
629 SilcClientConnection conn,
630 SilcClientEntry client_entry)
632 if (!client || !client_entry)
635 if (!client_entry->internal.send_key && !client_entry->internal.receive_key)
638 silc_cipher_free(client_entry->internal.send_key);
639 silc_cipher_free(client_entry->internal.receive_key);
641 if (client_entry->internal.key) {
642 memset(client_entry->internal.key, 0, client_entry->internal.key_len);
643 silc_free(client_entry->internal.key);
646 client_entry->internal.send_key = NULL;
647 client_entry->internal.receive_key = NULL;
648 client_entry->internal.key = NULL;
653 /* Returns array of set private message keys associated to the connection
654 `conn'. Returns allocated SilcPrivateMessageKeys array and the array
655 count to the `key_count' argument. The array must be freed by the caller
656 by calling the silc_client_free_private_message_keys function. Note:
657 the keys returned in the array is in raw format. It might not be desired
658 to show the keys as is. The application might choose not to show the keys
659 at all or to show the fingerprints of the keys. */
661 SilcPrivateMessageKeys
662 silc_client_list_private_message_keys(SilcClient client,
663 SilcClientConnection conn,
664 SilcUInt32 *key_count)
666 SilcPrivateMessageKeys keys;
667 SilcUInt32 count = 0;
669 SilcIDCacheEntry id_cache;
670 SilcClientEntry entry;
672 if (!client || !conn)
675 if (!silc_idcache_get_all(conn->internal->client_cache, &list))
678 keys = silc_calloc(silc_list_count(list), sizeof(*keys));
682 silc_list_start(list);
683 while ((id_cache = silc_list_get(list))) {
684 entry = id_cache->context;
685 if (entry->internal.send_key) {
686 keys[count].client_entry = entry;
687 keys[count].cipher = (char *)silc_cipher_get_name(entry->internal.
689 keys[count].key = (entry->internal.generated == FALSE ?
690 entry->internal.key : NULL);
691 keys[count].key_len = (entry->internal.generated == FALSE ?
692 entry->internal.key_len : 0);
703 /* Frees the SilcPrivateMessageKeys array returned by the function
704 silc_client_list_private_message_keys. */
706 void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
707 SilcUInt32 key_count)
712 /* Sets away `message'. The away message may be set when the client's
713 mode is changed to SILC_UMODE_GONE and the client whishes to reply
714 to anyone who sends private message. The `message' will be sent
715 automatically back to the the client who send private message. If
716 away message is already set this replaces the old message with the
717 new one. If `message' is NULL the old away message is removed.
718 The sender may freely free the memory of the `message'. */
720 void silc_client_set_away_message(SilcClient client,
721 SilcClientConnection conn,
724 assert(client && conn);
726 if (!message && conn->internal->away) {
727 silc_free(conn->internal->away->away);
728 silc_free(conn->internal->away);
729 conn->internal->away = NULL;
733 if (!conn->internal->away)
734 conn->internal->away = silc_calloc(1, sizeof(*conn->internal->away));
735 if (conn->internal->away->away)
736 silc_free(conn->internal->away->away);
737 conn->internal->away->away = strdup(message);