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,
40 if (silc_unlikely(!client || !conn || !client_entry))
42 if (silc_unlikely(flags & SILC_MESSAGE_FLAG_SIGNED && !hash))
44 if (silc_unlikely(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,
58 if (silc_unlikely(!buffer)) {
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 /* Client resolving callback. Continues with the private message processing */
78 static void silc_client_private_message_resolved(SilcClient client,
79 SilcClientConnection conn,
84 /* If no client found, ignore the private message, a silent error */
86 silc_fsm_next(context, silc_client_private_message_error);
88 /* Continue processing the private message packet */
89 SILC_FSM_CALL_CONTINUE(context);
92 /* Private message received. */
94 SILC_FSM_STATE(silc_client_private_message)
96 SilcClientConnection conn = fsm_context;
97 SilcClient client = conn->client;
98 SilcPacket packet = state_context;
99 SilcMessagePayload payload = NULL;
100 SilcClientID remote_id;
101 SilcClientEntry remote_client = NULL;
102 SilcMessageFlags flags;
103 unsigned char *message;
104 SilcUInt32 message_len;
106 SILC_LOG_DEBUG(("Received private message"));
108 if (silc_unlikely(packet->src_id_type != SILC_ID_CLIENT)) {
109 /** Invalid packet */
110 silc_fsm_next(fsm, silc_client_private_message_error);
111 return SILC_FSM_CONTINUE;
114 if (silc_unlikely(!silc_id_str2id(packet->src_id, packet->src_id_len,
115 SILC_ID_CLIENT, &remote_id,
116 sizeof(remote_id)))) {
117 /** Invalid source ID */
118 silc_fsm_next(fsm, silc_client_private_message_error);
119 return SILC_FSM_CONTINUE;
122 /* Check whether we know this client already */
123 remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
124 if (!remote_client || !remote_client->nickname[0]) {
125 /** Resolve client info */
126 silc_client_unref_client(client, conn, remote_client);
127 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
128 client, conn, &remote_id, NULL,
129 silc_client_private_message_resolved,
134 if (silc_unlikely(packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY &&
135 !remote_client->internal.receive_key &&
136 !remote_client->internal.hmac_receive))
139 /* Parse the payload and decrypt it also if private message key is set */
141 silc_message_payload_parse(silc_buffer_datalen(&packet->buffer),
142 TRUE, !remote_client->internal.generated,
143 remote_client->internal.receive_key,
144 remote_client->internal.hmac_receive,
146 if (silc_unlikely(!payload))
149 /* Pass the private message to application */
150 flags = silc_message_get_flags(payload);
151 message = silc_message_get_data(payload, &message_len);
152 client->internal->ops->private_message(client, conn, remote_client, payload,
153 flags, message, message_len);
155 /* See if we are away (gone). If we are away we will reply to the
156 sender with the set away message. */
157 if (conn->internal->away && conn->internal->away->away &&
158 !(flags & SILC_MESSAGE_FLAG_NOREPLY)) {
159 /* If it's me, ignore */
160 if (SILC_ID_CLIENT_COMPARE(&remote_id, conn->local_id))
163 /* Send the away message */
164 silc_client_send_private_message(client, conn, remote_client,
165 SILC_MESSAGE_FLAG_AUTOREPLY |
166 SILC_MESSAGE_FLAG_NOREPLY, NULL,
167 conn->internal->away->away,
168 strlen(conn->internal->away->away));
172 /** Packet processed */
173 silc_packet_free(packet);
174 silc_client_unref_client(client, conn, remote_client);
176 silc_message_payload_free(payload);
177 return SILC_FSM_FINISH;
180 /* Private message error. */
182 SILC_FSM_STATE(silc_client_private_message_error)
184 SilcPacket packet = state_context;
185 silc_packet_free(packet);
186 return SILC_FSM_FINISH;
189 /* Blocks the calling process or thread until private message has been
190 received from the specified client. */
192 SilcBool silc_client_private_message_wait(SilcClientConnection conn,
193 SilcClientEntry client_entry,
195 SilcMessagePayload *payload)
198 SilcClientID remote_id;
199 SilcFSMThread thread;
201 /* Block until private message arrives */
203 if ((silc_packet_wait(waiter, 0, &packet)) < 0)
206 /* Parse sender ID */
207 if (!silc_id_str2id(packet->src_id, packet->src_id_len,
208 SILC_ID_CLIENT, &remote_id,
209 sizeof(remote_id))) {
210 silc_packet_free(packet);
214 /* If the private message is not for the requested client, pass it to
215 normal private message processing. */
216 if (!SILC_ID_CLIENT_COMPARE(&remote_id, &client_entry->id)) {
217 thread = silc_fsm_thread_alloc(&conn->internal->fsm, conn,
218 silc_client_fsm_destructor, NULL, FALSE);
220 silc_packet_free(packet);
224 /* The packet will be processed in the connection thread, after this
225 FSM thread is started. */
226 silc_fsm_set_state_context(thread, packet);
227 silc_fsm_start(thread, silc_client_private_message);
231 /* Parse the payload and decrypt it also if private message key is set */
233 silc_message_payload_parse(silc_buffer_data(&packet->buffer),
234 silc_buffer_len(&packet->buffer),
235 TRUE, !client_entry->internal.generated,
236 client_entry->internal.receive_key,
237 client_entry->internal.hmac_receive,
240 silc_packet_free(packet);
247 silc_packet_free(packet);
251 /*************************** Private Message Key ****************************/
253 /* Sends private message key request. Sender of this packet is initiator
254 when setting the private message key. */
257 silc_client_send_private_message_key_request(SilcClient client,
258 SilcClientConnection conn,
259 SilcClientEntry client_entry)
261 const char *cipher, *hmac;
263 SILC_LOG_DEBUG(("Sending private message key request"));
265 cipher = silc_cipher_get_name(client_entry->internal.send_key);
266 hmac = silc_hmac_get_name(client_entry->internal.hmac_send);
268 /* Send the packet */
269 return silc_packet_send_va_ext(conn->stream,
270 SILC_PACKET_PRIVATE_MESSAGE_KEY,
271 0, 0, NULL, SILC_ID_CLIENT,
272 &client_entry->id, NULL, NULL,
273 SILC_STR_UI_SHORT(strlen(cipher)),
274 SILC_STR_DATA(cipher, strlen(cipher)),
275 SILC_STR_UI_SHORT(strlen(hmac)),
276 SILC_STR_DATA(hmac, strlen(hmac)),
280 /* Client resolving callback. Here we simply mark that we are the responder
281 side of this private message key request. */
283 static void silc_client_private_message_key_cb(SilcClient client,
284 SilcClientConnection conn,
289 SilcPacket packet = context;
290 unsigned char *cipher = NULL, *hmac = NULL;
291 SilcClientEntry client_entry;
295 silc_packet_free(packet);
299 /* Parse the private message key payload */
300 ret = silc_buffer_unformat(&packet->buffer,
301 SILC_STR_UI16_STRING_ALLOC(&cipher),
302 SILC_STR_UI16_STRING_ALLOC(&hmac),
307 /* Mark that we are responder */
308 client_entry = silc_dlist_get(clients);
309 client_entry->internal.prv_resp = TRUE;
311 /* XXX we should notify application that remote wants to set up the
312 static key. And we should tell if we already have key with remote.
313 Application should return status telling whether to delete the key
319 silc_packet_free(packet);
322 /* Processes incoming Private Message Key payload to indicate that the
323 sender whishes to set up a static private message key. */
325 SILC_FSM_STATE(silc_client_private_message_key)
327 SilcClientConnection conn = fsm_context;
328 SilcClient client = conn->client;
329 SilcPacket packet = state_context;
330 SilcClientID remote_id;
332 if (packet->src_id_type != SILC_ID_CLIENT) {
333 silc_packet_free(packet);
334 return SILC_FSM_FINISH;
337 if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
338 &remote_id, sizeof(remote_id))) {
339 silc_packet_free(packet);
340 return SILC_FSM_FINISH;
343 /* Always resolve the remote client. The actual packet is processed
344 in the resolving callback. */
345 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
346 client, conn, &remote_id, NULL,
347 silc_client_private_message_key_cb,
351 /* Adds new private message key to `client_entry'. If we are setting this
352 before receiving request for it from `client_entry' we will send the
353 request to the client. Otherwise, we are responder side. */
355 SilcBool silc_client_add_private_message_key(SilcClient client,
356 SilcClientConnection conn,
357 SilcClientEntry client_entry,
363 SilcSKEKeyMaterial keymat;
366 if (!client || !client_entry)
369 /* Return FALSE if key already set */
370 if (client_entry->internal.send_key && client_entry->internal.receive_key)
374 cipher = SILC_DEFAULT_CIPHER;
376 hmac = SILC_DEFAULT_HMAC;
378 /* Check the requested cipher and HMAC */
379 if (!silc_cipher_is_supported(cipher))
381 if (!silc_hmac_is_supported(hmac))
385 client_entry->internal.key = silc_memdup(key, key_len);
386 client_entry->internal.key_len = key_len;
388 /* Produce the key material as the protocol defines */
389 keymat = silc_ske_process_key_material_data(key, key_len, 16, 256, 16,
390 conn->internal->sha1hash);
394 /* Set the key into use */
395 ret = silc_client_add_private_message_key_ske(client, conn, client_entry,
396 cipher, hmac, keymat);
397 client_entry->internal.generated = FALSE;
399 /* Free the key material */
400 silc_ske_free_key_material(keymat);
402 /* If we are setting the key without a request from the remote client,
403 we will send request to remote. */
404 if (!client_entry->internal.prv_resp)
405 silc_client_send_private_message_key_request(client, conn, client_entry);
410 /* Same as above but takes the key material from the SKE key material
413 SilcBool silc_client_add_private_message_key_ske(SilcClient client,
414 SilcClientConnection conn,
415 SilcClientEntry client_entry,
418 SilcSKEKeyMaterial keymat)
420 if (!client || !client_entry)
423 /* Return FALSE if key already set */
424 if (client_entry->internal.send_key && client_entry->internal.receive_key)
428 cipher = SILC_DEFAULT_CIPHER;
430 hmac = SILC_DEFAULT_HMAC;
432 /* Check the requested cipher and HMAC */
433 if (!silc_cipher_is_supported(cipher))
435 if (!silc_hmac_is_supported(hmac))
438 client_entry->internal.generated = TRUE;
440 /* Allocate the cipher and HMAC */
441 if (!silc_cipher_alloc(cipher, &client_entry->internal.send_key))
443 if (!silc_cipher_alloc(cipher, &client_entry->internal.receive_key))
445 if (!silc_hmac_alloc(hmac, NULL, &client_entry->internal.hmac_send))
447 if (!silc_hmac_alloc(hmac, NULL, &client_entry->internal.hmac_receive))
451 if (client_entry->internal.prv_resp) {
452 silc_cipher_set_key(client_entry->internal.send_key,
453 keymat->receive_enc_key,
454 keymat->enc_key_len, TRUE);
455 silc_cipher_set_iv(client_entry->internal.send_key,
457 silc_cipher_set_key(client_entry->internal.receive_key,
458 keymat->send_enc_key,
459 keymat->enc_key_len, FALSE);
460 silc_cipher_set_iv(client_entry->internal.receive_key, keymat->send_iv);
461 silc_hmac_set_key(client_entry->internal.hmac_send,
462 keymat->receive_hmac_key,
463 keymat->hmac_key_len);
464 silc_hmac_set_key(client_entry->internal.hmac_receive,
465 keymat->send_hmac_key,
466 keymat->hmac_key_len);
468 silc_cipher_set_key(client_entry->internal.send_key,
469 keymat->send_enc_key,
470 keymat->enc_key_len, TRUE);
471 silc_cipher_set_iv(client_entry->internal.send_key,
473 silc_cipher_set_key(client_entry->internal.receive_key,
474 keymat->receive_enc_key,
475 keymat->enc_key_len, FALSE);
476 silc_cipher_set_iv(client_entry->internal.receive_key, keymat->receive_iv);
477 silc_hmac_set_key(client_entry->internal.hmac_send,
478 keymat->send_hmac_key,
479 keymat->hmac_key_len);
480 silc_hmac_set_key(client_entry->internal.hmac_receive,
481 keymat->receive_hmac_key,
482 keymat->hmac_key_len);
488 /* Removes the private message from the library. The key won't be used
489 after this to protect the private messages with the remote `client_entry'
490 client. Returns FALSE on error, TRUE otherwise. */
492 SilcBool silc_client_del_private_message_key(SilcClient client,
493 SilcClientConnection conn,
494 SilcClientEntry client_entry)
496 if (!client || !client_entry)
499 if (!client_entry->internal.send_key && !client_entry->internal.receive_key)
502 silc_cipher_free(client_entry->internal.send_key);
503 silc_cipher_free(client_entry->internal.receive_key);
505 if (client_entry->internal.key) {
506 memset(client_entry->internal.key, 0, client_entry->internal.key_len);
507 silc_free(client_entry->internal.key);
510 client_entry->internal.send_key = NULL;
511 client_entry->internal.receive_key = NULL;
512 client_entry->internal.key = NULL;
513 client_entry->internal.prv_resp = FALSE;
518 /* Returns array of set private message keys associated to the connection
519 `conn'. Returns allocated SilcPrivateMessageKeys array and the array
520 count to the `key_count' argument. The array must be freed by the caller
521 by calling the silc_client_free_private_message_keys function. Note:
522 the keys returned in the array is in raw format. It might not be desired
523 to show the keys as is. The application might choose not to show the keys
524 at all or to show the fingerprints of the keys. */
526 SilcPrivateMessageKeys
527 silc_client_list_private_message_keys(SilcClient client,
528 SilcClientConnection conn,
529 SilcUInt32 *key_count)
531 SilcPrivateMessageKeys keys;
532 SilcUInt32 count = 0;
534 SilcIDCacheEntry id_cache;
535 SilcClientEntry entry;
537 if (!client || !conn)
540 silc_mutex_lock(conn->internal->lock);
541 if (!silc_idcache_get_all(conn->internal->client_cache, &list)) {
542 silc_mutex_unlock(conn->internal->lock);
546 keys = silc_calloc(silc_list_count(list), sizeof(*keys));
548 silc_mutex_unlock(conn->internal->lock);
552 silc_list_start(list);
553 while ((id_cache = silc_list_get(list))) {
554 entry = id_cache->context;
555 if (entry->internal.send_key) {
556 keys[count].client_entry = entry;
557 keys[count].cipher = (char *)silc_cipher_get_name(entry->internal.
559 keys[count].key = (entry->internal.generated == FALSE ?
560 entry->internal.key : NULL);
561 keys[count].key_len = (entry->internal.generated == FALSE ?
562 entry->internal.key_len : 0);
567 silc_mutex_unlock(conn->internal->lock);
575 /* Frees the SilcPrivateMessageKeys array returned by the function
576 silc_client_list_private_message_keys. */
578 void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
579 SilcUInt32 key_count)
584 /* Sets away `message'. The away message may be set when the client's
585 mode is changed to SILC_UMODE_GONE and the client whishes to reply
586 to anyone who sends private message. The `message' will be sent
587 automatically back to the the client who send private message. If
588 away message is already set this replaces the old message with the
589 new one. If `message' is NULL the old away message is removed.
590 The sender may freely free the memory of the `message'. */
592 void silc_client_set_away_message(SilcClient client,
593 SilcClientConnection conn,
596 assert(client && conn);
598 if (!message && conn->internal->away) {
599 silc_free(conn->internal->away->away);
600 silc_free(conn->internal->away);
601 conn->internal->away = NULL;
605 if (!conn->internal->away)
606 conn->internal->away = silc_calloc(1, sizeof(*conn->internal->away));
607 if (conn->internal->away->away)
608 silc_free(conn->internal->away->away);
609 conn->internal->away->away = strdup(message);