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