5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2007 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,
41 if (silc_unlikely(!client || !conn || !client_entry))
43 if (silc_unlikely(flags & SILC_MESSAGE_FLAG_SIGNED && !hash))
45 if (silc_unlikely(conn->internal->disconnected))
48 SILC_LOG_DEBUG(("Sending private message"));
50 sid.type = SILC_ID_CLIENT;
51 sid.u.client_id = conn->local_entry->id;
52 rid.type = SILC_ID_CLIENT;
53 rid.u.client_id = client_entry->id;
55 /* Encode private message payload */
57 silc_message_payload_encode(flags, data, data_len,
58 (!client_entry->internal.send_key ? FALSE :
59 !client_entry->internal.generated),
60 TRUE, client_entry->internal.send_key,
61 client_entry->internal.hmac_send,
62 client->rng, NULL, conn->private_key,
63 hash, &sid, &rid, NULL);
64 if (silc_unlikely(!buffer)) {
65 SILC_LOG_ERROR(("Error encoding private message"));
69 /* Send the private message packet */
70 ret = silc_packet_send_ext(conn->stream, SILC_PACKET_PRIVATE_MESSAGE,
71 client_entry->internal.send_key ?
72 SILC_PACKET_FLAG_PRIVMSG_KEY : 0,
73 0, NULL, SILC_ID_CLIENT, &client_entry->id,
74 silc_buffer_datalen(buffer), NULL, NULL);
76 silc_buffer_free(buffer);
80 /************************* Private Message Receive **************************/
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;
112 SILC_LOG_DEBUG(("Received private message"));
114 if (silc_unlikely(packet->src_id_type != SILC_ID_CLIENT)) {
115 /** Invalid packet */
116 silc_fsm_next(fsm, silc_client_private_message_error);
117 return SILC_FSM_CONTINUE;
120 if (silc_unlikely(!silc_id_str2id(packet->src_id, packet->src_id_len,
121 SILC_ID_CLIENT, &remote_id,
122 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 (silc_unlikely(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,
151 packet->src_id, packet->src_id_len,
152 packet->dst_id, packet->dst_id_len,
154 if (silc_unlikely(!payload))
157 /* Pass the private message to application */
158 flags = silc_message_get_flags(payload);
159 message = silc_message_get_data(payload, &message_len);
160 client->internal->ops->private_message(client, conn, remote_client, payload,
161 flags, message, message_len);
163 /* See if we are away (gone). If we are away we will reply to the
164 sender with the set away message. */
165 if (conn->internal->away_message &&
166 !(flags & SILC_MESSAGE_FLAG_NOREPLY)) {
167 /* If it's me, ignore */
168 if (SILC_ID_CLIENT_COMPARE(&remote_id, conn->local_id))
171 /* Send the away message */
172 silc_client_send_private_message(client, conn, remote_client,
173 SILC_MESSAGE_FLAG_AUTOREPLY |
174 SILC_MESSAGE_FLAG_NOREPLY, NULL,
175 conn->internal->away_message,
176 strlen(conn->internal->away_message));
180 /** Packet processed */
181 silc_packet_free(packet);
182 silc_client_unref_client(client, conn, remote_client);
184 silc_message_payload_free(payload);
185 return SILC_FSM_FINISH;
188 /* Private message error. */
190 SILC_FSM_STATE(silc_client_private_message_error)
192 SilcPacket packet = state_context;
193 silc_packet_free(packet);
194 return SILC_FSM_FINISH;
197 /* Initialize private message waiter for the `conn' connection. */
199 SilcBool silc_client_private_message_wait_init(SilcClient client,
200 SilcClientConnection conn,
201 SilcClientEntry client_entry)
205 if (client_entry->internal.prv_waiter)
208 /* We want SILC_PACKET_PRIVATE_MESSAGE packets from this source ID. */
209 id.type = SILC_ID_CLIENT;
210 id.u.client_id = client_entry->id;
212 client_entry->internal.prv_waiter =
213 silc_packet_wait_init(conn->stream, &id, SILC_PACKET_PRIVATE_MESSAGE, -1);
214 if (!client_entry->internal.prv_waiter)
220 /* Uninitializes private message waiter. */
222 void silc_client_private_message_wait_uninit(SilcClient client,
223 SilcClientConnection conn,
224 SilcClientEntry client_entry)
226 if (!client_entry->internal.prv_waiter)
228 silc_packet_wait_uninit(client_entry->internal.prv_waiter, conn->stream);
229 client_entry->internal.prv_waiter = NULL;
232 /* Blocks the calling process or thread until private message has been
233 received from the specified client. */
235 SilcBool silc_client_private_message_wait(SilcClient client,
236 SilcClientConnection conn,
237 SilcClientEntry client_entry,
238 SilcMessagePayload *payload)
242 if (!client_entry->internal.prv_waiter)
245 /* Block until private message arrives */
247 if ((silc_packet_wait(client_entry->internal.prv_waiter, 0, &packet)) < 0)
250 /* Parse the payload and decrypt it also if private message key is set */
252 silc_message_payload_parse(silc_buffer_data(&packet->buffer),
253 silc_buffer_len(&packet->buffer),
254 TRUE, !client_entry->internal.generated,
255 client_entry->internal.receive_key,
256 client_entry->internal.hmac_receive,
257 packet->src_id, packet->src_id_len,
258 packet->dst_id, packet->dst_id_len,
261 silc_packet_free(packet);
268 silc_packet_free(packet);
272 /*************************** Private Message Key ****************************/
274 /* Sends private message key request. Sender of this packet is initiator
275 when setting the private message key. */
278 silc_client_send_private_message_key_request(SilcClient client,
279 SilcClientConnection conn,
280 SilcClientEntry client_entry)
282 const char *cipher, *hmac;
284 SILC_LOG_DEBUG(("Sending private message key request"));
286 cipher = silc_cipher_get_name(client_entry->internal.send_key);
287 hmac = silc_hmac_get_name(client_entry->internal.hmac_send);
289 /* Send the packet */
290 return silc_packet_send_va_ext(conn->stream,
291 SILC_PACKET_PRIVATE_MESSAGE_KEY,
292 0, 0, NULL, SILC_ID_CLIENT,
293 &client_entry->id, NULL, NULL,
294 SILC_STR_UI_SHORT(strlen(cipher)),
295 SILC_STR_DATA(cipher, strlen(cipher)),
296 SILC_STR_UI_SHORT(strlen(hmac)),
297 SILC_STR_DATA(hmac, strlen(hmac)),
301 /* Client resolving callback. Here we simply mark that we are the responder
302 side of this private message key request. */
304 static void silc_client_private_message_key_cb(SilcClient client,
305 SilcClientConnection conn,
310 SilcFSMThread thread = context;
311 SilcPacket packet = silc_fsm_get_state_context(thread);
312 unsigned char *cipher = NULL, *hmac = NULL;
313 SilcClientEntry client_entry;
317 silc_packet_free(packet);
318 silc_fsm_finish(thread);
322 /* Parse the private message key payload */
323 ret = silc_buffer_unformat(&packet->buffer,
324 SILC_STR_UI16_STRING_ALLOC(&cipher),
325 SILC_STR_UI16_STRING_ALLOC(&hmac),
330 /* Mark that we are responder */
331 client_entry = silc_dlist_get(clients);
332 client_entry->internal.prv_resp = TRUE;
334 /* XXX we should notify application that remote wants to set up the
335 static key. And we should tell if we already have key with remote.
336 Application should return status telling whether to delete the key
342 silc_packet_free(packet);
343 silc_fsm_finish(thread);
346 /* Processes incoming Private Message Key payload to indicate that the
347 sender whishes to set up a static private message key. */
349 SILC_FSM_STATE(silc_client_private_message_key)
351 SilcClientConnection conn = fsm_context;
352 SilcClient client = conn->client;
353 SilcPacket packet = state_context;
354 SilcClientID remote_id;
356 if (packet->src_id_type != SILC_ID_CLIENT) {
357 silc_packet_free(packet);
358 return SILC_FSM_FINISH;
361 if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
362 &remote_id, sizeof(remote_id))) {
363 silc_packet_free(packet);
364 return SILC_FSM_FINISH;
367 /* Always resolve the remote client. The actual packet is processed
368 in the resolving callback. */
369 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
370 client, conn, &remote_id, NULL,
371 silc_client_private_message_key_cb,
375 /* Adds new private message key to `client_entry'. If we are setting this
376 before receiving request for it from `client_entry' we will send the
377 request to the client. Otherwise, we are responder side. */
379 SilcBool silc_client_add_private_message_key(SilcClient client,
380 SilcClientConnection conn,
381 SilcClientEntry client_entry,
387 SilcSKEKeyMaterial keymat;
390 if (!client || !client_entry)
393 /* Return FALSE if key already set */
394 if (client_entry->internal.send_key && client_entry->internal.receive_key)
398 cipher = SILC_DEFAULT_CIPHER;
400 hmac = SILC_DEFAULT_HMAC;
402 /* Check the requested cipher and HMAC */
403 if (!silc_cipher_is_supported(cipher))
405 if (!silc_hmac_is_supported(hmac))
409 client_entry->internal.key = silc_memdup(key, key_len);
410 client_entry->internal.key_len = key_len;
412 /* Produce the key material as the protocol defines */
413 keymat = silc_ske_process_key_material_data(key, key_len, 16, 256, 16,
414 conn->internal->sha1hash);
418 /* Set the key into use */
419 ret = silc_client_add_private_message_key_ske(client, conn, client_entry,
420 cipher, hmac, keymat);
421 client_entry->internal.generated = FALSE;
423 /* Free the key material */
424 silc_ske_free_key_material(keymat);
426 /* If we are setting the key without a request from the remote client,
427 we will send request to remote. */
428 if (!client_entry->internal.prv_resp)
429 silc_client_send_private_message_key_request(client, conn, client_entry);
434 /* Same as above but takes the key material from the SKE key material
437 SilcBool silc_client_add_private_message_key_ske(SilcClient client,
438 SilcClientConnection conn,
439 SilcClientEntry client_entry,
442 SilcSKEKeyMaterial keymat)
444 if (!client || !client_entry)
447 /* Return FALSE if key already set */
448 if (client_entry->internal.send_key && client_entry->internal.receive_key)
452 cipher = SILC_DEFAULT_CIPHER;
454 hmac = SILC_DEFAULT_HMAC;
456 /* Check the requested cipher and HMAC */
457 if (!silc_cipher_is_supported(cipher))
459 if (!silc_hmac_is_supported(hmac))
462 client_entry->internal.generated = TRUE;
464 /* Allocate the cipher and HMAC */
465 if (!silc_cipher_alloc(cipher, &client_entry->internal.send_key))
467 if (!silc_cipher_alloc(cipher, &client_entry->internal.receive_key))
469 if (!silc_hmac_alloc(hmac, NULL, &client_entry->internal.hmac_send))
471 if (!silc_hmac_alloc(hmac, NULL, &client_entry->internal.hmac_receive))
475 if (client_entry->internal.prv_resp) {
476 silc_cipher_set_key(client_entry->internal.send_key,
477 keymat->receive_enc_key,
478 keymat->enc_key_len, TRUE);
479 silc_cipher_set_iv(client_entry->internal.send_key,
481 silc_cipher_set_key(client_entry->internal.receive_key,
482 keymat->send_enc_key,
483 keymat->enc_key_len, FALSE);
484 silc_cipher_set_iv(client_entry->internal.receive_key, keymat->send_iv);
485 silc_hmac_set_key(client_entry->internal.hmac_send,
486 keymat->receive_hmac_key,
487 keymat->hmac_key_len);
488 silc_hmac_set_key(client_entry->internal.hmac_receive,
489 keymat->send_hmac_key,
490 keymat->hmac_key_len);
492 silc_cipher_set_key(client_entry->internal.send_key,
493 keymat->send_enc_key,
494 keymat->enc_key_len, TRUE);
495 silc_cipher_set_iv(client_entry->internal.send_key,
497 silc_cipher_set_key(client_entry->internal.receive_key,
498 keymat->receive_enc_key,
499 keymat->enc_key_len, FALSE);
500 silc_cipher_set_iv(client_entry->internal.receive_key, keymat->receive_iv);
501 silc_hmac_set_key(client_entry->internal.hmac_send,
502 keymat->send_hmac_key,
503 keymat->hmac_key_len);
504 silc_hmac_set_key(client_entry->internal.hmac_receive,
505 keymat->receive_hmac_key,
506 keymat->hmac_key_len);
512 /* Removes the private message from the library. The key won't be used
513 after this to protect the private messages with the remote `client_entry'
514 client. Returns FALSE on error, TRUE otherwise. */
516 SilcBool silc_client_del_private_message_key(SilcClient client,
517 SilcClientConnection conn,
518 SilcClientEntry client_entry)
520 if (!client || !client_entry)
523 if (!client_entry->internal.send_key && !client_entry->internal.receive_key)
526 silc_cipher_free(client_entry->internal.send_key);
527 silc_cipher_free(client_entry->internal.receive_key);
529 if (client_entry->internal.key) {
530 memset(client_entry->internal.key, 0, client_entry->internal.key_len);
531 silc_free(client_entry->internal.key);
534 client_entry->internal.send_key = NULL;
535 client_entry->internal.receive_key = NULL;
536 client_entry->internal.key = NULL;
537 client_entry->internal.prv_resp = FALSE;
542 /* Returns array of set private message keys associated to the connection
543 `conn'. Returns allocated SilcPrivateMessageKeys array and the array
544 count to the `key_count' argument. The array must be freed by the caller
545 by calling the silc_client_free_private_message_keys function. Note:
546 the keys returned in the array is in raw format. It might not be desired
547 to show the keys as is. The application might choose not to show the keys
548 at all or to show the fingerprints of the keys. */
550 SilcPrivateMessageKeys
551 silc_client_list_private_message_keys(SilcClient client,
552 SilcClientConnection conn,
553 SilcUInt32 *key_count)
555 SilcPrivateMessageKeys keys;
556 SilcUInt32 count = 0;
558 SilcIDCacheEntry id_cache;
559 SilcClientEntry entry;
561 if (!client || !conn)
564 silc_mutex_lock(conn->internal->lock);
565 if (!silc_idcache_get_all(conn->internal->client_cache, &list)) {
566 silc_mutex_unlock(conn->internal->lock);
570 keys = silc_calloc(silc_list_count(list), sizeof(*keys));
572 silc_mutex_unlock(conn->internal->lock);
576 silc_list_start(list);
577 while ((id_cache = silc_list_get(list))) {
578 entry = id_cache->context;
579 if (entry->internal.send_key) {
580 keys[count].client_entry = entry;
581 keys[count].cipher = (char *)silc_cipher_get_name(entry->internal.
583 keys[count].key = (entry->internal.generated == FALSE ?
584 entry->internal.key : NULL);
585 keys[count].key_len = (entry->internal.generated == FALSE ?
586 entry->internal.key_len : 0);
591 silc_mutex_unlock(conn->internal->lock);
599 /* Frees the SilcPrivateMessageKeys array returned by the function
600 silc_client_list_private_message_keys. */
602 void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
603 SilcUInt32 key_count)
608 /* Return private message key from the client entry. */
611 silc_client_private_message_key_is_set(SilcClient client,
612 SilcClientConnection conn,
613 SilcClientEntry client_entry)
615 return client_entry->internal.send_key != NULL;
618 /* Sets away `message'. The away message may be set when the client's
619 mode is changed to SILC_UMODE_GONE and the client whishes to reply
620 to anyone who sends private message. The `message' will be sent
621 automatically back to the the client who send private message. If
622 away message is already set this replaces the old message with the
623 new one. If `message' is NULL the old away message is removed.
624 The sender may freely free the memory of the `message'. */
626 SilcBool silc_client_set_away_message(SilcClient client,
627 SilcClientConnection conn,
630 if (!client || !conn)
634 silc_free(conn->internal->away_message);
635 conn->internal->away_message = NULL;
639 if (conn->internal->away_message)
640 silc_free(conn->internal->away_message);
642 conn->internal->away_message = strdup(message);
643 if (!conn->internal->away_message)