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 ****************************/
29 SilcClientConnection conn;
30 SilcClientEntry client_entry;
31 } *SilcClientPrvmsgContext;
33 /* Message payload encoding callback */
35 static void silc_client_send_private_message_final(SilcBuffer message,
38 SilcClientPrvmsgContext p = context;
40 /* Send the private message packet */
42 silc_packet_send_ext(p->conn->stream, SILC_PACKET_PRIVATE_MESSAGE,
43 p->client_entry->internal.send_key ?
44 SILC_PACKET_FLAG_PRIVMSG_KEY : 0,
45 0, NULL, SILC_ID_CLIENT, &p->client_entry->id,
46 silc_buffer_datalen(message), NULL, NULL);
48 silc_client_unref_client(p->client, p->conn, p->client_entry);
52 /* Sends private message to remote client. */
54 SilcBool silc_client_send_private_message(SilcClient client,
55 SilcClientConnection conn,
56 SilcClientEntry client_entry,
57 SilcMessageFlags flags,
62 SilcClientPrvmsgContext p;
65 if (silc_unlikely(!client || !conn || !client_entry))
67 if (silc_unlikely(flags & SILC_MESSAGE_FLAG_SIGNED && !hash))
69 if (silc_unlikely(conn->internal->disconnected))
72 SILC_LOG_DEBUG(("Sending private message"));
74 sid.type = SILC_ID_CLIENT;
75 sid.u.client_id = conn->local_entry->id;
76 rid.type = SILC_ID_CLIENT;
77 rid.u.client_id = client_entry->id;
79 p = silc_calloc(1, sizeof(*p));
85 p->client_entry = silc_client_ref_client(client, conn, client_entry);
87 /* Encode private message payload */
88 silc_message_payload_encode(flags, data, data_len,
89 (!client_entry->internal.send_key ? FALSE :
90 !client_entry->internal.generated),
91 TRUE, client_entry->internal.send_key,
92 client_entry->internal.hmac_send,
93 client->rng, NULL, conn->private_key,
94 hash, &sid, &rid, NULL,
95 silc_client_send_private_message_final, p);
100 /************************* Private Message Receive **************************/
102 /* Client resolving callback. Continues with the private message processing */
104 static void silc_client_private_message_resolved(SilcClient client,
105 SilcClientConnection conn,
110 /* If no client found, ignore the private message, a silent error */
112 silc_fsm_next(context, silc_client_private_message_error);
114 /* Continue processing the private message packet */
115 SILC_FSM_CALL_CONTINUE(context);
118 /* Private message received. */
120 SILC_FSM_STATE(silc_client_private_message)
122 SilcClientConnection conn = fsm_context;
123 SilcClient client = conn->client;
124 SilcPacket packet = state_context;
125 SilcMessagePayload payload = NULL;
126 SilcClientID remote_id;
127 SilcClientEntry remote_client = NULL;
128 SilcMessageFlags flags;
129 unsigned char *message;
130 SilcUInt32 message_len;
132 SILC_LOG_DEBUG(("Received private message"));
134 if (silc_unlikely(packet->src_id_type != SILC_ID_CLIENT)) {
135 /** Invalid packet */
136 silc_fsm_next(fsm, silc_client_private_message_error);
137 return SILC_FSM_CONTINUE;
140 if (silc_unlikely(!silc_id_str2id(packet->src_id, packet->src_id_len,
141 SILC_ID_CLIENT, &remote_id,
142 sizeof(remote_id)))) {
143 /** Invalid source ID */
144 silc_fsm_next(fsm, silc_client_private_message_error);
145 return SILC_FSM_CONTINUE;
148 /* Check whether we know this client already */
149 remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
150 if (!remote_client || !remote_client->nickname[0]) {
151 /** Resolve client info */
152 silc_client_unref_client(client, conn, remote_client);
153 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
154 client, conn, &remote_id, NULL,
155 silc_client_private_message_resolved,
160 if (silc_unlikely(packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY &&
161 !remote_client->internal.receive_key &&
162 !remote_client->internal.hmac_receive))
165 /* Parse the payload and decrypt it also if private message key is set */
167 silc_message_payload_parse(silc_buffer_datalen(&packet->buffer),
168 TRUE, !remote_client->internal.generated,
169 remote_client->internal.receive_key,
170 remote_client->internal.hmac_receive,
171 packet->src_id, packet->src_id_len,
172 packet->dst_id, packet->dst_id_len,
174 if (silc_unlikely(!payload))
177 /* Pass the private message to application */
178 flags = silc_message_get_flags(payload);
179 message = silc_message_get_data(payload, &message_len);
180 client->internal->ops->private_message(client, conn, remote_client, payload,
181 flags, message, message_len);
183 /* See if we are away (gone). If we are away we will reply to the
184 sender with the set away message. */
185 if (conn->internal->away_message &&
186 !(flags & SILC_MESSAGE_FLAG_NOREPLY)) {
187 /* If it's me, ignore */
188 if (SILC_ID_CLIENT_COMPARE(&remote_id, conn->local_id))
191 /* Send the away message */
192 silc_client_send_private_message(client, conn, remote_client,
193 SILC_MESSAGE_FLAG_AUTOREPLY |
194 SILC_MESSAGE_FLAG_NOREPLY, NULL,
195 conn->internal->away_message,
196 strlen(conn->internal->away_message));
200 /** Packet processed */
201 silc_packet_free(packet);
202 silc_client_unref_client(client, conn, remote_client);
204 silc_message_payload_free(payload);
205 return SILC_FSM_FINISH;
208 /* Private message error. */
210 SILC_FSM_STATE(silc_client_private_message_error)
212 SilcPacket packet = state_context;
213 silc_packet_free(packet);
214 return SILC_FSM_FINISH;
217 /* Initialize private message waiter for the `conn' connection. */
219 SilcBool silc_client_private_message_wait_init(SilcClient client,
220 SilcClientConnection conn,
221 SilcClientEntry client_entry)
225 if (client_entry->internal.prv_waiter)
228 /* We want SILC_PACKET_PRIVATE_MESSAGE packets from this source ID. */
229 id.type = SILC_ID_CLIENT;
230 id.u.client_id = client_entry->id;
232 client_entry->internal.prv_waiter =
233 silc_packet_wait_init(conn->stream, &id, SILC_PACKET_PRIVATE_MESSAGE, -1);
234 if (!client_entry->internal.prv_waiter)
240 /* Uninitializes private message waiter. */
242 void silc_client_private_message_wait_uninit(SilcClient client,
243 SilcClientConnection conn,
244 SilcClientEntry client_entry)
246 if (!client_entry->internal.prv_waiter)
248 silc_packet_wait_uninit(client_entry->internal.prv_waiter, conn->stream);
249 client_entry->internal.prv_waiter = NULL;
252 /* Blocks the calling process or thread until private message has been
253 received from the specified client. */
255 SilcBool silc_client_private_message_wait(SilcClient client,
256 SilcClientConnection conn,
257 SilcClientEntry client_entry,
258 SilcMessagePayload *payload)
262 if (!client_entry->internal.prv_waiter)
265 /* Block until private message arrives */
267 if ((silc_packet_wait(client_entry->internal.prv_waiter, 0, &packet)) < 0)
270 /* Parse the payload and decrypt it also if private message key is set */
272 silc_message_payload_parse(silc_buffer_data(&packet->buffer),
273 silc_buffer_len(&packet->buffer),
274 TRUE, !client_entry->internal.generated,
275 client_entry->internal.receive_key,
276 client_entry->internal.hmac_receive,
277 packet->src_id, packet->src_id_len,
278 packet->dst_id, packet->dst_id_len,
281 silc_packet_free(packet);
288 silc_packet_free(packet);
292 /*************************** Private Message Key ****************************/
294 /* Sends private message key request. Sender of this packet is initiator
295 when setting the private message key. */
298 silc_client_send_private_message_key_request(SilcClient client,
299 SilcClientConnection conn,
300 SilcClientEntry client_entry)
302 const char *cipher, *hmac;
304 SILC_LOG_DEBUG(("Sending private message key request"));
306 cipher = silc_cipher_get_name(client_entry->internal.send_key);
307 hmac = silc_hmac_get_name(client_entry->internal.hmac_send);
309 /* Send the packet */
310 return silc_packet_send_va_ext(conn->stream,
311 SILC_PACKET_PRIVATE_MESSAGE_KEY,
312 0, 0, NULL, SILC_ID_CLIENT,
313 &client_entry->id, NULL, NULL,
314 SILC_STR_UI_SHORT(strlen(cipher)),
315 SILC_STR_DATA(cipher, strlen(cipher)),
316 SILC_STR_UI_SHORT(strlen(hmac)),
317 SILC_STR_DATA(hmac, strlen(hmac)),
321 /* Client resolving callback. Here we simply mark that we are the responder
322 side of this private message key request. */
324 static void silc_client_private_message_key_cb(SilcClient client,
325 SilcClientConnection conn,
330 SilcFSMThread thread = context;
331 SilcPacket packet = silc_fsm_get_state_context(thread);
332 unsigned char *cipher = NULL, *hmac = NULL;
333 SilcClientEntry client_entry;
337 silc_packet_free(packet);
338 silc_fsm_finish(thread);
342 /* Parse the private message key payload */
343 ret = silc_buffer_unformat(&packet->buffer,
344 SILC_STR_UI16_STRING_ALLOC(&cipher),
345 SILC_STR_UI16_STRING_ALLOC(&hmac),
350 /* Mark that we are responder */
351 client_entry = silc_dlist_get(clients);
352 client_entry->internal.prv_resp = TRUE;
354 /* XXX we should notify application that remote wants to set up the
355 static key. And we should tell if we already have key with remote.
356 Application should return status telling whether to delete the key
362 silc_packet_free(packet);
363 silc_fsm_finish(thread);
366 /* Processes incoming Private Message Key payload to indicate that the
367 sender whishes to set up a static private message key. */
369 SILC_FSM_STATE(silc_client_private_message_key)
371 SilcClientConnection conn = fsm_context;
372 SilcClient client = conn->client;
373 SilcPacket packet = state_context;
374 SilcClientID remote_id;
376 if (packet->src_id_type != SILC_ID_CLIENT) {
377 silc_packet_free(packet);
378 return SILC_FSM_FINISH;
381 if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
382 &remote_id, sizeof(remote_id))) {
383 silc_packet_free(packet);
384 return SILC_FSM_FINISH;
387 /* Always resolve the remote client. The actual packet is processed
388 in the resolving callback. */
389 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
390 client, conn, &remote_id, NULL,
391 silc_client_private_message_key_cb,
395 /* Adds new private message key to `client_entry'. If we are setting this
396 before receiving request for it from `client_entry' we will send the
397 request to the client. Otherwise, we are responder side. */
399 SilcBool silc_client_add_private_message_key(SilcClient client,
400 SilcClientConnection conn,
401 SilcClientEntry client_entry,
407 SilcSKEKeyMaterial keymat;
410 if (!client || !client_entry)
413 /* Return FALSE if key already set */
414 if (client_entry->internal.send_key && client_entry->internal.receive_key)
418 cipher = SILC_DEFAULT_CIPHER;
420 hmac = SILC_DEFAULT_HMAC;
422 /* Check the requested cipher and HMAC */
423 if (!silc_cipher_is_supported(cipher))
425 if (!silc_hmac_is_supported(hmac))
429 client_entry->internal.key = silc_memdup(key, key_len);
430 client_entry->internal.key_len = key_len;
432 /* Produce the key material as the protocol defines */
433 keymat = silc_ske_process_key_material_data(key, key_len, 16, 256, 16,
434 conn->internal->sha1hash);
438 /* Set the key into use */
439 ret = silc_client_add_private_message_key_ske(client, conn, client_entry,
440 cipher, hmac, keymat);
441 client_entry->internal.generated = FALSE;
443 /* Free the key material */
444 silc_ske_free_key_material(keymat);
446 /* If we are setting the key without a request from the remote client,
447 we will send request to remote. */
448 if (!client_entry->internal.prv_resp)
449 silc_client_send_private_message_key_request(client, conn, client_entry);
454 /* Same as above but takes the key material from the SKE key material
457 SilcBool silc_client_add_private_message_key_ske(SilcClient client,
458 SilcClientConnection conn,
459 SilcClientEntry client_entry,
462 SilcSKEKeyMaterial keymat)
464 if (!client || !client_entry)
467 /* Return FALSE if key already set */
468 if (client_entry->internal.send_key && client_entry->internal.receive_key)
472 cipher = SILC_DEFAULT_CIPHER;
474 hmac = SILC_DEFAULT_HMAC;
476 /* Check the requested cipher and HMAC */
477 if (!silc_cipher_is_supported(cipher))
479 if (!silc_hmac_is_supported(hmac))
482 client_entry->internal.generated = TRUE;
484 /* Allocate the cipher and HMAC */
485 if (!silc_cipher_alloc(cipher, &client_entry->internal.send_key))
487 if (!silc_cipher_alloc(cipher, &client_entry->internal.receive_key))
489 if (!silc_hmac_alloc(hmac, NULL, &client_entry->internal.hmac_send))
491 if (!silc_hmac_alloc(hmac, NULL, &client_entry->internal.hmac_receive))
495 if (client_entry->internal.prv_resp) {
496 silc_cipher_set_key(client_entry->internal.send_key,
497 keymat->receive_enc_key,
498 keymat->enc_key_len, TRUE);
499 silc_cipher_set_iv(client_entry->internal.send_key,
501 silc_cipher_set_key(client_entry->internal.receive_key,
502 keymat->send_enc_key,
503 keymat->enc_key_len, FALSE);
504 silc_cipher_set_iv(client_entry->internal.receive_key, keymat->send_iv);
505 silc_hmac_set_key(client_entry->internal.hmac_send,
506 keymat->receive_hmac_key,
507 keymat->hmac_key_len);
508 silc_hmac_set_key(client_entry->internal.hmac_receive,
509 keymat->send_hmac_key,
510 keymat->hmac_key_len);
512 silc_cipher_set_key(client_entry->internal.send_key,
513 keymat->send_enc_key,
514 keymat->enc_key_len, TRUE);
515 silc_cipher_set_iv(client_entry->internal.send_key,
517 silc_cipher_set_key(client_entry->internal.receive_key,
518 keymat->receive_enc_key,
519 keymat->enc_key_len, FALSE);
520 silc_cipher_set_iv(client_entry->internal.receive_key, keymat->receive_iv);
521 silc_hmac_set_key(client_entry->internal.hmac_send,
522 keymat->send_hmac_key,
523 keymat->hmac_key_len);
524 silc_hmac_set_key(client_entry->internal.hmac_receive,
525 keymat->receive_hmac_key,
526 keymat->hmac_key_len);
532 /* Removes the private message from the library. The key won't be used
533 after this to protect the private messages with the remote `client_entry'
534 client. Returns FALSE on error, TRUE otherwise. */
536 SilcBool silc_client_del_private_message_key(SilcClient client,
537 SilcClientConnection conn,
538 SilcClientEntry client_entry)
540 if (!client || !client_entry)
543 if (!client_entry->internal.send_key && !client_entry->internal.receive_key)
546 silc_cipher_free(client_entry->internal.send_key);
547 silc_cipher_free(client_entry->internal.receive_key);
549 if (client_entry->internal.key) {
550 memset(client_entry->internal.key, 0, client_entry->internal.key_len);
551 silc_free(client_entry->internal.key);
554 client_entry->internal.send_key = NULL;
555 client_entry->internal.receive_key = NULL;
556 client_entry->internal.key = NULL;
557 client_entry->internal.prv_resp = FALSE;
562 /* Returns array of set private message keys associated to the connection
563 `conn'. Returns allocated SilcPrivateMessageKeys array and the array
564 count to the `key_count' argument. The array must be freed by the caller
565 by calling the silc_client_free_private_message_keys function. Note:
566 the keys returned in the array is in raw format. It might not be desired
567 to show the keys as is. The application might choose not to show the keys
568 at all or to show the fingerprints of the keys. */
570 SilcPrivateMessageKeys
571 silc_client_list_private_message_keys(SilcClient client,
572 SilcClientConnection conn,
573 SilcUInt32 *key_count)
575 SilcPrivateMessageKeys keys;
576 SilcUInt32 count = 0;
578 SilcIDCacheEntry id_cache;
579 SilcClientEntry entry;
581 if (!client || !conn)
584 silc_mutex_lock(conn->internal->lock);
585 if (!silc_idcache_get_all(conn->internal->client_cache, &list)) {
586 silc_mutex_unlock(conn->internal->lock);
590 keys = silc_calloc(silc_list_count(list), sizeof(*keys));
592 silc_mutex_unlock(conn->internal->lock);
596 silc_list_start(list);
597 while ((id_cache = silc_list_get(list))) {
598 entry = id_cache->context;
599 if (entry->internal.send_key) {
600 keys[count].client_entry = entry;
601 keys[count].cipher = (char *)silc_cipher_get_name(entry->internal.
603 keys[count].key = (entry->internal.generated == FALSE ?
604 entry->internal.key : NULL);
605 keys[count].key_len = (entry->internal.generated == FALSE ?
606 entry->internal.key_len : 0);
611 silc_mutex_unlock(conn->internal->lock);
619 /* Frees the SilcPrivateMessageKeys array returned by the function
620 silc_client_list_private_message_keys. */
622 void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
623 SilcUInt32 key_count)
628 /* Return private message key from the client entry. */
631 silc_client_private_message_key_is_set(SilcClient client,
632 SilcClientConnection conn,
633 SilcClientEntry client_entry)
635 return client_entry->internal.send_key != NULL;
638 /* Sets away `message'. The away message may be set when the client's
639 mode is changed to SILC_UMODE_GONE and the client whishes to reply
640 to anyone who sends private message. The `message' will be sent
641 automatically back to the the client who send private message. If
642 away message is already set this replaces the old message with the
643 new one. If `message' is NULL the old away message is removed.
644 The sender may freely free the memory of the `message'. */
646 SilcBool silc_client_set_away_message(SilcClient client,
647 SilcClientConnection conn,
650 if (!client || !conn)
654 silc_free(conn->internal->away_message);
655 conn->internal->away_message = NULL;
659 if (conn->internal->away_message)
660 silc_free(conn->internal->away_message);
662 conn->internal->away_message = strdup(message);
663 if (!conn->internal->away_message)