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