updates.
[silc.git] / lib / silcclient / client_prvmsg.c
1 /*
2
3   client_prvmsg.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2001 Pekka Riikonen
8
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; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* $Id$ */
21 /* This file includes the private message sending and receiving routines
22    and private message key handling routines. */
23
24 #include "clientlibincludes.h"
25 #include "client_internal.h"
26
27 /* Sends private message to remote client. If private message key has
28    not been set with this client then the message will be encrypted using
29    normal session keys. Private messages are special packets in SILC
30    network hence we need this own function for them. This is similiar
31    to silc_client_packet_send_to_channel except that we send private
32    message. The `data' is the private message. If the `force_send' is
33    TRUE the packet is sent immediately. */
34
35 void silc_client_send_private_message(SilcClient client,
36                                       SilcClientConnection conn,
37                                       SilcClientEntry client_entry,
38                                       SilcMessageFlags flags,
39                                       unsigned char *data, 
40                                       uint32 data_len, 
41                                       int force_send)
42 {
43   SilcSocketConnection sock = conn->sock;
44   SilcBuffer buffer;
45   SilcPacketContext packetdata;
46   SilcCipher cipher;
47   SilcHmac hmac;
48
49   SILC_LOG_DEBUG(("Sending private message"));
50
51   /* Encode private message payload */
52   buffer = silc_private_message_payload_encode(flags,
53                                                data_len, data,
54                                                client_entry->send_key);
55
56   /* If we don't have private message specific key then private messages
57      are just as any normal packet thus call normal packet sending.  If
58      the key exist then the encryption process is a bit different and
59      will be done in the rest of this function. */
60   if (!client_entry->send_key) {
61     silc_client_packet_send(client, sock, SILC_PACKET_PRIVATE_MESSAGE,
62                             client_entry->id, SILC_ID_CLIENT, NULL, NULL,
63                             buffer->data, buffer->len, force_send);
64     goto out;
65   }
66
67   /* We have private message specific key */
68
69   /* Get data used in the encryption */
70   cipher = client_entry->send_key;
71   hmac = conn->hmac_send;
72
73   /* Set the packet context pointers. */
74   packetdata.flags = SILC_PACKET_FLAG_PRIVMSG_KEY;
75   packetdata.type = SILC_PACKET_PRIVATE_MESSAGE;
76   packetdata.src_id = conn->local_id_data;
77   packetdata.src_id_len = silc_id_get_len(conn->local_id, SILC_ID_CLIENT);
78   packetdata.src_id_type = SILC_ID_CLIENT;
79   packetdata.dst_id = silc_id_id2str(client_entry->id, SILC_ID_CLIENT);
80   packetdata.dst_id_len = silc_id_get_len(client_entry->id, SILC_ID_CLIENT);
81   packetdata.dst_id_type = SILC_ID_CLIENT;
82   packetdata.truelen = buffer->len + SILC_PACKET_HEADER_LEN + 
83     packetdata.src_id_len + packetdata.dst_id_len;
84   packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
85                                           packetdata.src_id_len +
86                                           packetdata.dst_id_len));
87
88   /* Prepare outgoing data buffer for packet sending */
89   silc_packet_send_prepare(sock, 
90                            SILC_PACKET_HEADER_LEN +
91                            packetdata.src_id_len + 
92                            packetdata.dst_id_len,
93                            packetdata.padlen,
94                            buffer->len);
95   
96   packetdata.buffer = sock->outbuf;
97
98   /* Put the actual encrypted message payload data into the buffer. */
99   silc_buffer_put(sock->outbuf, buffer->data, buffer->len);
100
101   /* Create the outgoing packet */
102   silc_packet_assemble(&packetdata);
103
104   /* Encrypt the header and padding of the packet. */
105   cipher = conn->send_key;
106   silc_packet_encrypt(cipher, hmac, sock->outbuf, SILC_PACKET_HEADER_LEN + 
107                       packetdata.src_id_len + packetdata.dst_id_len +
108                       packetdata.padlen);
109
110   SILC_LOG_HEXDUMP(("Private message packet, len %d", sock->outbuf->len),
111                    sock->outbuf->data, sock->outbuf->len);
112
113   /* Now actually send the packet */
114   silc_client_packet_send_real(client, sock, force_send, FALSE);
115   silc_free(packetdata.dst_id);
116
117  out:
118   silc_buffer_free(buffer);
119 }     
120
121 static void silc_client_private_message_cb(SilcClient client,
122                                            SilcClientConnection conn,
123                                            SilcClientEntry *clients,
124                                            uint32 clients_count,
125                                            void *context)
126 {
127   SilcPacketContext *packet = (SilcPacketContext *)context;
128
129   silc_client_private_message(client, conn->sock, packet);
130   silc_packet_context_free(packet);
131 }
132
133 /* Private message received. This processes the private message and
134    finally displays it on the screen. */
135
136 void silc_client_private_message(SilcClient client, 
137                                  SilcSocketConnection sock, 
138                                  SilcPacketContext *packet)
139 {
140   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
141   SilcPrivateMessagePayload payload = NULL;
142   SilcIDCacheEntry id_cache;
143   SilcClientID *remote_id = NULL;
144   SilcClientEntry remote_client;
145
146   if (packet->src_id_type != SILC_ID_CLIENT)
147     goto out;
148
149   remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, 
150                              SILC_ID_CLIENT);
151   if (!remote_id)
152     goto out;
153
154   /* Check whether we know this client already */
155   if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)remote_id, 
156                                        NULL, NULL, 
157                                        silc_hash_client_id_compare, NULL,
158                                        &id_cache)) {
159     /* Resolve the client info */
160     silc_client_get_client_by_id_resolve(client, conn, remote_id,
161                                          silc_client_private_message_cb,
162                                          silc_packet_context_dup(packet));
163     return;
164   }
165
166   remote_client = (SilcClientEntry)id_cache->context;
167
168   /* Parse the payload and decrypt it also if private message key is set */
169   payload = silc_private_message_payload_parse(packet->buffer,
170                                                remote_client->send_key);
171   if (!payload) {
172     silc_free(remote_id);
173     return;
174   }
175
176   /* Pass the private message to application */
177   client->ops->private_message(client, conn, remote_client,
178                                silc_private_message_get_flags(payload),
179                                silc_private_message_get_message(payload, 
180                                                                 NULL));
181
182   /* See if we are away (gone). If we are away we will reply to the
183      sender with the set away message. */
184   if (conn->away && conn->away->away) {
185     /* If it's me, ignore */
186     if (SILC_ID_CLIENT_COMPARE(remote_id, conn->local_id))
187       goto out;
188
189     /* Send the away message */
190     silc_client_send_private_message(client, conn, remote_client,
191                                      SILC_MESSAGE_FLAG_AUTOREPLY,
192                                      conn->away->away,
193                                      strlen(conn->away->away), TRUE);
194   }
195
196  out:
197   if (payload)
198     silc_private_message_payload_free(payload);
199   if (remote_id)
200     silc_free(remote_id);
201 }
202
203 /* Function that actually employes the received private message key */
204
205 static void silc_client_private_message_key_cb(SilcClient client,
206                                                SilcClientConnection conn,
207                                                SilcClientEntry *clients,
208                                                uint32 clients_count,
209                                                void *context)
210 {
211   SilcPacketContext *packet = (SilcPacketContext *)context;
212   unsigned char *key;
213   uint16 key_len;
214   unsigned char *cipher;
215   int ret;
216
217   if (!clients)
218     goto out;
219
220   /* Parse the private message key payload */
221   ret = silc_buffer_unformat(packet->buffer,
222                              SILC_STR_UI16_NSTRING(&key, &key_len),
223                              SILC_STR_UI16_STRING(&cipher),
224                              SILC_STR_END);
225   if (!ret)
226     goto out;
227
228   if (key_len > packet->buffer->len)
229     goto out;
230
231   /* Now take the key in use */
232   if (!silc_client_add_private_message_key(client, conn, clients[0],
233                                            cipher, key, key_len, FALSE))
234     goto out;
235
236   /* Print some info for application */
237   client->ops->say(client, conn, 
238                    "Received private message key from %s%s%s %s%s%s", 
239                    clients[0]->nickname,
240                    clients[0]->server ? "@" : "",
241                    clients[0]->server ? clients[0]->server : "",
242                    clients[0]->username ? "(" : "",
243                    clients[0]->username ? clients[0]->username : "",
244                    clients[0]->username ? ")" : "");
245
246  out:
247   silc_packet_context_free(packet);
248 }
249
250 /* Processes incoming Private Message Key payload. The libary always
251    accepts the key and takes it into use. */
252
253 void silc_client_private_message_key(SilcClient client,
254                                      SilcSocketConnection sock,
255                                      SilcPacketContext *packet)
256 {
257   SilcClientID *remote_id;
258
259   if (packet->src_id_type != SILC_ID_CLIENT)
260     return;
261
262   remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, 
263                              SILC_ID_CLIENT);
264   if (!remote_id)
265     return;
266
267   silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
268                                        silc_client_private_message_key_cb,
269                                        silc_packet_context_dup(packet));
270   silc_free(remote_id);
271 }
272
273 /* Adds private message key to the client library. The key will be used to
274    encrypt all private message between the client and the remote client
275    indicated by the `client_entry'. If the `key' is NULL and the boolean
276    value `generate_key' is TRUE the library will generate random key.
277    The `key' maybe for example pre-shared-key, passphrase or similar.
278    The `cipher' MAY be provided but SHOULD be NULL to assure that the
279    requirements of the SILC protocol are met. The API, however, allows
280    to allocate any cipher.
281
282    It is not necessary to set key for normal private message usage. If the
283    key is not set then the private messages are encrypted using normal
284    session keys. Setting the private key, however, increases the security. 
285
286    Returns FALSE if the key is already set for the `client_entry', TRUE
287    otherwise. */
288
289 int silc_client_add_private_message_key(SilcClient client,
290                                         SilcClientConnection conn,
291                                         SilcClientEntry client_entry,
292                                         char *cipher,
293                                         unsigned char *key,
294                                         uint32 key_len,
295                                         int generate_key)
296 {
297   unsigned char private_key[32];
298   uint32 len;
299   int i;
300   SilcSKEKeyMaterial *keymat;
301
302   assert(client_entry);
303
304   /* Return FALSE if key already set */
305   if (client_entry->send_key && client_entry->receive_key)
306     return FALSE;
307
308   if (!cipher)
309     cipher = "aes-256-cbc";
310
311   /* Check the requested cipher */
312   if (!silc_cipher_is_supported(cipher))
313     return FALSE;
314
315   /* Generate key if not provided */
316   if (generate_key == TRUE) {
317     len = 32;
318     for (i = 0; i < len; i++) private_key[i] = silc_rng_get_byte(client->rng);
319     key = private_key;
320     key_len = len;
321     client_entry->generated = TRUE;
322   }
323
324   /* Save the key */
325   client_entry->key = silc_calloc(key_len, sizeof(*client_entry->key));
326   memcpy(client_entry->key, key, key_len);
327   client_entry->key_len = key_len;
328
329   /* Produce the key material as the protocol defines */
330   keymat = silc_calloc(1, sizeof(*keymat));
331   if (silc_ske_process_key_material_data(key, key_len, 16, 256, 16, 
332                                          client->md5hash, keymat) 
333       != SILC_SKE_STATUS_OK)
334     return FALSE;
335
336   /* Allocate the ciphers */
337   silc_cipher_alloc(cipher, &client_entry->send_key);
338   silc_cipher_alloc(cipher, &client_entry->receive_key);
339
340   /* Set the keys */
341   silc_cipher_set_key(client_entry->send_key, keymat->send_enc_key,
342                       keymat->enc_key_len);
343   silc_cipher_set_iv(client_entry->send_key, keymat->send_iv);
344   silc_cipher_set_key(client_entry->receive_key, keymat->receive_enc_key,
345                       keymat->enc_key_len);
346   silc_cipher_set_iv(client_entry->receive_key, keymat->receive_iv);
347
348   /* Free the key material */
349   silc_ske_free_key_material(keymat);
350
351   return TRUE;
352 }
353
354 /* Same as above but takes the key material from the SKE key material
355    structure. This structure is received if the application uses the
356    silc_client_send_key_agreement to negotiate the key material. The
357    `cipher' SHOULD be provided as it is negotiated also in the SKE
358    protocol. */
359
360 int silc_client_add_private_message_key_ske(SilcClient client,
361                                             SilcClientConnection conn,
362                                             SilcClientEntry client_entry,
363                                             char *cipher,
364                                             SilcSKEKeyMaterial *key)
365 {
366   assert(client_entry);
367
368   /* Return FALSE if key already set */
369   if (client_entry->send_key && client_entry->receive_key)
370     return FALSE;
371
372   if (!cipher)
373     cipher = "aes-256-cbc";
374
375   /* Check the requested cipher */
376   if (!silc_cipher_is_supported(cipher))
377     return FALSE;
378
379   /* Allocate the ciphers */
380   silc_cipher_alloc(cipher, &client_entry->send_key);
381   silc_cipher_alloc(cipher, &client_entry->receive_key);
382
383   /* Set the keys */
384   silc_cipher_set_key(client_entry->send_key, key->send_enc_key,
385                       key->enc_key_len);
386   silc_cipher_set_iv(client_entry->send_key, key->send_iv);
387   silc_cipher_set_key(client_entry->receive_key, key->receive_enc_key,
388                       key->enc_key_len);
389   silc_cipher_set_iv(client_entry->receive_key, key->receive_iv);
390
391   return TRUE;
392 }
393
394 /* Sends private message key payload to the remote client indicated by
395    the `client_entry'. If the `force_send' is TRUE the packet is sent
396    immediately. Returns FALSE if error occurs, TRUE otherwise. The
397    application should call this function after setting the key to the
398    client.
399
400    Note that the key sent using this function is sent to the remote client
401    through the SILC network. The packet is protected using normal session
402    keys. */
403
404 int silc_client_send_private_message_key(SilcClient client,
405                                          SilcClientConnection conn,
406                                          SilcClientEntry client_entry,
407                                          int force_send)
408 {
409   SilcSocketConnection sock = conn->sock;
410   SilcBuffer buffer;
411   int cipher_len;
412
413   if (!client_entry->send_key || !client_entry->key)
414     return FALSE;
415
416   SILC_LOG_DEBUG(("Sending private message key"));
417
418   cipher_len = strlen(client_entry->send_key->cipher->name);
419
420   /* Create private message key payload */
421   buffer = silc_buffer_alloc(2 + client_entry->key_len);
422   silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
423   silc_buffer_format(buffer,
424                      SILC_STR_UI_SHORT(client_entry->key_len),
425                      SILC_STR_UI_XNSTRING(client_entry->key, 
426                                           client_entry->key_len),
427                      SILC_STR_UI_SHORT(cipher_len),
428                      SILC_STR_UI_XNSTRING(client_entry->send_key->cipher->name,
429                                           cipher_len),
430                      SILC_STR_END);
431
432   /* Send the packet */
433   silc_client_packet_send(client, sock, SILC_PACKET_PRIVATE_MESSAGE_KEY,
434                           client_entry->id, SILC_ID_CLIENT, NULL, NULL,
435                           buffer->data, buffer->len, force_send);
436   silc_free(buffer);
437
438   return TRUE;
439 }
440
441 /* Removes the private message from the library. The key won't be used
442    after this to protect the private messages with the remote `client_entry'
443    client. Returns FALSE on error, TRUE otherwise. */
444
445 int silc_client_del_private_message_key(SilcClient client,
446                                         SilcClientConnection conn,
447                                         SilcClientEntry client_entry)
448 {
449   assert(client_entry);
450
451   if (!client_entry->send_key && !client_entry->receive_key)
452     return FALSE;
453
454   silc_cipher_free(client_entry->send_key);
455   silc_cipher_free(client_entry->receive_key);
456
457   if (client_entry->key) {
458     memset(client_entry->key, 0, client_entry->key_len);
459     silc_free(client_entry->key);
460   }
461
462   client_entry->send_key = NULL;
463   client_entry->receive_key = NULL;
464   client_entry->key = NULL;
465
466   return TRUE;
467 }
468
469 /* Returns array of set private message keys associated to the connection
470    `conn'. Returns allocated SilcPrivateMessageKeys array and the array
471    count to the `key_count' argument. The array must be freed by the caller
472    by calling the silc_client_free_private_message_keys function. Note: 
473    the keys returned in the array is in raw format. It might not be desired
474    to show the keys as is. The application might choose not to show the keys
475    at all or to show the fingerprints of the keys. */
476
477 SilcPrivateMessageKeys
478 silc_client_list_private_message_keys(SilcClient client,
479                                       SilcClientConnection conn,
480                                       uint32 *key_count)
481 {
482   SilcPrivateMessageKeys keys;
483   uint32 count = 0;
484   SilcIDCacheEntry id_cache;
485   SilcIDCacheList list;
486   SilcClientEntry entry;
487
488   if (!silc_idcache_get_all(conn->client_cache, &list))
489     return NULL;
490
491   if (!silc_idcache_list_count(list)) {
492     silc_idcache_list_free(list);
493     return NULL;
494   }
495
496   keys = silc_calloc(silc_idcache_list_count(list), sizeof(*keys));
497
498   silc_idcache_list_first(list, &id_cache);
499   while (id_cache) {
500     entry = (SilcClientEntry)id_cache->context;
501
502     if (entry->send_key) {
503       keys[count].client_entry = entry;
504       keys[count].cipher = entry->send_key->cipher->name;
505       keys[count].key = entry->generated == FALSE ? entry->key : NULL;
506       keys[count].key_len = entry->generated == FALSE ? entry->key_len : 0;
507       count++;
508     }
509
510     if (!silc_idcache_list_next(list, &id_cache))
511       break;
512   }
513
514   if (key_count)
515     *key_count = count;
516
517   return keys;
518 }
519
520 /* Frees the SilcPrivateMessageKeys array returned by the function
521    silc_client_list_private_message_keys. */
522
523 void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
524                                            uint32 key_count)
525 {
526   silc_free(keys);
527 }
528
529 /* Sets away `message'.  The away message may be set when the client's
530    mode is changed to SILC_UMODE_GONE and the client whishes to reply
531    to anyone who sends private message.  The `message' will be sent
532    automatically back to the the client who send private message.  If
533    away message is already set this replaces the old message with the
534    new one.  If `message' is NULL the old away message is removed. 
535    The sender may freely free the memory of the `message'. */
536
537 void silc_client_set_away_message(SilcClient client,
538                                   SilcClientConnection conn,
539                                   char *message)
540 {
541   if (!message && conn->away) {
542     silc_free(conn->away->away);
543     silc_free(conn->away);
544     conn->away = NULL;
545   }
546
547   if (message) {
548     if (!conn->away)
549       conn->away = silc_calloc(1, sizeof(*conn->away));
550     if (conn->away->away)
551       silc_free(conn->away->away);
552     conn->away->away = strdup(message);
553   }
554 }