updates.
[silc.git] / apps / silcd / server.c
index 3ef616134ee65a1d55860cbad94b33b55c2691e0..7ba210e77dd9d8085d16c1518ede3d66c3e49a1a 100644 (file)
@@ -39,6 +39,7 @@ SILC_TASK_CALLBACK(silc_server_packet_process);
 SILC_TASK_CALLBACK(silc_server_packet_parse_real);
 SILC_TASK_CALLBACK(silc_server_timeout_remote);
 SILC_TASK_CALLBACK(silc_server_failure_callback);
+SILC_TASK_CALLBACK(silc_server_rekey_callback);
 
 /* Allocates a new SILC server object. This has to be done before the server
    can be used. After allocation one must call silc_server_init to initialize
@@ -119,6 +120,16 @@ int silc_server_init(SilcServer server)
   assert(server);
   assert(server->config);
 
+  /* Set public and private keys */
+  if (!server->config->server_keys ||
+      !server->config->server_keys->public_key || 
+      !server->config->server_keys->private_key) {
+    SILC_LOG_ERROR(("Server public key and/or private key does not exist"));
+    return FALSE;
+  }
+  server->public_key = server->config->server_keys->public_key;
+  server->private_key = server->config->server_keys->private_key;
+
   /* XXX After server is made as Silc Server Library this can be given
      as argument, for now this is hard coded */
   server->params = silc_calloc(1, sizeof(*server->params));
@@ -151,58 +162,6 @@ int silc_server_init(SilcServer server)
   /* Initialize none cipher */
   silc_cipher_alloc("none", &server->none_cipher);
 
-  /* XXXXX Generate RSA key pair */
-  {
-    unsigned char *public_key;
-    unsigned char *private_key;
-    uint32 pk_len, prv_len;
-    struct stat st;
-
-    if (stat("pubkey.pub", &st) < 0 && stat("privkey.prv", &st) < 0) {
-
-      if (silc_pkcs_alloc("rsa", &server->pkcs) == FALSE) {
-       SILC_LOG_ERROR(("Could not create RSA key pair"));
-       goto err0;
-      }
-      
-      if (server->pkcs->pkcs->init(server->pkcs->context, 
-                                  1024, server->rng) == FALSE) {
-       SILC_LOG_ERROR(("Could not generate RSA key pair"));
-       goto err0;
-      }
-      
-      public_key = server->pkcs->pkcs->get_public_key(server->pkcs->context,
-                                                     &pk_len);
-      private_key = server->pkcs->pkcs->get_private_key(server->pkcs->context,
-                                                       &prv_len);
-      
-      SILC_LOG_HEXDUMP(("public key"), public_key, pk_len);
-      SILC_LOG_HEXDUMP(("private key"), private_key, prv_len);
-      
-      server->public_key = 
-       silc_pkcs_public_key_alloc("rsa", "UN=root, HN=dummy",
-                                  public_key, pk_len);
-      server->private_key = 
-       silc_pkcs_private_key_alloc("rsa", private_key, prv_len);
-      
-      /* XXX Save keys */
-      silc_pkcs_save_public_key("pubkey.pub", server->public_key,
-                               SILC_PKCS_FILE_PEM);
-      silc_pkcs_save_private_key("privkey.prv", server->private_key, NULL,
-                                SILC_PKCS_FILE_BIN);
-
-      memset(public_key, 0, pk_len);
-      memset(private_key, 0, prv_len);
-      silc_free(public_key);
-      silc_free(private_key);
-    } else {
-      silc_pkcs_load_public_key("pubkey.pub", &server->public_key,
-                               SILC_PKCS_FILE_PEM);
-      silc_pkcs_load_private_key("privkey.prv", &server->private_key,
-                                SILC_PKCS_FILE_BIN);
-    }
-  }
-
   /* Create a listening server. Note that our server can listen on
      multiple ports. All listeners are created here and now. */
   /* XXX Still check this whether to use server_info or listen_port. */
@@ -557,6 +516,9 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
   SilcServerKEInternalContext *proto_ctx;
   int sock;
 
+  SILC_LOG_INFO(("Connecting to the router %s on port %d", 
+                sconn->remote_host, sconn->remote_port));
+
   /* Connect to remote host */
   sock = silc_net_create_connection(sconn->remote_port, 
                                    sconn->remote_host);
@@ -709,7 +671,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
     (SilcServerKEInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
   SilcServerConnection sconn = (SilcServerConnection)ctx->context;
-  SilcSocketConnection sock = NULL;
+  SilcSocketConnection sock = server->sockets[fd];
   SilcServerConnAuthInternalContext *proto_ctx;
   SilcServerConfigSectionServerConnection *conn = NULL;
 
@@ -719,6 +681,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
       protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
     silc_protocol_free(protocol);
+    sock->protocol = NULL;
     silc_ske_free_key_material(ctx->keymat);
     if (ctx->packet)
       silc_packet_context_free(ctx->packet);
@@ -742,8 +705,10 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
                                        ctx->ske->prop->pkcs,
                                        ctx->ske->prop->hash,
                                        ctx->ske->prop->hmac,
+                                       ctx->ske->prop->group,
                                        ctx->responder)) {
     silc_protocol_free(protocol);
+    sock->protocol = NULL;
     silc_ske_free_key_material(ctx->keymat);
     if (ctx->packet)
       silc_packet_context_free(ctx->packet);
@@ -765,7 +730,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
   proto_ctx->server = (void *)server;
   proto_ctx->context = (void *)sconn;
-  proto_ctx->sock = sock = server->sockets[fd];
+  proto_ctx->sock = sock;
   proto_ctx->ske = ctx->ske;      /* Save SKE object from previous protocol */
   proto_ctx->dest_id_type = ctx->dest_id_type;
   proto_ctx->dest_id = ctx->dest_id;
@@ -786,6 +751,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
     SILC_LOG_ERROR(("Could not find connection data for %s (%s) on port",
                    sock->hostname, sock->ip, sock->port));
     silc_protocol_free(protocol);
+    sock->protocol = NULL;
     if (ctx->packet)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
@@ -845,6 +811,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   SilcBuffer packet;
   SilcServerHBContext hb_context;
   unsigned char *id_string;
+  SilcIDListData idata;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -893,7 +860,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
 
   /* Add the connected router to local server list */
   server->standalone = FALSE;
-  id_entry = silc_idlist_add_server(server->local_list, sock->hostname,
+  id_entry = silc_idlist_add_server(server->local_list, strdup(sock->hostname),
                                    SILC_ROUTER, ctx->dest_id, NULL, sock);
   if (!id_entry) {
     if (ctx->dest_id)
@@ -909,7 +876,8 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   sock->type = SILC_SOCKET_TYPE_ROUTER;
   server->id_entry->router = id_entry;
   server->router = id_entry;
-  server->router->data.registered = TRUE;
+  idata = (SilcIDListData)sock->user_data;
+  idata->registered = TRUE;
 
   /* Perform keepalive. The `hb_context' will be freed automatically
      when finally calling the silc_socket_free function. XXX hardcoded 
@@ -920,6 +888,14 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
                            silc_server_perform_heartbeat,
                            server->timeout_queue);
 
+  /* Register re-key timeout */
+  idata->rekey->timeout = 3600; /* XXX hardcoded */
+  idata->rekey->context = (void *)server;
+  silc_task_register(server->timeout_queue, sock->sock, 
+                    silc_server_rekey_callback,
+                    (void *)sock, idata->rekey->timeout, 0,
+                    SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+
   /* If we are router then announce our possible servers. */
   if (server->server_type == SILC_ROUTER)
     silc_server_announce_servers(server);
@@ -986,15 +962,19 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
      process. Either we have to do our own resolver stuff or in the future
      we can use threads. */
   /* Perform name and address lookups for the remote host. */
-  silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
-  if ((server->params->require_reverse_mapping && !newsocket->hostname) ||
-      !newsocket->ip) {
-    SILC_LOG_ERROR(("IP/DNS lookup failed"));
-    server->stat.conn_failures++;
-    return;
+  if (!silc_net_check_host_by_sock(sock, &newsocket->hostname, 
+                                  &newsocket->ip)) {
+    if ((server->params->require_reverse_mapping && !newsocket->hostname) ||
+       !newsocket->ip) {
+      SILC_LOG_ERROR(("IP/DNS lookup failed %s",
+                     newsocket->hostname ? newsocket->hostname :
+                     newsocket->ip ? newsocket->ip : ""));
+      server->stat.conn_failures++;
+      return;
+    }
+    if (!newsocket->hostname)
+      newsocket->hostname = strdup(newsocket->ip);
   }
-  if (!newsocket->hostname)
-    newsocket->hostname = strdup(newsocket->ip);
   newsocket->port = silc_net_get_remote_port(sock);
 
   SILC_LOG_INFO(("Incoming connection from %s (%s)", newsocket->hostname,
@@ -1049,7 +1029,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
   SilcServerKEInternalContext *ctx = 
     (SilcServerKEInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
-  SilcSocketConnection sock = NULL;
+  SilcSocketConnection sock = server->sockets[fd];
   SilcServerConnAuthInternalContext *proto_ctx;
 
   SILC_LOG_DEBUG(("Start"));
@@ -1058,6 +1038,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
       protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
     silc_protocol_free(protocol);
+    sock->protocol = NULL;
     silc_ske_free_key_material(ctx->keymat);
     if (ctx->packet)
       silc_packet_context_free(ctx->packet);
@@ -1082,8 +1063,10 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
                                        ctx->ske->prop->pkcs,
                                        ctx->ske->prop->hash,
                                        ctx->ske->prop->hmac,
+                                       ctx->ske->prop->group,
                                        ctx->responder)) {
     silc_protocol_free(protocol);
+    sock->protocol = NULL;
     silc_ske_free_key_material(ctx->keymat);
     if (ctx->packet)
       silc_packet_context_free(ctx->packet);
@@ -1105,7 +1088,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
      is sent as context for the protocol. */
   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
   proto_ctx->server = (void *)server;
-  proto_ctx->sock = sock = server->sockets[fd];
+  proto_ctx->sock = sock;
   proto_ctx->ske = ctx->ske;   /* Save SKE object from previous protocol */
   proto_ctx->responder = TRUE;
   proto_ctx->dest_id_type = ctx->dest_id_type;
@@ -1157,6 +1140,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
       protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
     silc_protocol_free(protocol);
+    sock->protocol = NULL;
     if (ctx->packet)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
@@ -1321,7 +1305,8 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
     if (sock->outbuf->data - sock->outbuf->head)
      silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
 
-    ret = silc_server_packet_send_real(server, sock, TRUE);
+    /* Send the packet */
+    ret = silc_packet_send(sock, TRUE);
 
     /* If returned -2 could not write to connection now, will do
        it later. */
@@ -1529,7 +1514,6 @@ void silc_server_packet_parse(SilcPacketParserContext *parser_context)
   SilcSocketConnection sock = parser_context->sock;
 
   switch (sock->type) {
-  case SILC_SOCKET_TYPE_CLIENT:
   case SILC_SOCKET_TYPE_UNKNOWN:
     /* Parse the packet with timeout */
     silc_task_register(server->timeout_queue, sock->sock,
@@ -1538,6 +1522,15 @@ void silc_server_packet_parse(SilcPacketParserContext *parser_context)
                       SILC_TASK_TIMEOUT,
                       SILC_TASK_PRI_NORMAL);
     break;
+  case SILC_SOCKET_TYPE_CLIENT:
+    /* Parse the packet with timeout (unless protocol is active) */
+    silc_task_register(server->timeout_queue, sock->sock,
+                      silc_server_packet_parse_real,
+                      (void *)parser_context, 0, 
+                      (sock->protocol ? 1 : 100000),
+                      SILC_TASK_TIMEOUT,
+                      SILC_TASK_PRI_NORMAL);
+    break;
   case SILC_SOCKET_TYPE_SERVER:
   case SILC_SOCKET_TYPE_ROUTER:
     /* Packets from servers are parsed as soon as possible */
@@ -1737,25 +1730,40 @@ void silc_server_packet_parse_type(SilcServer server,
       break;
 
     if (sock->protocol && sock->protocol->protocol &&
-       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
+       (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE ||
+        sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY)) {
 
-      SilcServerKEInternalContext *proto_ctx = 
-       (SilcServerKEInternalContext *)sock->protocol->context;
-
-      if (proto_ctx->packet)
-       silc_packet_context_free(proto_ctx->packet);
+      if (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY) {
+       SilcServerRekeyInternalContext *proto_ctx = 
+         (SilcServerRekeyInternalContext *)sock->protocol->context;
+       
+       if (proto_ctx->packet)
+         silc_packet_context_free(proto_ctx->packet);
+       
+       proto_ctx->packet = silc_packet_context_dup(packet);
 
-      proto_ctx->packet = silc_packet_context_dup(packet);
-      proto_ctx->dest_id_type = packet->src_id_type;
-      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
-                                         packet->src_id_type);
-      if (!proto_ctx->dest_id)
-       break;
+       /* Let the protocol handle the packet */
+       sock->protocol->execute(server->timeout_queue, 0, 
+                               sock->protocol, sock->sock, 0, 0);
+      } else {
+       SilcServerKEInternalContext *proto_ctx = 
+         (SilcServerKEInternalContext *)sock->protocol->context;
+       
+       if (proto_ctx->packet)
+         silc_packet_context_free(proto_ctx->packet);
+       
+       proto_ctx->packet = silc_packet_context_dup(packet);
+       proto_ctx->dest_id_type = packet->src_id_type;
+       proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+                                           packet->src_id_type);
+       if (!proto_ctx->dest_id)
+         break;
 
-      /* Let the protocol handle the packet */
-      sock->protocol->execute(server->timeout_queue, 0, 
-                             sock->protocol, sock->sock,
-                             0, 100000);
+       /* Let the protocol handle the packet */
+       sock->protocol->execute(server->timeout_queue, 0, 
+                               sock->protocol, sock->sock,
+                               0, 100000);
+      }
     } else {
       SILC_LOG_ERROR(("Received Key Exchange 1 packet but no key exchange "
                      "protocol active, packet dropped."));
@@ -1768,25 +1776,40 @@ void silc_server_packet_parse_type(SilcServer server,
       break;
 
     if (sock->protocol && sock->protocol->protocol &&
-       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
+       (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE ||
+        sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY)) {
 
-      SilcServerKEInternalContext *proto_ctx = 
-       (SilcServerKEInternalContext *)sock->protocol->context;
-
-      if (proto_ctx->packet)
-       silc_packet_context_free(proto_ctx->packet);
+      if (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY) {
+       SilcServerRekeyInternalContext *proto_ctx = 
+         (SilcServerRekeyInternalContext *)sock->protocol->context;
+       
+       if (proto_ctx->packet)
+         silc_packet_context_free(proto_ctx->packet);
+       
+       proto_ctx->packet = silc_packet_context_dup(packet);
 
-      proto_ctx->packet = silc_packet_context_dup(packet);
-      proto_ctx->dest_id_type = packet->src_id_type;
-      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
-                                         packet->src_id_type);
-      if (!proto_ctx->dest_id)
-       break;
+       /* Let the protocol handle the packet */
+       sock->protocol->execute(server->timeout_queue, 0, 
+                               sock->protocol, sock->sock, 0, 0);
+      } else {
+       SilcServerKEInternalContext *proto_ctx = 
+         (SilcServerKEInternalContext *)sock->protocol->context;
+       
+       if (proto_ctx->packet)
+         silc_packet_context_free(proto_ctx->packet);
+       
+       proto_ctx->packet = silc_packet_context_dup(packet);
+       proto_ctx->dest_id_type = packet->src_id_type;
+       proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+                                           packet->src_id_type);
+       if (!proto_ctx->dest_id)
+         break;
 
-      /* Let the protocol handle the packet */
-      sock->protocol->execute(server->timeout_queue, 0, 
-                             sock->protocol, sock->sock,
-                             0, 100000);
+       /* Let the protocol handle the packet */
+       sock->protocol->execute(server->timeout_queue, 0, 
+                               sock->protocol, sock->sock,
+                               0, 100000);
+      }
     } else {
       SILC_LOG_ERROR(("Received Key Exchange 2 packet but no key exchange "
                      "protocol active, packet dropped."));
@@ -1902,6 +1925,46 @@ void silc_server_packet_parse_type(SilcServer server,
     silc_server_key_agreement(server, sock, packet);
     break;
 
+  case SILC_PACKET_REKEY:
+    /*
+     * Received re-key packet. The sender wants to regenerate the session
+     * keys.
+     */
+    SILC_LOG_DEBUG(("Re-key packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_rekey(server, sock, packet);
+    break;
+
+  case SILC_PACKET_REKEY_DONE:
+    /*
+     * The re-key is done.
+     */
+    SILC_LOG_DEBUG(("Re-key done packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+
+    if (sock->protocol && sock->protocol->protocol &&
+       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY) {
+
+      SilcServerRekeyInternalContext *proto_ctx = 
+       (SilcServerRekeyInternalContext *)sock->protocol->context;
+
+      if (proto_ctx->packet)
+       silc_packet_context_free(proto_ctx->packet);
+
+      proto_ctx->packet = silc_packet_context_dup(packet);
+
+      /* Let the protocol handle the packet */
+      sock->protocol->execute(server->timeout_queue, 0, 
+                             sock->protocol, sock->sock,
+                             0, 100000);
+    } else {
+      SILC_LOG_ERROR(("Received Re-key done packet but no re-key "
+                     "protocol active, packet dropped."));
+    }
+    break;
+
   default:
     SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
     break;
@@ -1938,7 +2001,12 @@ SILC_TASK_CALLBACK(silc_server_close_connection_final)
 void silc_server_close_connection(SilcServer server,
                                  SilcSocketConnection sock)
 {
-  SILC_LOG_DEBUG(("Closing connection %d", sock->sock));
+  SILC_LOG_INFO(("Closing connection %s:%d [%s] (%d)", sock->hostname,
+                sock->port,  
+                (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                 sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                 sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                 "Router"), sock->sock));
 
   /* We won't listen for this connection anymore */
   silc_schedule_unset_listen_fd(sock->sock);
@@ -3381,7 +3449,7 @@ void silc_server_save_users_on_channel(SilcServer server,
   }
 }
 
-/* Lookups route to the client indicated by `id' client ID. The connection
+/* Lookups route to the client indicated by the `id_data'. The connection
    object and internal data object is returned. Returns NULL if route
    could not be found to the client. If the `client_id' is specified then
    it is used and the `id_data' is ignored. */
@@ -3423,12 +3491,14 @@ SilcSocketConnection silc_server_get_client_route(SilcServer server,
       /* We are of course in this case the client's router thus the real
         "router" of the client is the server who owns the client. Thus
         we will send the packet to that server. */
-      *idata = (SilcIDListData)client->router;
+      if (idata)
+       *idata = (SilcIDListData)client->router;
       return client->router->connection;
     }
 
     /* Seems that client really is directly connected to us */
-    *idata = (SilcIDListData)client;
+    if (idata)
+      *idata = (SilcIDListData)client;
     return client->connection;
   }
 
@@ -3436,7 +3506,8 @@ SilcSocketConnection silc_server_get_client_route(SilcServer server,
      server our action is to send the packet to our router. */
   if (server->server_type == SILC_SERVER && !server->standalone) {
     silc_free(id);
-    *idata = (SilcIDListData)server->router;
+    if (idata)
+      *idata = (SilcIDListData)server->router;
     return server->router->connection;
   }
 
@@ -3451,7 +3522,8 @@ SilcSocketConnection silc_server_get_client_route(SilcServer server,
       dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
 
       silc_free(id);
-      *idata = (SilcIDListData)dst_sock->user_data;
+      if (idata)
+       *idata = (SilcIDListData)dst_sock->user_data;
       return dst_sock;
     }
   }
@@ -3541,3 +3613,84 @@ SilcClientEntry silc_server_get_client_resolve(SilcServer server,
 
   return client;
 }
+
+/* A timeout callback for the re-key. We will be the initiator of the
+   re-key protocol. */
+
+SILC_TASK_CALLBACK(silc_server_rekey_callback)
+{
+  SilcSocketConnection sock = (SilcSocketConnection)context;
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
+  SilcServer server = (SilcServer)idata->rekey->context;
+  SilcProtocol protocol;
+  SilcServerRekeyInternalContext *proto_ctx;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Allocate internal protocol context. This is sent as context
+     to the protocol. */
+  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+  proto_ctx->server = (void *)server;
+  proto_ctx->sock = sock;
+  proto_ctx->responder = FALSE;
+  proto_ctx->pfs = idata->rekey->pfs;
+      
+  /* Perform rekey protocol. Will call the final callback after the
+     protocol is over. */
+  silc_protocol_alloc(SILC_PROTOCOL_SERVER_REKEY, 
+                     &protocol, proto_ctx, silc_server_rekey_final);
+  sock->protocol = protocol;
+      
+  /* Run the protocol */
+  protocol->execute(server->timeout_queue, 0, protocol, 
+                   sock->sock, 0, 0);
+
+  /* Re-register re-key timeout */
+  silc_task_register(server->timeout_queue, sock->sock, 
+                    silc_server_rekey_callback,
+                    context, idata->rekey->timeout, 0,
+                    SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+}
+
+/* The final callback for the REKEY protocol. This will actually take the
+   new key material into use. */
+
+SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcServerRekeyInternalContext *ctx =
+    (SilcServerRekeyInternalContext *)protocol->context;
+  SilcServer server = (SilcServer)ctx->server;
+  SilcSocketConnection sock = ctx->sock;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
+    /* Error occured during protocol */
+    silc_protocol_cancel(server->timeout_queue, protocol);
+    silc_protocol_free(protocol);
+    sock->protocol = NULL;
+    if (ctx->packet)
+      silc_packet_context_free(ctx->packet);
+    if (ctx->ske)
+      silc_ske_free(ctx->ske);
+    silc_free(ctx);
+    return;
+  }
+
+  /* Take the keys into use */
+  if (ctx->pfs == TRUE)
+    silc_server_protocol_rekey_generate_pfs(server, ctx);
+  else
+    silc_server_protocol_rekey_generate(server, ctx);
+
+  /* Cleanup */
+  silc_protocol_free(protocol);
+  sock->protocol = NULL;
+  if (ctx->packet)
+    silc_packet_context_free(ctx->packet);
+  if (ctx->ske)
+    silc_ske_free(ctx->ske);
+  silc_free(ctx);
+}