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