Merged from silc_1_0_branch.
[silc.git] / apps / silcd / server_query.c
index f3696c37a47a78a54f8b3d27a4b267eaf7c0db5d..6475c57301b06b7dcde972704472061994bf0c7a 100644 (file)
 */
 /* $Id$ */
 
-/* XXX TODO Requested Attributes to WHOIS
-   (NOTE: entry that is incomplete must be resolved first before resolving
-   the attributes) */
-
 #include "serverincludes.h"
 #include "server_internal.h"
 
@@ -45,8 +41,8 @@ typedef struct {
 /* Represents one error occurred during query */
 typedef struct {
   void *id;                        /* ID */
-  SilcUInt16 index;                /* Index to IDs */
   SilcIdType id_type;              /* ID type */
+  SilcUInt16 index;                /* Index to IDs */
   unsigned int from_cmd : 1;               /* TRUE if `index' is from command args,
                                       otherwise from query->ids */
   unsigned int error : 7;          /* The actual error (SilcStatus) */
@@ -108,6 +104,9 @@ void silc_server_query_send_reply(SilcServer server,
                                  SilcUInt32 servers_count,
                                  SilcChannelEntry *channels,
                                  SilcUInt32 channels_count);
+SilcBuffer silc_server_query_reply_attrs(SilcServer server,
+                                        SilcServerQuery query,
+                                        SilcClientEntry client_entry);
 
 /* Free the query context structure and all allocated resources. */
 
@@ -372,7 +371,8 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
       }
 
       /* Get the nickname@server string and parse it */
-      if (!silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) {
+      if (tmp_len > 128 ||
+         !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) {
        silc_server_query_send_error(server, query,
                                     SILC_STATUS_ERR_BAD_NICKNAME, 0);
        silc_server_query_free(query);
@@ -388,8 +388,8 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
        if (!tmp)
          continue;
 
-       id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-       if (!id) {
+       id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
+       if (!id || id_type != SILC_ID_CLIENT) {
          silc_server_query_add_error(server, query, TRUE, i + 4,
                                      SILC_STATUS_ERR_BAD_CLIENT_ID);
          continue;
@@ -407,6 +407,9 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
              for (i = 0; i < query->ids_count; i++)
                silc_free(query->ids[i].id);
              silc_free(query->ids);
+             query->ids = NULL;
+             query->ids_count = 0;
+             silc_free(id);
              return;
            }
          }
@@ -425,8 +428,16 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
 
     /* Get requested attributes if set */
     tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
-    if (tmp)
-      query->attrs = silc_attribute_payload_parse_list(tmp, tmp_len);
+    if (tmp && tmp_len <= SILC_ATTRIBUTE_MAX_REQUEST_LEN) {
+      query->attrs = silc_attribute_payload_parse(tmp, tmp_len);
+
+      /* When Requested Attributes is present we will assure that this
+        client cannot execute the WHOIS command too fast.  This would be
+        same as having SILC_CF_LAG_STRICT. */
+      if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
+         cmd->sock->user_data)
+       ((SilcClientEntry)cmd->sock->user_data)->fast_command = 6;
+    }
     break;
 
   case SILC_COMMAND_WHOWAS:
@@ -440,7 +451,8 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
     }
 
     /* Get the nickname@server string and parse it */
-    if (!silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) {
+    if (tmp_len > 128 ||
+       !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) {
       silc_server_query_send_error(server, query,
                                   SILC_STATUS_ERR_BAD_NICKNAME, 0);
       silc_server_query_free(query);
@@ -462,7 +474,8 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
       tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
       if (tmp) {
        /* Get the nickname@server string and parse it */
-       if (!silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server))
+       if (tmp_len > 128 ||
+           !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server))
          silc_server_query_add_error(server, query, TRUE, 1,
                                      SILC_STATUS_ERR_BAD_NICKNAME);
       }
@@ -474,9 +487,16 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
 
       /* Get channel name */
       tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
-      if (tmp)
+      if (tmp && tmp_len <= 256)
        query->channel_name = silc_memdup(tmp, tmp_len);
 
+      if (!query->nickname && !query->server_name && !query->channel_name) {
+       silc_server_query_send_error(server, query,
+                                    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
+       silc_server_query_free(query);
+       return;
+      }
+
     } else {
       /* Parse the IDs included in the query */
       query->ids = silc_calloc(argc, sizeof(*query->ids));
@@ -489,24 +509,40 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
        id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
        if (!id) {
          silc_server_query_add_error(server, query, TRUE, i + 5,
-                                     SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                     SILC_STATUS_ERR_BAD_CLIENT_ID);
          continue;
        }
 
        /* Normal server must check whether this ID exist, and if not then
           send the query to router, unless done so already */
        if (server->server_type == SILC_SERVER && !query->resolved) {
-         if (!silc_idlist_find_client_by_id(server->local_list,
-                                            id, TRUE, NULL)) {
-           if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT ||
-               !silc_idlist_find_client_by_id(server->global_list,
+         if (id_type == SILC_ID_CLIENT) {
+           if (!silc_idlist_find_client_by_id(server->local_list,
                                               id, TRUE, NULL)) {
-             silc_server_query_send_router(server, query);
-             for (i = 0; i < query->ids_count; i++)
-               silc_free(query->ids[i].id);
-             silc_free(query->ids);
-             return;
+             if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT ||
+                 !silc_idlist_find_client_by_id(server->global_list,
+                                                id, TRUE, NULL)) {
+               silc_server_query_send_router(server, query);
+               for (i = 0; i < query->ids_count; i++)
+                 silc_free(query->ids[i].id);
+               silc_free(query->ids);
+               query->ids = NULL;
+               query->ids_count = 0;
+               silc_free(id);
+               return;
+             }
            }
+         } else {
+           /* For now all other ID's except Client ID's are explicitly
+              sent to router for resolving. */
+           silc_server_query_send_router(server, query);
+           for (i = 0; i < query->ids_count; i++)
+             silc_free(query->ids[i].id);
+           silc_free(query->ids);
+           query->ids = NULL;
+           query->ids_count = 0;
+           silc_free(id);
+           return;
          }
        }
 
@@ -713,10 +749,13 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query,
          !(client_entry->data.status & SILC_IDLIST_STATUS_REGISTERED))
        continue;
 
-      /* If requested attributes is set then we always resolve the client
+      /* If Requested Attributes is set then we always resolve the client
         information, if not then check whether the entry is complete or not
         and decide whether we need to resolve or not. */
       if (!query->attrs) {
+
+       /* Even if nickname and stuff are present, we may need to resolve
+          the entry */
        if (client_entry->nickname && client_entry->username &&
            client_entry->userinfo) {
          /* Check if cannot query this anyway, so take next one */
@@ -732,6 +771,11 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query,
        }
       }
 
+      /* Remove the NOATTR status periodically */
+      if (client_entry->data.status & SILC_IDLIST_STATUS_NOATTR &&
+         client_entry->updated + 600 < time(NULL))
+       client_entry->data.status &= ~SILC_IDLIST_STATUS_NOATTR;
+
       /* When requested attributes is present and local client is detached
         we cannot send the command to the client, we'll reply on behalf of
         the client instead. */
@@ -780,7 +824,9 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query,
          !(client_entry->data.status & SILC_IDLIST_STATUS_REGISTERED))
        continue;
 
+      /* Even if nickname is present, we may need to resolve the entry */
       if (client_entry->nickname) {
+
        /* If we are router, client is local to us, or client is on channel
           we do not need to resolve the client information. */
        if (server->server_type != SILC_SERVER || SILC_IS_LOCAL(client_entry)
@@ -846,6 +892,21 @@ void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
     for (i = 0; i < query->querylist_count; i++) {
       r = &query->querylist[i];
 
+      /* If Requested Attributes were present put them to this resolving */
+      if (query->attrs && query->querycmd == SILC_COMMAND_WHOIS) {
+       len = r->argc + 1;
+       r->arg = silc_realloc(r->arg, sizeof(*r->arg) * len);
+       r->arg_lens = silc_realloc(r->arg_lens, sizeof(*r->arg_lens) * len);
+       r->arg_types = silc_realloc(r->arg_types, sizeof(*r->arg_types) * len);
+
+       tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
+       if (tmp)
+         r->arg[r->argc] = silc_memdup(tmp, len);
+       r->arg_lens[r->argc] = len;
+       r->arg_types[r->argc] = 3;
+       r->argc++;
+      }
+
       /* Send WHOIS command */
       res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
                                            r->argc, r->arg, r->arg_lens,
@@ -895,7 +956,7 @@ void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
     case SILC_COMMAND_WHOIS:
     case SILC_COMMAND_IDENTIFY:
       /* Take existing query context if exist for this connection */
-      for (i = 0; i < query->queries_count; i++)
+      for (i = 0; i < query->querylist_count; i++)
        if (query->querylist[i].sock == sock) {
          r = &query->querylist[i];
          break;
@@ -915,21 +976,6 @@ void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
          r->timeout = 3;
       }
 
-      /* If attributes were present put them to this resolving */
-      if (query->attrs && query->querycmd == SILC_COMMAND_WHOIS) {
-       len = r->argc + 1;
-       r->arg = silc_realloc(r->arg, sizeof(*r->arg) * len);
-       r->arg_lens = silc_realloc(r->arg_lens, sizeof(*r->arg_lens) * len);
-       r->arg_types = silc_realloc(r->arg_types, sizeof(*r->arg_types) * len);
-
-       tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
-       if (tmp)
-         r->arg[r->argc] = silc_memdup(tmp, len);
-       r->arg_lens[r->argc] = len;
-       r->arg_types[r->argc] = 3;
-       r->argc++;
-      }
-
       len = r->argc + 1;
       r->arg = silc_realloc(r->arg, sizeof(*r->arg) * len);
       r->arg_lens = silc_realloc(r->arg_lens, sizeof(*r->arg_lens) * len);
@@ -961,6 +1007,7 @@ void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
   client_entry->data.status |= SILC_IDLIST_STATUS_RESOLVING;
   client_entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
   client_entry->resolve_cmd_ident = ident;
+  client_entry->updated = time(NULL);
 
   /* Save the queried ID, which we will reprocess after we get this and
      all other queries back. */
@@ -1011,6 +1058,7 @@ void silc_server_query_resolve_reply(void *context, void *reply)
     id = &query->queries[i];
 
     if (error == SILC_STATUS_ERR_TIMEDOUT) {
+
       /* If timeout occurred for local entry when resolving attributes
         mark that this client doesn't support attributes in WHOIS. This
         assures we won't send the request again to the client. */
@@ -1019,8 +1067,22 @@ void silc_server_query_resolve_reply(void *context, void *reply)
                                                     id->id, TRUE, NULL);
        SILC_LOG_DEBUG(("Client %s does not support Requested Attributes",
                        silc_id_render(id->id, SILC_ID_CLIENT)));
-       if (client_entry && SILC_IS_LOCAL(client_entry))
+       if (client_entry && SILC_IS_LOCAL(client_entry)) {
          client_entry->data.status |= SILC_IDLIST_STATUS_NOATTR;
+         client_entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
+          continue;
+       }
+      }
+
+      /* Remove the RESOLVING status from the client entry */
+      if (query->querycmd != SILC_COMMAND_WHOWAS) {
+       client_entry = silc_idlist_find_client_by_id(server->local_list,
+                                                    id->id, TRUE, NULL);
+       if (!client_entry)
+         client_entry = silc_idlist_find_client_by_id(server->global_list,
+                                                      id->id, TRUE, NULL);
+       if (client_entry)
+         client_entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
       }
     }
   }
@@ -1067,6 +1129,7 @@ void silc_server_query_send_reply(SilcServer server,
   SilcBuffer idp;
   int i, k, valid_count;
   char nh[256], uh[256];
+  bool sent_reply = FALSE;
 
   SILC_LOG_DEBUG(("Sending reply to query"));
 
@@ -1169,10 +1232,11 @@ void silc_server_query_send_reply(SilcServer server,
       case SILC_COMMAND_WHOIS:
        {
          unsigned char idle[4], mode[4];
-         unsigned char *fingerprint, fempty[20];
-         SilcBuffer channels, umode_list = NULL;
+         unsigned char *fingerprint, fempty[20], *attrs = NULL;
+         SilcBuffer channels, umode_list = NULL, tmpattrs = NULL;
 
          memset(fempty, 0, sizeof(fempty));
+         memset(idle, 0, sizeof(idle));
          silc_strncat(uh, sizeof(uh), entry->username,
                       strlen(entry->username));
          if (!strchr(entry->username, '@') && entry->connection) {
@@ -1200,9 +1264,23 @@ void silc_server_query_send_reply(SilcServer server,
          if (entry->connection)
            SILC_PUT32_MSB((time(NULL) - entry->data.last_receive), idle);
 
+         /* If Requested Attribute were present, and we do not have the
+            attributes we will reply to them on behalf of the client. */
+         len = 0;
+         if (query->attrs) {
+           if (!entry->attrs && SILC_IS_LOCAL(entry)) {
+             tmpattrs = silc_server_query_reply_attrs(server, query, entry);
+             entry->attrs = silc_memdup(tmpattrs->data, tmpattrs->len);
+             entry->attrs_len = tmpattrs->len;
+             silc_buffer_free(tmpattrs);
+           }
+           attrs = entry->attrs;
+           len = entry->attrs_len;
+         }
+
          /* Send command reply */
          silc_server_send_command_reply(server, cmd->sock, query->querycmd,
-                                        status, 0, ident, 9,
+                                        status, 0, ident, 10,
                                         2, idp->data, idp->len,
                                         3, nh, strlen(nh),
                                         4, uh, strlen(uh),
@@ -1216,7 +1294,18 @@ void silc_server_query_send_reply(SilcServer server,
                                         fingerprint ? 20 : 0,
                                         10, umode_list ? umode_list->data :
                                         NULL, umode_list ? umode_list->len :
-                                        0);
+                                        0, 11, attrs, len);
+
+         sent_reply = TRUE;
+
+         /* For now we always delete Requested Attributes, unless the client
+            is detached, in which case we don't want to reconstruct the
+            same data everytime */
+         if (!(entry->mode & SILC_UMODE_DETACHED) &&
+             !(entry->data.status & SILC_IDLIST_STATUS_NOATTR)) {
+           silc_free(entry->attrs);
+           entry->attrs = NULL;
+         }
 
          if (channels)
            silc_buffer_free(channels);
@@ -1233,6 +1322,7 @@ void silc_server_query_send_reply(SilcServer server,
                                         status, 0, ident, 2,
                                         2, idp->data, idp->len,
                                         3, nh, strlen(nh));
+         sent_reply = TRUE;
        } else {
          silc_strncat(uh, sizeof(uh), entry->username,
                       strlen(entry->username));
@@ -1248,6 +1338,7 @@ void silc_server_query_send_reply(SilcServer server,
                                         2, idp->data, idp->len,
                                         3, nh, strlen(nh),
                                         4, uh, strlen(uh));
+         sent_reply = TRUE;
        }
        break;
 
@@ -1265,6 +1356,7 @@ void silc_server_query_send_reply(SilcServer server,
                                       5, entry->userinfo, 
                                       entry->userinfo ? 
                                       strlen(entry->userinfo) : 0);
+       sent_reply = TRUE;
        break;
       }
 
@@ -1321,6 +1413,7 @@ void silc_server_query_send_reply(SilcServer server,
                                     entry->server_name ? 
                                     strlen(entry->server_name) : 0);
       silc_buffer_free(idp);
+      sent_reply = TRUE;
       
       if (status == SILC_STATUS_LIST_END)
        break;
@@ -1364,6 +1457,7 @@ void silc_server_query_send_reply(SilcServer server,
                                     entry->channel_name ? 
                                     strlen(entry->channel_name) : 0);
       silc_buffer_free(idp);
+      sent_reply = TRUE;
       
       if (status == SILC_STATUS_LIST_END)
        break;
@@ -1384,6 +1478,7 @@ void silc_server_query_send_reply(SilcServer server,
 
       /* Take error argument */
       if (query->errors[i].from_cmd) {
+       len = 0;
        tmp = silc_argument_get_arg_type(cmd->args,
                                         query->errors[i].index, &len);
        if (query->errors[i].index == 1)
@@ -1429,6 +1524,7 @@ void silc_server_query_send_reply(SilcServer server,
                                      0 : query->errors[i].error), ident, 1,
                                     type, tmp, len);
       silc_buffer_free(idp);
+      sent_reply = TRUE;
 
       if (status == SILC_STATUS_LIST_END)
        break;
@@ -1436,10 +1532,165 @@ void silc_server_query_send_reply(SilcServer server,
     }
   }
 
+  if (!sent_reply)
+    SILC_LOG_ERROR(("BUG: Query did not send anything"));
+
   /* Cleanup */
   silc_server_query_free(query);
 }
 
+/* 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_get_key_len(server->pkcs) / 8 <= sizeof(sign) -1  &&
+      silc_pkcs_sign_with_hash(server->pkcs, server->sha1hash,
+                              buffer->data, buffer->len,
+                              sign, &sign_len)) {
+    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;
+}
+
 /* Find client by the Client ID indicated by the `client_id', and if not
    found then query it by using WHOIS command.  The client information
    is also resolved if the cached information is incomplete or if the