+/* This routine is used to reply to Requested Attributes in WHOIS on behalf
+ of the client since we were unable to resolve them from the client.
+ Either client does not support Requested Attributes or isn't replying
+ to them like it should. */
+
+SilcBuffer silc_server_query_reply_attrs(SilcServer server,
+ SilcServerQuery query,
+ SilcClientEntry client_entry)
+{
+ SilcBuffer buffer = NULL;
+ SilcAttribute attribute;
+ SilcAttributePayload attr;
+ SilcAttributeObjPk pk;
+ SilcAttributeObjService service;
+ unsigned char *tmp;
+ unsigned char sign[2048 + 1];
+ SilcUInt32 sign_len;
+
+ SILC_LOG_DEBUG(("Constructing Requested Attributes"));
+
+ /* Go through all requested attributes */
+ silc_dlist_start(query->attrs);
+ while ((attr = silc_dlist_get(query->attrs)) != SILC_LIST_END) {
+ attribute = silc_attribute_get_attribute(attr);
+ switch (attribute) {
+
+ case SILC_ATTRIBUTE_SERVICE:
+ /* Put SERVICE. Put only SILC service. */
+ memset(&service, 0, sizeof(service));
+ service.port = (server->config->server_info->primary ?
+ server->config->server_info->primary->port : SILC_PORT);
+ silc_strncat(service.address, sizeof(service.address),
+ server->server_name, strlen(server->server_name));
+ service.status = !(client_entry->mode & SILC_UMODE_DETACHED);
+ if (client_entry->connection)
+ service.idle = time(NULL) - client_entry->data.last_receive;
+ buffer = silc_attribute_payload_encode(buffer, attribute,
+ SILC_ATTRIBUTE_FLAG_VALID,
+ &service, sizeof(service));
+ if (!buffer)
+ return NULL;
+ break;
+
+ case SILC_ATTRIBUTE_STATUS_MOOD:
+ /* Put STATUS_MOOD */
+ buffer = silc_attribute_payload_encode(buffer, attribute,
+ SILC_ATTRIBUTE_FLAG_VALID,
+ (void *)
+ SILC_ATTRIBUTE_MOOD_NORMAL,
+ sizeof(SilcUInt32));
+ if (!buffer)
+ return NULL;
+ break;
+
+ case SILC_ATTRIBUTE_STATUS_FREETEXT:
+ /* Put STATUS_FREETEXT. We just tell in the message that we are
+ replying on behalf of the client. */
+ tmp =
+ "This information was provided by the server on behalf of the user";
+ buffer = silc_attribute_payload_encode(buffer, attribute,
+ SILC_ATTRIBUTE_FLAG_VALID,
+ tmp, strlen(tmp));
+ if (!buffer)
+ return NULL;
+ break;
+
+ case SILC_ATTRIBUTE_PREFERRED_CONTACT:
+ /* Put PREFERRED_CONTACT */
+ buffer = silc_attribute_payload_encode(buffer, attribute,
+ SILC_ATTRIBUTE_FLAG_VALID,
+ (void *)
+ SILC_ATTRIBUTE_CONTACT_CHAT,
+ sizeof(SilcUInt32));
+ if (!buffer)
+ return NULL;
+ break;
+
+ case SILC_ATTRIBUTE_USER_PUBLIC_KEY:
+ /* Put USER_PUBLIC_KEY */
+ if (client_entry->data.public_key) {
+ pk.type = "silc-rsa";
+ pk.data = silc_pkcs_public_key_encode(client_entry->data.public_key,
+ &pk.data_len);
+ buffer = silc_attribute_payload_encode(buffer, attribute, pk.data ?
+ SILC_ATTRIBUTE_FLAG_VALID :
+ SILC_ATTRIBUTE_FLAG_INVALID,
+ &pk, sizeof(pk));
+ silc_free(pk.data);
+ if (!buffer)
+ return NULL;
+ break;
+ }
+
+ /* No public key available */
+ buffer = silc_attribute_payload_encode(buffer, attribute,
+ SILC_ATTRIBUTE_FLAG_INVALID,
+ NULL, 0);
+ if (!buffer)
+ return NULL;
+ break;
+
+ default:
+ /* Ignore SERVER_PUBLIC_KEY since we are going to put it anyway later */
+ if (attribute == SILC_ATTRIBUTE_SERVER_PUBLIC_KEY ||
+ attribute == SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE)
+ break;
+
+ /* For other attributes we cannot reply so mark it invalid */
+ buffer = silc_attribute_payload_encode(buffer, attribute,
+ SILC_ATTRIBUTE_FLAG_INVALID,
+ NULL, 0);
+ if (!buffer)
+ return NULL;
+ break;
+ }
+ }
+
+ /* Always put our public key. This assures that we send at least
+ something valid back always. */
+ pk.type = "silc-rsa";
+ pk.data = silc_pkcs_public_key_encode(server->public_key, &pk.data_len);
+ buffer = silc_attribute_payload_encode(buffer,
+ SILC_ATTRIBUTE_SERVER_PUBLIC_KEY,
+ pk.data ? SILC_ATTRIBUTE_FLAG_VALID :
+ SILC_ATTRIBUTE_FLAG_INVALID,
+ &pk, sizeof(pk));
+ silc_free(pk.data);
+ if (!buffer)
+ return NULL;
+
+ /* Finally compute the digital signature of all the data we provided
+ as an indication that we provided rightfull information, and this
+ also authenticates our public key. */
+ if (silc_pkcs_private_key_get_len(server->private_key) / 8 <=
+ sizeof(sign) -1 &&
+ silc_pkcs_sign(server->private_key, buffer->data,
+ silc_buffer_len(buffer), sign, sizeof(sign), &sign_len,
+ TRUE, server->sha1hash)) {
+ pk.type = NULL;
+ pk.data = sign;
+ pk.data_len = sign_len;
+ buffer =
+ silc_attribute_payload_encode(buffer,
+ SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE,
+ SILC_ATTRIBUTE_FLAG_VALID,
+ &pk, sizeof(pk));
+ }
+ if (!buffer)
+ return NULL;
+
+ return buffer;
+}
+