/* client_attrs.c Author: Pekka Riikonen Copyright (C) 2002 - 2007 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ /* $Id$ */ #include "silc.h" #include "silcclient.h" #include "client_internal.h" typedef struct { SilcBuffer buffer; SilcPKCSSignCb sign_cb; void *context; } *SilcAttrSign; typedef struct { SilcBuffer buffer; } SilcAttrForeach; /* Add one attribute that was found from hash table */ static void silc_client_attributes_process_foreach(void *key, void *context, void *user_context) { SilcAttribute attribute = (SilcAttribute)SILC_PTR_TO_32(key); SilcAttributePayload attr = context; SilcAttrForeach *f = user_context; const unsigned char *data; unsigned char tmp[32]; SilcUInt32 data_len; if (!context) { SILC_LOG_DEBUG(("Attribute %d was not set", attribute)); /* USER_PUBLIC_KEY we have set earlier */ if (attribute == SILC_ATTRIBUTE_USER_PUBLIC_KEY) return; /* The requested attribute was not found */ f->buffer = silc_attribute_payload_encode(f->buffer, attribute, SILC_ATTRIBUTE_FLAG_INVALID, NULL, 0); return; } SILC_LOG_DEBUG(("Attribute %d found", attribute)); data = silc_attribute_get_data(attr, &data_len); /* We replace the TIMEZONE with valid value here */ if (attribute == SILC_ATTRIBUTE_TIMEZONE) { if (silc_timezone(tmp, sizeof(tmp))) { data = tmp; data_len = strlen(tmp); f->buffer = silc_attribute_payload_encode(f->buffer, attribute, SILC_ATTRIBUTE_FLAG_VALID, (void *)data, data_len); } return; } f->buffer = silc_attribute_payload_encode_data(f->buffer, attribute, SILC_ATTRIBUTE_FLAG_VALID, data, data_len); } /* Attribute signature callback */ static void silc_client_attributes_process_signed(SilcBool success, const unsigned char *signature, SilcUInt32 signature_len, void *context) { SilcAttrSign s = context; SilcAttributeObjPk pk; if (success) { pk.type = NULL; pk.data = (unsigned char *)signature; pk.data_len = signature_len; s->buffer = silc_attribute_payload_encode(s->buffer, SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE, SILC_ATTRIBUTE_FLAG_VALID, &pk, sizeof(pk)); } s->sign_cb(TRUE, silc_buffer_data(s->buffer), silc_buffer_len(s->buffer), s->context); silc_buffer_free(s->buffer); silc_free(s); } /* Process list of attributes. Returns reply to the requested attributes. */ void silc_client_attributes_process(SilcClient client, SilcClientConnection conn, SilcDList attrs, SilcPKCSSignCb sign_cb, void *context) { SilcAttrSign s; SilcBuffer buffer = NULL; SilcAttrForeach f; SilcAttribute attribute; SilcAttributePayload attr; SilcAttributeObjPk pk; SILC_LOG_DEBUG(("Process Requested Attributes")); /* If nothing is set by application assume that we don't want to use attributes, ignore the request. */ if (!conn->internal->attrs) { SILC_LOG_DEBUG(("User has not set any attributes")); sign_cb(FALSE, NULL, 0, context); return; } /* Always put our public key. */ pk.type = "silc-rsa"; pk.data = silc_pkcs_public_key_encode(NULL, conn->public_key, &pk.data_len); buffer = silc_attribute_payload_encode(buffer, SILC_ATTRIBUTE_USER_PUBLIC_KEY, pk.data ? SILC_ATTRIBUTE_FLAG_VALID : SILC_ATTRIBUTE_FLAG_INVALID, &pk, sizeof(pk)); silc_free(pk.data); /* Go through all requested attributes */ f.buffer = buffer; silc_dlist_start(attrs); while ((attr = silc_dlist_get(attrs)) != SILC_LIST_END) { /* Put all attributes of this type */ attribute = silc_attribute_get_attribute(attr); /* Ignore signature since we will compute it later */ if (attribute == SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE) continue; silc_hash_table_find_foreach(conn->internal->attrs, SILC_32_TO_PTR(attribute), silc_client_attributes_process_foreach, &f); } buffer = f.buffer; s = silc_calloc(1, sizeof(*s)); if (!s) { sign_cb(FALSE, NULL, 0, context); return; } s->sign_cb = sign_cb; s->context = context; s->buffer = buffer; /* Finally compute the digital signature of all the data we provided. */ silc_pkcs_sign_async(conn->private_key, silc_buffer_data(buffer), silc_buffer_len(buffer), TRUE, NULL, client->rng, silc_client_attributes_process_signed, s); } static void silc_client_attribute_destruct(void *key, void *context, void *user_context) { silc_attribute_payload_free(context); } /* Add new attribute */ SilcAttributePayload silc_client_attribute_add(SilcClient client, SilcClientConnection conn, SilcAttribute attribute, void *object, SilcUInt32 object_size) { SilcAttributePayload attr; attr = silc_attribute_payload_alloc(attribute, SILC_ATTRIBUTE_FLAG_VALID, object, object_size); if (!attr) return NULL; if (!conn->internal->attrs) conn->internal->attrs = silc_hash_table_alloc(NULL, 0, silc_hash_ptr, NULL, NULL, NULL, silc_client_attribute_destruct, NULL, TRUE); silc_hash_table_add(conn->internal->attrs, SILC_32_TO_PTR(attribute), attr); return attr; } static void silc_client_attribute_del_foreach(void *key, void *context, void *user_context) { SilcClientConnection conn = user_context; SilcAttributePayload attr = context; SilcAttribute attribute; if (!attr) return; attribute = silc_attribute_get_attribute(attr); silc_hash_table_del_by_context(conn->internal->attrs, SILC_32_TO_PTR(attribute), attr); } /* Delete one attribute */ SilcBool silc_client_attribute_del(SilcClient client, SilcClientConnection conn, SilcAttribute attribute, SilcAttributePayload attr) { SilcBool ret; if (!conn->internal->attrs) return FALSE; if (attr) { attribute = silc_attribute_get_attribute(attr); ret = silc_hash_table_del_by_context(conn->internal->attrs, SILC_32_TO_PTR(attribute), attr); } else if (attribute) { silc_hash_table_find_foreach(conn->internal->attrs, SILC_32_TO_PTR(attribute), silc_client_attribute_del_foreach, conn); ret = TRUE; } else{ return FALSE; } if (ret) if (!silc_hash_table_count(conn->internal->attrs)) { silc_hash_table_free(conn->internal->attrs); conn->internal->attrs = NULL; } return ret; } /* Return all attributes */ SilcHashTable silc_client_attributes_get(SilcClient client, SilcClientConnection conn) { return conn->internal->attrs; } /* Construct a Requested Attributes buffer. If the `attribute' is zero (0) then all attributes are requested. Additionally `attribute' and all variable arguments can be one requested attribute. Always set the last requested attribute to zero (0) to complete list of requested attribute. */ SilcBuffer silc_client_attributes_request(SilcAttribute attribute, ...) { va_list va; SilcBuffer buffer = NULL; if (!attribute) return silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO, SILC_ATTRIBUTE_USER_ICON, SILC_ATTRIBUTE_SERVICE, SILC_ATTRIBUTE_STATUS_MOOD, SILC_ATTRIBUTE_STATUS_FREETEXT, SILC_ATTRIBUTE_STATUS_MESSAGE, SILC_ATTRIBUTE_PREFERRED_LANGUAGE, SILC_ATTRIBUTE_PREFERRED_CONTACT, SILC_ATTRIBUTE_TIMEZONE, SILC_ATTRIBUTE_GEOLOCATION, SILC_ATTRIBUTE_DEVICE_INFO, SILC_ATTRIBUTE_USER_PUBLIC_KEY, 0); va_start(va, attribute); while (attribute) { buffer = silc_attribute_payload_encode(buffer, attribute, 0, NULL, 0); attribute = (SilcAttribute)va_arg(va, SilcUInt32); } va_end(va); return buffer; }