077d867d4d3b515be047ae6e3302f79b9011b58c
[crypto.git] / lib / silccore / silcattrs.c
1 /*
2
3   silcattrs.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2002 - 2007 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; version 2 of the License.
12
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.
17
18 */
19 /* Implementation of Attribute Payload routines */
20 /* $Id$ */
21
22 #include "silc.h"
23 #include "silcattrs.h"
24
25 /******************************************************************************
26
27                              Attribute Payload
28
29 ******************************************************************************/
30
31 struct SilcAttributePayloadStruct {
32   SilcAttribute attribute;
33   SilcAttributeFlags flags;
34   SilcUInt16 data_len;
35   unsigned char *data;
36 };
37
38 /* Internal routine for encoding a attribute */
39
40 static unsigned char *
41 silc_attribute_payload_encode_int(SilcAttribute attribute,
42                                   SilcAttributeFlags flags,
43                                   void *object,
44                                   SilcUInt32 object_size,
45                                   SilcUInt32 *ret_len)
46 {
47   SilcBuffer tmpbuf = NULL;
48   unsigned char tmp[4], *str = NULL, *ret;
49   SilcUInt32 len;
50
51   /* Encode according to attribute type */
52   if (flags & SILC_ATTRIBUTE_FLAG_VALID) {
53     if (!object && !object_size)
54       return NULL;
55
56     switch (attribute) {
57
58     case SILC_ATTRIBUTE_USER_INFO:
59       {
60         SilcVCard vcard = object;
61         if (object_size != sizeof(*vcard))
62           return NULL;
63         str = silc_vcard_encode(vcard, &object_size);
64         if (!str)
65           return NULL;
66         object = str;
67       }
68       break;
69
70     case SILC_ATTRIBUTE_SERVICE:
71       {
72         SilcAttributeObjService *service = object;
73         SilcUInt32 len2;
74         if (object_size != sizeof(*service))
75           return NULL;
76         len = strlen(service->address);
77         len2 = strlen(service->signon);
78         tmpbuf = silc_buffer_alloc_size(13 + len + len2);
79         if (!tmpbuf)
80           return NULL;
81         silc_buffer_format(tmpbuf,
82                            SILC_STR_UI_INT(service->port),
83                            SILC_STR_UI_SHORT(len),
84                            SILC_STR_UI_XNSTRING(service->address, len),
85                            SILC_STR_UI_CHAR(service->status),
86                            SILC_STR_UI_SHORT(len2),
87                            SILC_STR_UI_XNSTRING(service->signon, len2),
88                            SILC_STR_UI_INT(service->idle),
89                            SILC_STR_END);
90         object = tmpbuf->data;
91         object_size = silc_buffer_len(tmpbuf);
92       }
93       break;
94
95     case SILC_ATTRIBUTE_STATUS_MOOD:
96     case SILC_ATTRIBUTE_PREFERRED_CONTACT:
97       {
98         SilcUInt32 mask = SILC_PTR_TO_32(object);
99         if (object_size != sizeof(SilcUInt32))
100           return NULL;
101         SILC_PUT32_MSB(mask, tmp);
102         object = tmp;
103         object_size = sizeof(SilcUInt32);
104       }
105       break;
106
107     case SILC_ATTRIBUTE_STATUS_FREETEXT:
108     case SILC_ATTRIBUTE_PREFERRED_LANGUAGE:
109     case SILC_ATTRIBUTE_TIMEZONE:
110       {
111         unsigned char *string = object;
112         str = silc_malloc(2 + object_size);
113         if (!str)
114           return NULL;
115         SILC_PUT16_MSB(object_size, str);
116         memcpy(str + 2, string, object_size);
117         object = str;
118         object_size += 2;
119       }
120       break;
121
122     case SILC_ATTRIBUTE_STATUS_MESSAGE:
123     case SILC_ATTRIBUTE_EXTENSION:
124     case SILC_ATTRIBUTE_USER_ICON:
125       {
126         SilcMime mime = object;
127         if (object_size != sizeof(*mime))
128           return NULL;
129         str = silc_mime_encode(mime, &object_size);
130         if (!str)
131           return NULL;
132       }
133       break;
134
135     case SILC_ATTRIBUTE_GEOLOCATION:
136       {
137         SilcAttributeObjGeo *geo = object;
138         SilcUInt32 len1, len2, len3, len4;
139         if (object_size != sizeof(*geo))
140           return NULL;
141         len1 = (geo->longitude ? strlen(geo->longitude) : 0);
142         len2 = (geo->latitude  ? strlen(geo->latitude)  : 0);
143         len3 = (geo->altitude  ? strlen(geo->altitude)  : 0);
144         len4 = (geo->accuracy  ? strlen(geo->accuracy)  : 0);
145         if (len1 + len2 + len3 + len4 == 0)
146           return NULL;
147         len = len1 + len2 + len3 + len4;
148         tmpbuf = silc_buffer_alloc_size(8 + len);
149         if (!tmpbuf)
150           return NULL;
151         silc_buffer_format(tmpbuf,
152                            SILC_STR_UI_SHORT(len1),
153                            SILC_STR_UI16_STRING(len1 ? geo->longitude : ""),
154                            SILC_STR_UI_SHORT(len2),
155                            SILC_STR_UI16_STRING(len2 ? geo->latitude : ""),
156                            SILC_STR_UI_SHORT(len3),
157                            SILC_STR_UI16_STRING(len3 ? geo->altitude : ""),
158                            SILC_STR_UI_SHORT(len4),
159                            SILC_STR_UI16_STRING(len4 ? geo->accuracy : ""),
160                            SILC_STR_END);
161         object = tmpbuf->data;
162         object_size = silc_buffer_len(tmpbuf);
163       }
164       break;
165
166     case SILC_ATTRIBUTE_DEVICE_INFO:
167       {
168         SilcAttributeObjDevice *dev = object;
169         SilcUInt32 len1, len2, len3, len4;
170         if (object_size != sizeof(*dev))
171           return NULL;
172         len1 = (dev->manufacturer ? strlen(dev->manufacturer) : 0);
173         len2 = (dev->version      ? strlen(dev->version)      : 0);
174         len3 = (dev->model        ? strlen(dev->model)        : 0);
175         len4 = (dev->language     ? strlen(dev->language)     : 0);
176         if (len1 + len2 + len3 + len4 == 0)
177           return NULL;
178         len = len1 + len2 + len3 + len4;
179         tmpbuf = silc_buffer_alloc_size(4 + 8 + len);
180         if (!tmpbuf)
181           return NULL;
182         silc_buffer_format(tmpbuf,
183                            SILC_STR_UI_INT(dev->type),
184                            SILC_STR_UI_SHORT(len1),
185                            SILC_STR_UI16_STRING(len1 ? dev->manufacturer : ""),
186                            SILC_STR_UI_SHORT(len2),
187                            SILC_STR_UI16_STRING(len2 ? dev->version : ""),
188                            SILC_STR_UI_SHORT(len3),
189                            SILC_STR_UI16_STRING(len3 ? dev->model : ""),
190                            SILC_STR_UI_SHORT(len4),
191                            SILC_STR_UI16_STRING(len4 ? dev->language : ""),
192                            SILC_STR_END);
193         object = tmpbuf->data;
194         object_size = silc_buffer_len(tmpbuf);
195       }
196       break;
197
198     case SILC_ATTRIBUTE_PHONE_NUMBER:
199       {
200         SilcAttributeObjPN *pn = object;
201         if (object_size != sizeof(*pn))
202           return NULL;
203         if (!pn->number || strlen(pn->number) < 5)
204           return NULL;
205         tmpbuf = silc_buffer_alloc(0);
206         if (!tmpbuf)
207           return NULL;
208         if (silc_buffer_format(tmpbuf,
209                                SILC_STR_UI_INT(pn->format),
210                                SILC_STR_UI_SHORT(strlen(pn->number)),
211                                SILC_STR_UI16_STRING(pn->number),
212                                SILC_STR_END) < 0)
213           return NULL;
214         object = tmpbuf->data;
215         object_size = silc_buffer_len(tmpbuf);
216       }
217       break;
218
219     case SILC_ATTRIBUTE_USER_PUBLIC_KEY:
220     case SILC_ATTRIBUTE_SERVER_PUBLIC_KEY:
221       {
222         SilcAttributeObjPk *pk = object;
223         if (object_size != sizeof(*pk))
224           return NULL;
225         len = (pk->type ? strlen(pk->type) : 0);
226         tmpbuf = silc_buffer_alloc_size(2 + len + pk->data_len);
227         if (!tmpbuf)
228           return NULL;
229         silc_buffer_format(tmpbuf,
230                            SILC_STR_UI_SHORT(len),
231                            SILC_STR_UI16_STRING(pk->type),
232                            SILC_STR_UI_XNSTRING(pk->data, pk->data_len),
233                            SILC_STR_END);
234         object = tmpbuf->data;
235         object_size = silc_buffer_len(tmpbuf);
236       }
237       break;
238
239     case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE:
240     case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE:
241       {
242         SilcAttributeObjPk *pk = object;
243         if (object_size != sizeof(*pk))
244           return NULL;
245         object = pk->data;
246         object_size = pk->data_len;
247       }
248       break;
249
250     default:
251       return NULL;
252       break;
253     }
254
255     ret = silc_memdup(object, object_size);
256
257     if (tmpbuf)
258       silc_buffer_free(tmpbuf);
259     silc_free(str);
260
261     if (ret_len)
262       *ret_len = object_size;
263
264     return ret;
265   }
266
267   return NULL;
268 }
269
270 /* Allocates attribute payload and encodes the attribute there */
271
272 SilcAttributePayload silc_attribute_payload_alloc(SilcAttribute attribute,
273                                                   SilcAttributeFlags flags,
274                                                   void *object,
275                                                   SilcUInt32 object_size)
276 {
277   SilcAttributePayload attr;
278   SilcUInt32 tmp_len;
279
280   attr = silc_calloc(1, sizeof(*attr));
281   if (!attr)
282     return NULL;
283
284   attr->attribute = attribute;
285   attr->flags = flags;
286   attr->data =
287     silc_attribute_payload_encode_int(attribute, flags, object,
288                                       object_size, &tmp_len);
289   attr->data_len = (SilcUInt16)tmp_len;
290   if (!attr->data) {
291     silc_free(attr);
292     return NULL;
293   }
294
295   return attr;
296 }
297
298 /* Parse list of payloads */
299
300 SilcDList silc_attribute_payload_parse(const unsigned char *payload,
301                                        SilcUInt32 payload_len)
302 {
303   SilcBufferStruct buffer;
304   SilcDList list;
305   SilcAttributePayload newp;
306   SilcUInt32 len;
307   int ret;
308
309   SILC_LOG_DEBUG(("Parsing Attribute Payload list"));
310
311   silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
312   list = silc_dlist_init();
313
314   while (silc_buffer_len(&buffer)) {
315     newp = silc_calloc(1, sizeof(*newp));
316     if (!newp)
317       goto err;
318     ret = silc_buffer_unformat(&buffer,
319                                SILC_STR_UI_CHAR(&newp->attribute),
320                                SILC_STR_UI_CHAR(&newp->flags),
321                                SILC_STR_UI16_NSTRING_ALLOC(&newp->data,
322                                                            &newp->data_len),
323                                SILC_STR_END);
324     if (ret == -1)
325       goto err;
326
327     if (newp->data_len > silc_buffer_len(&buffer) - 4) {
328       SILC_LOG_ERROR(("Incorrect attribute payload in list"));
329       goto err;
330     }
331
332     len = 4 + newp->data_len;
333     if (silc_buffer_len(&buffer) < len)
334       break;
335     silc_buffer_pull(&buffer, len);
336
337     silc_dlist_add(list, newp);
338   }
339
340   return list;
341
342  err:
343   silc_attribute_payload_list_free(list);
344   return NULL;
345 }
346
347 /* Encode one attribute payload to buffer */
348
349 SilcBuffer silc_attribute_payload_encode(SilcBuffer attrs,
350                                          SilcAttribute attribute,
351                                          SilcAttributeFlags flags,
352                                          void *object,
353                                          SilcUInt32 object_size)
354 {
355   object = silc_attribute_payload_encode_int(attribute, flags, object,
356                                              object_size, &object_size);
357   attrs = silc_attribute_payload_encode_data(attrs, attribute, flags,
358                                              (const unsigned char *)object,
359                                              object_size);
360   silc_free(object);
361   return attrs;
362 }
363
364 /* Encoded the attribute data directly to buffer */
365
366 SilcBuffer silc_attribute_payload_encode_data(SilcBuffer attrs,
367                                               SilcAttribute attribute,
368                                               SilcAttributeFlags flags,
369                                               const unsigned char *data,
370                                               SilcUInt32 data_len)
371 {
372   SilcBuffer buffer = attrs;
373   SilcUInt32 len;
374
375   len = 4 + (SilcUInt16)data_len;
376   buffer = silc_buffer_realloc(buffer,
377                                (buffer ? silc_buffer_truelen(buffer) +
378                                 len : len));
379   if (!buffer)
380     return NULL;
381   silc_buffer_pull(buffer, silc_buffer_len(buffer));
382   silc_buffer_pull_tail(buffer, len);
383   silc_buffer_format(buffer,
384                      SILC_STR_UI_CHAR(attribute),
385                      SILC_STR_UI_CHAR(flags),
386                      SILC_STR_UI_SHORT((SilcUInt16)data_len),
387                      SILC_STR_UI_XNSTRING(data, (SilcUInt16)data_len),
388                      SILC_STR_END);
389   silc_buffer_push(buffer, buffer->data - buffer->head);
390
391   return buffer;
392 }
393
394 /* Free Attribute Payload */
395
396 void silc_attribute_payload_free(SilcAttributePayload payload)
397 {
398   silc_free(payload->data);
399   silc_free(payload);
400 }
401
402 /* Free's list of Attribute Payloads */
403
404 void silc_attribute_payload_list_free(SilcDList list)
405 {
406   SilcAttributePayload entry;
407
408   silc_dlist_start(list);
409   while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
410     silc_attribute_payload_free(entry);
411     silc_dlist_del(list, entry);
412   }
413
414   silc_dlist_uninit(list);
415 }
416
417 /* Return attribute type */
418
419 SilcAttribute silc_attribute_get_attribute(SilcAttributePayload payload)
420 {
421   return payload->attribute;
422 }
423
424 /* Return attribute flags */
425
426 SilcAttributeFlags silc_attribute_get_flags(SilcAttributePayload payload)
427 {
428   return payload->flags;
429 }
430
431 /* Return attribute data from the payload */
432
433 const unsigned char *silc_attribute_get_data(SilcAttributePayload payload,
434                                              SilcUInt32 *data_len)
435 {
436   if (data_len)
437     *data_len = (SilcUInt32)payload->data_len;
438   return (const unsigned char *)payload->data;
439 }
440
441 /* Construct digital signature verification data */
442
443 unsigned char *silc_attribute_get_verify_data(SilcDList attrs,
444                                               SilcBool server_verification,
445                                               SilcUInt32 *data_len)
446 {
447   SilcAttributePayload attr;
448   SilcBufferStruct buffer;
449   unsigned char *data = NULL;
450   SilcUInt32 len = 0;
451
452   silc_dlist_start(attrs);
453   while ((attr = silc_dlist_get(attrs)) != SILC_LIST_END) {
454     switch (attr->attribute) {
455     case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE:
456       /* Server signature is never part of the verification data */
457       break;
458
459     case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE:
460       /* For user signature verification this is not part of the data */
461       if (!server_verification)
462         break;
463
464       /* Fallback, for server signature verification, user digital signature
465          is part of verification data. */
466
467     default:
468       /* All other data is part of the verification data */
469       data = silc_realloc(data, sizeof(*data) * (4 + attr->data_len + len));
470       if (!data)
471         return NULL;
472       silc_buffer_set(&buffer, data + len, 4 + attr->data_len);
473       silc_buffer_format(&buffer,
474                          SILC_STR_UI_CHAR(attr->attribute),
475                          SILC_STR_UI_CHAR(attr->flags),
476                          SILC_STR_UI_SHORT(attr->data_len),
477                          SILC_STR_UI_XNSTRING(attr->data, attr->data_len),
478                          SILC_STR_END);
479       len += 4 + attr->data_len;
480       break;
481     }
482   }
483
484   if (data_len)
485     *data_len = len;
486
487   return data;
488 }
489
490 /* Return parsed attribute object */
491
492 SilcBool silc_attribute_get_object(SilcAttributePayload payload,
493                                    void *object, SilcUInt32 object_size)
494 {
495   SilcUInt16 len;
496   SilcBool ret = FALSE;
497
498   if (!object || payload->flags & SILC_ATTRIBUTE_FLAG_INVALID)
499     return FALSE;
500
501   switch (payload->attribute) {
502   case SILC_ATTRIBUTE_USER_INFO:
503     {
504       SilcVCard vcard = object;
505       if (object_size != sizeof(*vcard))
506         break;
507       if (!silc_vcard_decode(payload->data, payload->data_len, vcard))
508         break;
509       ret = TRUE;
510     }
511     break;
512
513   case SILC_ATTRIBUTE_SERVICE:
514     {
515       SilcAttributeObjService *service = object;
516       SilcBufferStruct buf;
517       SilcUInt16 addr_len, signon_len;
518       char *addr, *signon;
519       int res;
520       if (object_size != sizeof(*service))
521         break;
522       if (payload->data_len < 13)
523         break;
524       silc_buffer_set(&buf, payload->data, payload->data_len);
525       res = silc_buffer_unformat(&buf,
526                                  SILC_STR_UI_INT(&service->port),
527                                  SILC_STR_UI16_NSTRING(&addr, &addr_len),
528                                  SILC_STR_UI_CHAR(&service->status),
529                                  SILC_STR_UI16_NSTRING(&signon, &signon_len),
530                                  SILC_STR_UI_INT(&service->idle),
531                                  SILC_STR_END);
532       if (res == -1)
533         break;
534       memset(service->address, 0, sizeof(service->address));
535       memset(service->signon, 0, sizeof(service->signon));
536       memcpy(service->address, addr,
537              (addr_len < sizeof(service->address) - 1 ? addr_len :
538               sizeof(service->address) - 1));
539       memcpy(service->signon, signon,
540              (signon_len < sizeof(service->signon) - 1 ? signon_len :
541               sizeof(service->signon) - 1));
542       ret = TRUE;
543     }
544     break;
545
546   case SILC_ATTRIBUTE_STATUS_MOOD:
547   case SILC_ATTRIBUTE_PREFERRED_CONTACT:
548     {
549       SilcUInt32 *mask = (SilcUInt32 *)object;
550       if (object_size != sizeof(SilcUInt32))
551         break;
552       if (payload->data_len < 4)
553         break;
554       SILC_GET32_MSB(*mask, payload->data);
555       ret = TRUE;
556     }
557     break;
558
559   case SILC_ATTRIBUTE_STATUS_FREETEXT:
560   case SILC_ATTRIBUTE_PREFERRED_LANGUAGE:
561   case SILC_ATTRIBUTE_TIMEZONE:
562     {
563       char *string = object;
564       if (payload->data_len < 2)
565         break;
566       SILC_GET16_MSB(len, payload->data);
567       if (payload->data_len < 2 + len)
568         break;
569       if (object_size < len)
570         break;
571       memcpy(string, payload->data + 2, len);
572       ret = TRUE;
573     }
574     break;
575
576   case SILC_ATTRIBUTE_STATUS_MESSAGE:
577   case SILC_ATTRIBUTE_EXTENSION:
578   case SILC_ATTRIBUTE_USER_ICON:
579     {
580       SilcMime mime = object;
581       if (object_size != sizeof(*mime))
582         break;
583       if (!silc_mime_decode(mime, payload->data, payload->data_len))
584         break;
585       ret = TRUE;
586     }
587     break;
588
589   case SILC_ATTRIBUTE_GEOLOCATION:
590     {
591       SilcAttributeObjGeo *geo = object;
592       SilcBufferStruct buffer;
593       int res;
594       if (object_size != sizeof(*geo))
595         break;
596       silc_buffer_set(&buffer, (unsigned char *)payload->data,
597                       payload->data_len);
598       res = silc_buffer_unformat(&buffer,
599                                  SILC_STR_UI16_STRING_ALLOC(&geo->longitude),
600                                  SILC_STR_UI16_STRING_ALLOC(&geo->latitude),
601                                  SILC_STR_UI16_STRING_ALLOC(&geo->altitude),
602                                  SILC_STR_UI16_STRING_ALLOC(&geo->accuracy),
603                                  SILC_STR_END);
604       if (res == -1)
605         break;
606       ret = TRUE;
607     }
608     break;
609
610   case SILC_ATTRIBUTE_DEVICE_INFO:
611     {
612       SilcAttributeObjDevice *dev = object;
613       SilcBufferStruct buffer;
614       SilcUInt32 type;
615       int res;
616       if (object_size != sizeof(*dev))
617         break;
618       silc_buffer_set(&buffer, (unsigned char *)payload->data,
619                       payload->data_len);
620       res =
621         silc_buffer_unformat(&buffer,
622                              SILC_STR_UI_INT(&type),
623                              SILC_STR_UI16_STRING_ALLOC(&dev->manufacturer),
624                              SILC_STR_UI16_STRING_ALLOC(&dev->version),
625                              SILC_STR_UI16_STRING_ALLOC(&dev->model),
626                              SILC_STR_UI16_STRING_ALLOC(&dev->language),
627                              SILC_STR_END);
628       if (res == -1)
629         break;
630       dev->type = type;
631       ret = TRUE;
632     }
633     break;
634
635   case SILC_ATTRIBUTE_PHONE_NUMBER:
636     {
637       SilcAttributeObjPN *pn = object;
638       SilcBufferStruct buffer;
639       SilcUInt32 pn_format;
640       int res;
641       if (object_size != sizeof(*pn))
642         break;
643       silc_buffer_set(&buffer, (unsigned char *)payload->data,
644                       payload->data_len);
645       res =
646         silc_buffer_unformat(&buffer,
647                              SILC_STR_UI_INT(&pn_format),
648                              SILC_STR_UI16_STRING_ALLOC(&pn->number),
649                              SILC_STR_END);
650       if (res == -1)
651         break;
652       pn->format = pn_format;
653       ret = TRUE;
654     }
655     break;
656
657   case SILC_ATTRIBUTE_USER_PUBLIC_KEY:
658   case SILC_ATTRIBUTE_SERVER_PUBLIC_KEY:
659     {
660       SilcAttributeObjPk *pk = object;
661       SilcBufferStruct buffer;
662       int res;
663       if (object_size != sizeof(*pk))
664         break;
665       silc_buffer_set(&buffer, (unsigned char *)payload->data,
666                       payload->data_len);
667       res =
668         silc_buffer_unformat(&buffer,
669                              SILC_STR_UI16_NSTRING_ALLOC(&pk->type, &len),
670                              SILC_STR_END);
671       if (res == -1 || len > silc_buffer_len(&buffer) - 2)
672         break;
673       pk->data = silc_memdup(payload->data + 2 + len,
674                              payload->data_len - 2 - len);
675       pk->data_len = payload->data_len - 2 - len;
676       ret = TRUE;
677     }
678     break;
679
680   case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE:
681   case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE:
682     {
683       SilcAttributeObjPk *pk = object;
684       if (object_size != sizeof(*pk))
685         break;
686       pk->type = NULL;
687       pk->data = silc_memdup(payload->data, payload->data_len);
688       pk->data_len = payload->data_len;
689       ret = TRUE;
690     }
691     break;
692
693   default:
694     break;
695   }
696
697   return ret;
698 }