/* silcattrs.c Author: Pekka Riikonen Copyright (C) 2002 - 2004 Pekka Riikonen The contents of this file are subject to one of the Licenses specified in the COPYING file; You may not use this file except in compliance with the License. The software distributed under the License is distributed on an "AS IS" basis, in the hope that it will be useful, but WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the COPYING file for more information. */ /* Implementation of Attribute Payload routines */ /* $Id: silcattrs.c,v 1.13.2.4 2005/11/21 18:24:38 priikone Exp $ */ #include "silcincludes.h" #include "silcattrs.h" /****************************************************************************** Attribute Payload ******************************************************************************/ struct SilcAttributePayloadStruct { SilcAttribute attribute; SilcAttributeFlags flags; SilcUInt16 data_len; unsigned char *data; }; /* Internal routine for encoding a attribute */ static unsigned char * silc_attribute_payload_encode_int(SilcAttribute attribute, SilcAttributeFlags flags, void *object, SilcUInt32 object_size, SilcUInt32 *ret_len) { SilcBuffer tmpbuf = NULL; unsigned char tmp[4], *str = NULL, *ret; SilcUInt32 len; /* Encode according to attribute type */ if (flags & SILC_ATTRIBUTE_FLAG_VALID) { if (!object && !object_size) return NULL; switch (attribute) { case SILC_ATTRIBUTE_USER_INFO: { SilcVCard vcard = object; if (object_size != sizeof(*vcard)) return NULL; str = silc_vcard_encode(vcard, &object_size); if (!str) return NULL; object = str; } break; case SILC_ATTRIBUTE_SERVICE: { SilcAttributeObjService *service = object; SilcUInt32 len2; if (object_size != sizeof(*service)) return NULL; len = strlen(service->address); len2 = strlen(service->signon); tmpbuf = silc_buffer_alloc_size(13 + len + len2); silc_buffer_format(tmpbuf, SILC_STR_UI_INT(service->port), SILC_STR_UI_SHORT(len), SILC_STR_UI_XNSTRING(service->address, len), SILC_STR_UI_CHAR(service->status), SILC_STR_UI_SHORT(len2), SILC_STR_UI_XNSTRING(service->signon, len2), SILC_STR_UI_INT(service->idle), SILC_STR_END); object = tmpbuf->data; object_size = tmpbuf->len; } break; case SILC_ATTRIBUTE_STATUS_MOOD: case SILC_ATTRIBUTE_PREFERRED_CONTACT: { SilcUInt32 mask = SILC_PTR_TO_32(object); if (object_size != sizeof(SilcUInt32)) return NULL; SILC_PUT32_MSB(mask, tmp); object = tmp; object_size = sizeof(SilcUInt32); } break; case SILC_ATTRIBUTE_STATUS_FREETEXT: case SILC_ATTRIBUTE_PREFERRED_LANGUAGE: case SILC_ATTRIBUTE_TIMEZONE: { unsigned char *string = object; str = silc_malloc(2 + object_size); if (!str) return NULL; SILC_PUT16_MSB(object_size, str); memcpy(str + 2, string, object_size); object = str; object_size += 2; } break; case SILC_ATTRIBUTE_STATUS_MESSAGE: case SILC_ATTRIBUTE_EXTENSION: case SILC_ATTRIBUTE_USER_ICON: { SilcAttributeObjMime *mime = object; if (object_size != sizeof(*mime)) return NULL; object = (void *)mime->mime; object_size = mime->mime_len; } break; case SILC_ATTRIBUTE_GEOLOCATION: { SilcAttributeObjGeo *geo = object; SilcUInt32 len1, len2, len3, len4; if (object_size != sizeof(*geo)) return NULL; len1 = (geo->longitude ? strlen(geo->longitude) : 0); len2 = (geo->latitude ? strlen(geo->latitude) : 0); len3 = (geo->altitude ? strlen(geo->altitude) : 0); len4 = (geo->accuracy ? strlen(geo->accuracy) : 0); if (len1 + len2 + len3 + len4 == 0) return NULL; len = len1 + len2 + len3 + len4; tmpbuf = silc_buffer_alloc_size(8 + len); if (!tmpbuf) return NULL; silc_buffer_format(tmpbuf, SILC_STR_UI_SHORT(len1), SILC_STR_UI16_STRING(len1 ? geo->longitude : ""), SILC_STR_UI_SHORT(len2), SILC_STR_UI16_STRING(len2 ? geo->latitude : ""), SILC_STR_UI_SHORT(len3), SILC_STR_UI16_STRING(len3 ? geo->altitude : ""), SILC_STR_UI_SHORT(len4), SILC_STR_UI16_STRING(len4 ? geo->accuracy : ""), SILC_STR_END); object = tmpbuf->data; object_size = tmpbuf->len; } break; case SILC_ATTRIBUTE_DEVICE_INFO: { SilcAttributeObjDevice *dev = object; SilcUInt32 len1, len2, len3, len4; if (object_size != sizeof(*dev)) return NULL; len1 = (dev->manufacturer ? strlen(dev->manufacturer) : 0); len2 = (dev->version ? strlen(dev->version) : 0); len3 = (dev->model ? strlen(dev->model) : 0); len4 = (dev->language ? strlen(dev->language) : 0); if (len1 + len2 + len3 + len4 == 0) return NULL; len = len1 + len2 + len3 + len4; tmpbuf = silc_buffer_alloc_size(4 + 8 + len); if (!tmpbuf) return NULL; silc_buffer_format(tmpbuf, SILC_STR_UI_INT(dev->type), SILC_STR_UI_SHORT(len1), SILC_STR_UI16_STRING(len1 ? dev->manufacturer : ""), SILC_STR_UI_SHORT(len2), SILC_STR_UI16_STRING(len2 ? dev->version : ""), SILC_STR_UI_SHORT(len3), SILC_STR_UI16_STRING(len3 ? dev->model : ""), SILC_STR_UI_SHORT(len4), SILC_STR_UI16_STRING(len4 ? dev->language : ""), SILC_STR_END); object = tmpbuf->data; object_size = tmpbuf->len; } break; case SILC_ATTRIBUTE_USER_PUBLIC_KEY: case SILC_ATTRIBUTE_SERVER_PUBLIC_KEY: { SilcAttributeObjPk *pk = object; if (object_size != sizeof(*pk)) return NULL; len = (pk->type ? strlen(pk->type) : 0); tmpbuf = silc_buffer_alloc_size(2 + len + pk->data_len); if (!tmpbuf) return NULL; silc_buffer_format(tmpbuf, SILC_STR_UI_SHORT(len), SILC_STR_UI16_STRING(pk->type), SILC_STR_UI_XNSTRING(pk->data, pk->data_len), SILC_STR_END); object = tmpbuf->data; object_size = tmpbuf->len; } break; case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE: case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE: { SilcAttributeObjPk *pk = object; if (object_size != sizeof(*pk)) return NULL; object = pk->data; object_size = pk->data_len; } break; default: return NULL; break; } ret = silc_memdup(object, object_size); if (tmpbuf) silc_buffer_free(tmpbuf); silc_free(str); if (ret_len) *ret_len = object_size; return ret; } return NULL; } /* Allocates attribute payload and encodes the attribute there */ SilcAttributePayload silc_attribute_payload_alloc(SilcAttribute attribute, SilcAttributeFlags flags, void *object, SilcUInt32 object_size) { SilcAttributePayload attr; SilcUInt32 tmp_len; attr = silc_calloc(1, sizeof(*attr)); if (!attr) return NULL; attr->attribute = attribute; attr->flags = flags; attr->data = silc_attribute_payload_encode_int(attribute, flags, object, object_size, &tmp_len); attr->data_len = (SilcUInt16)tmp_len; if (!attr->data) { silc_free(attr); return NULL; } return attr; } /* Parse list of payloads */ SilcDList silc_attribute_payload_parse(const unsigned char *payload, SilcUInt32 payload_len) { SilcBufferStruct buffer; SilcDList list; SilcAttributePayload newp; SilcUInt32 len; int ret; SILC_LOG_DEBUG(("Parsing Attribute Payload list")); silc_buffer_set(&buffer, (unsigned char *)payload, payload_len); list = silc_dlist_init(); while (buffer.len) { newp = silc_calloc(1, sizeof(*newp)); if (!newp) goto err; ret = silc_buffer_unformat(&buffer, SILC_STR_UI_CHAR(&newp->attribute), SILC_STR_UI_CHAR(&newp->flags), SILC_STR_UI16_NSTRING_ALLOC(&newp->data, &newp->data_len), SILC_STR_END); if (ret == -1) goto err; if (newp->data_len > buffer.len - 4) { SILC_LOG_ERROR(("Incorrect attribute payload in list")); goto err; } len = 4 + newp->data_len; if (buffer.len < len) break; silc_buffer_pull(&buffer, len); silc_dlist_add(list, newp); } return list; err: silc_attribute_payload_list_free(list); return NULL; } /* Encode one attribute payload to buffer */ SilcBuffer silc_attribute_payload_encode(SilcBuffer attrs, SilcAttribute attribute, SilcAttributeFlags flags, void *object, SilcUInt32 object_size) { object = silc_attribute_payload_encode_int(attribute, flags, object, object_size, &object_size); attrs = silc_attribute_payload_encode_data(attrs, attribute, flags, (const unsigned char *)object, object_size); silc_free(object); return attrs; } /* Encoded the attribute data directly to buffer */ SilcBuffer silc_attribute_payload_encode_data(SilcBuffer attrs, SilcAttribute attribute, SilcAttributeFlags flags, const unsigned char *data, SilcUInt32 data_len) { SilcBuffer buffer = attrs; SilcUInt32 len; len = 4 + (SilcUInt16)data_len; buffer = silc_buffer_realloc(buffer, (buffer ? buffer->truelen + len : len)); if (!buffer) return NULL; silc_buffer_pull(buffer, buffer->len); silc_buffer_pull_tail(buffer, len); silc_buffer_format(buffer, SILC_STR_UI_CHAR(attribute), SILC_STR_UI_CHAR(flags), SILC_STR_UI_SHORT((SilcUInt16)data_len), SILC_STR_UI_XNSTRING(data, (SilcUInt16)data_len), SILC_STR_END); silc_buffer_push(buffer, buffer->data - buffer->head); return buffer; } /* Free Attribute Payload */ void silc_attribute_payload_free(SilcAttributePayload payload) { silc_free(payload->data); silc_free(payload); } /* Free's list of Attribute Payloads */ void silc_attribute_payload_list_free(SilcDList list) { SilcAttributePayload entry; silc_dlist_start(list); while ((entry = silc_dlist_get(list)) != SILC_LIST_END) { silc_attribute_payload_free(entry); silc_dlist_del(list, entry); } silc_dlist_uninit(list); } /* Return attribute type */ SilcAttribute silc_attribute_get_attribute(SilcAttributePayload payload) { return payload->attribute; } /* Return attribute flags */ SilcAttributeFlags silc_attribute_get_flags(SilcAttributePayload payload) { return payload->flags; } /* Return attribute data from the payload */ const unsigned char *silc_attribute_get_data(SilcAttributePayload payload, SilcUInt32 *data_len) { if (data_len) *data_len = (SilcUInt32)payload->data_len; return (const unsigned char *)payload->data; } /* Construct digital signature verification data */ unsigned char *silc_attribute_get_verify_data(SilcDList attrs, bool server_verification, SilcUInt32 *data_len) { SilcAttributePayload attr; SilcBufferStruct buffer; unsigned char *data = NULL; SilcUInt32 len = 0; silc_dlist_start(attrs); while ((attr = silc_dlist_get(attrs)) != SILC_LIST_END) { switch (attr->attribute) { case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE: /* Server signature is never part of the verification data */ break; case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE: /* For user signature verification this is not part of the data */ if (!server_verification) break; /* Fallback, for server signature verification, user digital signature is part of verification data. */ default: /* All other data is part of the verification data */ data = silc_realloc(data, sizeof(*data) * (4 + attr->data_len + len)); if (!data) return NULL; silc_buffer_set(&buffer, data + len, 4 + attr->data_len); silc_buffer_format(&buffer, SILC_STR_UI_CHAR(attr->attribute), SILC_STR_UI_CHAR(attr->flags), SILC_STR_UI_SHORT(attr->data_len), SILC_STR_UI_XNSTRING(attr->data, attr->data_len), SILC_STR_END); len += 4 + attr->data_len; break; } } if (data_len) *data_len = len; return data; } /* Return parsed attribute object */ bool silc_attribute_get_object(SilcAttributePayload payload, void *object, SilcUInt32 object_size) { SilcUInt16 len; bool ret = FALSE; if (!object || payload->flags & SILC_ATTRIBUTE_FLAG_INVALID) return FALSE; switch (payload->attribute) { case SILC_ATTRIBUTE_USER_INFO: { SilcVCard vcard = object; if (object_size != sizeof(*vcard)) break; if (!silc_vcard_decode(payload->data, payload->data_len, vcard)) break; ret = TRUE; } break; case SILC_ATTRIBUTE_SERVICE: { SilcAttributeObjService *service = object; SilcBufferStruct buf; SilcUInt16 addr_len, signon_len; char *addr, *signon; int res; if (object_size != sizeof(*service)) break; if (payload->data_len < 13) break; silc_buffer_set(&buf, payload->data, payload->data_len); res = silc_buffer_unformat(&buf, SILC_STR_UI_INT(&service->port), SILC_STR_UI16_NSTRING(&addr, &addr_len), SILC_STR_UI_CHAR(&service->status), SILC_STR_UI16_NSTRING(&signon, &signon_len), SILC_STR_UI_INT(&service->idle), SILC_STR_END); if (res == -1) break; memset(service->address, 0, sizeof(service->address)); memset(service->signon, 0, sizeof(service->signon)); memcpy(service->address, addr, (addr_len < sizeof(service->address) - 1 ? addr_len : sizeof(service->address) - 1)); memcpy(service->signon, signon, (signon_len < sizeof(service->signon) - 1 ? signon_len : sizeof(service->signon) - 1)); ret = TRUE; } break; case SILC_ATTRIBUTE_STATUS_MOOD: case SILC_ATTRIBUTE_PREFERRED_CONTACT: { SilcUInt32 *mask = (SilcUInt32 *)object; if (object_size != sizeof(SilcUInt32)) break; if (payload->data_len < 4) break; SILC_GET32_MSB(*mask, payload->data); ret = TRUE; } break; case SILC_ATTRIBUTE_STATUS_FREETEXT: case SILC_ATTRIBUTE_PREFERRED_LANGUAGE: case SILC_ATTRIBUTE_TIMEZONE: { char *string = object; if (payload->data_len < 2) break; SILC_GET16_MSB(len, payload->data); if (payload->data_len < 2 + len) break; if (object_size < len) break; memcpy(string, payload->data + 2, len); ret = TRUE; } break; case SILC_ATTRIBUTE_STATUS_MESSAGE: case SILC_ATTRIBUTE_EXTENSION: case SILC_ATTRIBUTE_USER_ICON: { SilcAttributeObjMime *mime = object; if (object_size != sizeof(*mime)) break; mime->mime = (const unsigned char *)payload->data; mime->mime_len = payload->data_len; ret = TRUE; } break; case SILC_ATTRIBUTE_GEOLOCATION: { SilcAttributeObjGeo *geo = object; SilcBufferStruct buffer; int res; if (object_size != sizeof(*geo)) break; silc_buffer_set(&buffer, (unsigned char *)payload->data, payload->data_len); res = silc_buffer_unformat(&buffer, SILC_STR_UI16_STRING_ALLOC(&geo->longitude), SILC_STR_UI16_STRING_ALLOC(&geo->latitude), SILC_STR_UI16_STRING_ALLOC(&geo->altitude), SILC_STR_UI16_STRING_ALLOC(&geo->accuracy), SILC_STR_END); if (res == -1) break; ret = TRUE; } break; case SILC_ATTRIBUTE_DEVICE_INFO: { SilcAttributeObjDevice *dev = object; SilcBufferStruct buffer; SilcUInt32 type; int res; if (object_size != sizeof(*dev)) break; silc_buffer_set(&buffer, (unsigned char *)payload->data, payload->data_len); res = silc_buffer_unformat(&buffer, SILC_STR_UI_INT(&type), SILC_STR_UI16_STRING_ALLOC(&dev->manufacturer), SILC_STR_UI16_STRING_ALLOC(&dev->version), SILC_STR_UI16_STRING_ALLOC(&dev->model), SILC_STR_UI16_STRING_ALLOC(&dev->language), SILC_STR_END); if (res == -1) break; dev->type = type; ret = TRUE; } break; case SILC_ATTRIBUTE_USER_PUBLIC_KEY: case SILC_ATTRIBUTE_SERVER_PUBLIC_KEY: { SilcAttributeObjPk *pk = object; SilcBufferStruct buffer; int res; if (object_size != sizeof(*pk)) break; silc_buffer_set(&buffer, (unsigned char *)payload->data, payload->data_len); res = silc_buffer_unformat(&buffer, SILC_STR_UI16_NSTRING_ALLOC(&pk->type, &len), SILC_STR_END); if (res == -1 || len > buffer.len - 2) break; pk->data = silc_memdup(payload->data + 2 + len, payload->data_len - 2 - len); pk->data_len = payload->data_len - 2 - len; ret = TRUE; } break; case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE: case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE: { SilcAttributeObjPk *pk = object; if (object_size != sizeof(*pk)) break; pk->type = NULL; pk->data = silc_memdup(payload->data, payload->data_len); pk->data_len = payload->data_len; ret = TRUE; } break; default: break; } return ret; }