updates.
[silc.git] / apps / silcd / server.c
index 5192004d89ed1f3c8d6a08d5e56b4a08dc0c13e4..22af66f580e2b078d4e77b6723dfbb1293cceaa5 100644 (file)
@@ -111,7 +111,7 @@ void silc_server_free(SilcServer server)
 
 int silc_server_init(SilcServer server)
 {
-  int *sock = NULL, sock_count = 0, i;
+  int *sock = NULL, sock_count, i;
   SilcServerID *id;
   SilcServerEntry id_entry;
   SilcIDListPurge purge;
@@ -181,10 +181,15 @@ int silc_server_init(SilcServer server)
 
     tmp = silc_net_create_server(server->config->listen_port->port,
                                 server->config->listen_port->listener_ip);
-    if (tmp < 0)
+
+    if (tmp < 0) {
+      SILC_LOG_ERROR(("Could not create server listener: %s on %d",
+                     server->config->listen_port->listener_ip,
+                     server->config->listen_port->port));
       goto err0;
+    }
 
-    sock = silc_realloc(sock, (sizeof(int *) * (sock_count + 1)));
+    sock = silc_realloc(sock, sizeof(*sock) * (sock_count + 1));
     sock[sock_count] = tmp;
     sock_count++;
     listen = listen->next;
@@ -218,11 +223,32 @@ int silc_server_init(SilcServer server)
     silc_net_set_socket_nonblock(sock[i]);
     server->sock = sock[i];
     
+    /* Add ourselves also to the socket table. The entry allocated above
+       is sent as argument for fast referencing in the future. */
+    silc_socket_alloc(sock[i], SILC_SOCKET_TYPE_SERVER, NULL, &newsocket);
+    server->sockets[sock[i]] = newsocket;
+    
+    /* Perform name and address lookups to resolve the listenning address
+       and port. */
+    if (!silc_net_check_local_by_sock(sock[i], &newsocket->hostname, 
+                                     &newsocket->ip)) {
+      if ((server->params->require_reverse_mapping && !newsocket->hostname) ||
+         !newsocket->ip) {
+       SILC_LOG_ERROR(("IP/DNS lookup failed for local host %s",
+                       newsocket->hostname ? newsocket->hostname :
+                       newsocket->ip ? newsocket->ip : ""));
+       server->stat.conn_failures++;
+       goto err0;
+      }
+      if (!newsocket->hostname)
+       newsocket->hostname = strdup(newsocket->ip);
+    }
+    newsocket->port = silc_net_get_local_port(sock[i]);
+
     /* Create a Server ID for the server. */
-    silc_id_create_server_id(sock[i], server->rng, &id);
-    if (!id) {
+    silc_id_create_server_id(newsocket->ip, newsocket->port, server->rng, &id);
+    if (!id)
       goto err0;
-    }
     
     server->id = id;
     server->id_string = silc_id_id2str(id, SILC_ID_SERVER);
@@ -244,33 +270,10 @@ int silc_server_init(SilcServer server)
     }
     id_entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
     
-    /* Add ourselves also to the socket table. The entry allocated above
-       is sent as argument for fast referencing in the future. */
-    silc_socket_alloc(sock[i], SILC_SOCKET_TYPE_SERVER, id_entry, 
-                     &newsocket);
-
-    server->sockets[sock[i]] = newsocket;
-    
-    /* Perform name and address lookups to resolve the listenning address
-       and port. */
-    if (!silc_net_check_local_by_sock(sock[i], &newsocket->hostname, 
-                                    &newsocket->ip)) {
-      if ((server->params->require_reverse_mapping && !newsocket->hostname) ||
-         !newsocket->ip) {
-       SILC_LOG_ERROR(("IP/DNS lookup failed for local host %s",
-                       newsocket->hostname ? newsocket->hostname :
-                       newsocket->ip ? newsocket->ip : ""));
-       server->stat.conn_failures++;
-       goto err0;
-      }
-      if (!newsocket->hostname)
-       newsocket->hostname = strdup(newsocket->ip);
-    }
-    newsocket->port = silc_net_get_local_port(sock[i]);
-
     /* Put the allocated socket pointer also to the entry allocated above 
        for fast back-referencing to the socket list. */
-    id_entry->connection = (void *)server->sockets[sock[i]];
+    newsocket->user_data = (void *)id_entry;
+    id_entry->connection = (void *)newsocket;
     server->id_entry = id_entry;
   }
 
@@ -352,19 +355,42 @@ int silc_server_init(SilcServer server)
   return FALSE;
 }
 
-/* Fork server to background and set gid+uid to non-root.
-   Silcd will not run as root, so trying to set either user or group to
-   root will cause silcd to exit. */
+/* Fork server to background and set gid+uid to non-root */
 
 void silc_server_daemonise(SilcServer server)
+{
+  int i;
+
+  i = fork ();
+
+  if (i) {
+    if (i > 0) {
+      if (geteuid())
+        SILC_LOG_DEBUG(("Server started as user"));
+      else
+        SILC_LOG_DEBUG(("Server started as root. Dropping privileges."));
+
+      SILC_LOG_DEBUG(("Forking SILC server to background"));
+      exit(0);
+    } else {
+      SILC_LOG_DEBUG(("fork() failed, cannot proceed"));
+      exit(1);
+    }
+  }
+  setsid();
+}
+
+/* Drop root privligies. If this cannot be done, die. */
+
+void silc_server_drop(SilcServer server)
 {
   /* Are we executing silcd as root or a regular user? */
-  if (geteuid()==0) {
-    
+  if (!geteuid()) {
+
     struct passwd *pw;
     struct group *gr;
     char *user, *group;
-    
+
     if (!server->config->identity || !server->config->identity->user || 
        !server->config->identity->group) {
       fprintf(stderr, "Error:"
@@ -374,11 +400,11 @@ void silc_server_daemonise(SilcServer server)
        "\tthe server as non-root user.\n");
       exit(1);
     }
-    
+
     /* Get the values given for user and group in configuration file */
     user=server->config->identity->user;
     group=server->config->identity->group;
-    
+
     /* Check whether the user/group information is text */ 
     if (atoi(user)!=0 || atoi(group)!=0) {
       SILC_LOG_DEBUG(("Invalid user and/or group information"));
@@ -387,27 +413,24 @@ void silc_server_daemonise(SilcServer server)
       fprintf(stderr, "Please assign them as names, not numbers\n");
       exit(1);
     }
-    
+
     /* Catch the nasty incident of string "0" returning 0 from atoi */
     if (strcmp("0", user)==0 || strcmp("0", group)==0) {
       SILC_LOG_DEBUG(("User and/or group configured to 0. Unacceptable"));
       fprintf(stderr, "User and/or group configured to 0. Exiting\n");
       exit(1);
     }
-    
-    pw=getpwnam(user);
-    gr=getgrnam(group);
 
-    if (!pw) {
+    if (!(pw=getpwnam(user))) {
       fprintf(stderr, "No such user %s found\n", user);
       exit(1);
     }
 
-    if (!gr) {
+    if (!(gr=getgrnam(group))) {
       fprintf(stderr, "No such group %s found\n", group);
       exit(1);
     }
-    
+
     /* Check whether user and/or group is set to root. If yes, exit
        immediately. Otherwise, setgid and setuid server to user.group */
     if (gr->gr_gid==0 || pw->pw_uid==0) {
@@ -418,16 +441,8 @@ void silc_server_daemonise(SilcServer server)
        "\tthe server as non-root user.\n");
       exit(1);
     } else {
-      /* Fork server to background, making it a daemon */
-      if (fork()) {
-        SILC_LOG_DEBUG(("Server started as root. Dropping privileges."));
-        SILC_LOG_DEBUG(("Forking SILC server to background"));
-        exit(0);
-      } 
-      setsid();
-      
       SILC_LOG_DEBUG(("Changing to group %s", group));
-      if(setgid(gr->gr_gid)==0) {
+      if (setgid(gr->gr_gid)==0) {
         SILC_LOG_DEBUG(("Setgid to %s", group));
       } else {
         SILC_LOG_DEBUG(("Setgid to %s failed", group));
@@ -435,8 +450,21 @@ void silc_server_daemonise(SilcServer server)
                 group);
         exit(1);
       }
-      SILC_LOG_DEBUG(("Changing to user nobody"));
-      if(setuid(pw->pw_uid)==0) {
+#if defined HAVE_SETGROUPS && defined HAVE_INITGROUPS
+      if (setgroups(0, NULL)!=0) {
+        SILC_LOG_DEBUG(("Setgroups to NULL failed"));
+        fprintf(stderr, "Tried to setgroups NULL but failed. Exiting\n");
+        exit(1);
+      }
+      if (initgroups(user, gr->gr_gid)!=0) {
+        SILC_LOG_DEBUG(("Initgroups to user %s (gid=%d) failed", user, gr->gr_gid));
+        fprintf(stderr, "Tried to initgroups %s (gid=%d) but no such user. Exiting\n",
+                user, gr->gr_gid);
+        exit(1);
+      }
+#endif
+      SILC_LOG_DEBUG(("Changing to user %s", user));
+      if (setuid(pw->pw_uid)==0) {
         SILC_LOG_DEBUG(("Setuid to %s", user));
       } else {
         SILC_LOG_DEBUG(("Setuid to %s failed", user));
@@ -445,14 +473,6 @@ void silc_server_daemonise(SilcServer server)
         exit(1);
       }
     }
-  } else {
-    /* Fork server to background, making it a daemon */
-    if (fork()) {
-      SILC_LOG_DEBUG(("Server started as user")); 
-      SILC_LOG_DEBUG(("Forking SILC server to background"));
-      exit(0);
-    }
-    setsid();
   }
 }
 
@@ -582,6 +602,8 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
   if (sconn->retry_count > server->params->retry_count && 
       server->params->retry_keep_trying == FALSE) {
     SILC_LOG_ERROR(("Could not connect to router, giving up"));
+    silc_free(sconn->remote_host);
+    silc_free(sconn);
     return;
   }
 
@@ -722,7 +744,8 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
   /* We now have the key material as the result of the key exchange
      protocol. Take the key material into use. Free the raw key material
      as soon as we've set them into use. */
-  if (!silc_server_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
+  if (!silc_server_protocol_ke_set_keys(server, ctx->ske, 
+                                       ctx->sock, ctx->keymat,
                                        ctx->ske->prop->cipher,
                                        ctx->ske->prop->pkcs,
                                        ctx->ske->prop->hash,
@@ -972,6 +995,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
     silc_packet_context_free(ctx->packet);
   if (ctx->ske)
     silc_ske_free(ctx->ske);
+  silc_free(ctx->auth_data);
   silc_free(ctx);
 }
 
@@ -1179,7 +1203,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
   /* We now have the key material as the result of the key exchange
      protocol. Take the key material into use. Free the raw key material
      as soon as we've set them into use. */
-  if (!silc_server_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
+  if (!silc_server_protocol_ke_set_keys(server, ctx->ske, 
+                                       ctx->sock, ctx->keymat,
                                        ctx->ske->prop->cipher,
                                        ctx->ske->prop->pkcs,
                                        ctx->ske->prop->hash,
@@ -1271,8 +1296,6 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
       silc_ske_free(ctx->ske);
     silc_free(ctx->dest_id);
     silc_free(ctx);
-    if (sock)
-      sock->protocol = NULL;
     silc_schedule_task_del_by_callback(server->schedule,
                                       silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
@@ -1505,11 +1528,11 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
 
     if (ret == -1)
       SILC_LOG_ERROR(("Error receiving packet from connection "
-                     "%s:%d [%s]", sock->hostname, sock->port,  
+                     "%s:%d [%s] %s", 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")));
+                      "Router"), strerror(errno)));
     return;
   }    
 
@@ -1650,7 +1673,7 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
 /* Parser callback called by silc_packet_receive_process. This merely
    registers timeout that will handle the actual parsing when appropriate. */
 
-void silc_server_packet_parse(SilcPacketParserContext *parser_context,
+bool silc_server_packet_parse(SilcPacketParserContext *parser_context,
                              void *context)
 {
   SilcServer server = (SilcServer)context;
@@ -1660,6 +1683,33 @@ void silc_server_packet_parse(SilcPacketParserContext *parser_context,
   if (idata)
     idata->psn_receive = parser_context->packet->sequence + 1;
 
+  /* If protocol for this connection is key exchange or rekey then we'll
+     process all packets synchronously, since there might be packets in
+     queue that we are not able to decrypt without first processing the
+     packets before them. */
+  if ((parser_context->packet->type == SILC_PACKET_REKEY ||
+       parser_context->packet->type == SILC_PACKET_REKEY_DONE) ||
+      (sock->protocol && sock->protocol->protocol && 
+       (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE ||
+       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY))) {
+    silc_server_packet_parse_real(server->schedule, 0, sock->sock,
+                                 parser_context);
+
+    /* Reprocess data since we'll return FALSE here.  This is because
+       the idata->receive_key might have become valid in the last packet
+       and we want to call this processor with valid cipher. */
+    if (idata)
+      silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ? 
+                                 TRUE : FALSE, idata->receive_key, 
+                                 idata->hmac_receive, idata->psn_receive, 
+                                 silc_server_packet_parse, server);
+    else
+      silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ? 
+                                 TRUE : FALSE, NULL, NULL, 0, 
+                                 silc_server_packet_parse, server);
+    return FALSE;
+  }
+
   switch (sock->type) {
   case SILC_SOCKET_TYPE_UNKNOWN:
   case SILC_SOCKET_TYPE_CLIENT:
@@ -1672,16 +1722,15 @@ void silc_server_packet_parse(SilcPacketParserContext *parser_context,
     break;
   case SILC_SOCKET_TYPE_SERVER:
   case SILC_SOCKET_TYPE_ROUTER:
-    /* Packets from servers are parsed as soon as possible */
-    silc_schedule_task_add(server->schedule, sock->sock,
-                          silc_server_packet_parse_real,
-                          (void *)parser_context, 0, 1,
-                          SILC_TASK_TIMEOUT,
-                          SILC_TASK_PRI_NORMAL);
+    /* Packets from servers are parsed immediately */
+    silc_server_packet_parse_real(server->schedule, 0, sock->sock,
+                                 parser_context);
     break;
   default:
-    return;
+    return TRUE;
   }
+
+  return TRUE;
 }
 
 /* Parses the packet type and calls what ever routines the packet type
@@ -2096,7 +2145,11 @@ void silc_server_packet_parse_type(SilcServer server,
     break;
 
   case SILC_PACKET_FTP:
-    /* Ignored */
+    /* FTP packet */
+    SILC_LOG_DEBUG(("FTP packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_ftp(server, sock, packet);
     break;
 
   case SILC_PACKET_RESUME_ROUTER:
@@ -2144,6 +2197,9 @@ SILC_TASK_CALLBACK(silc_server_close_connection_final)
 void silc_server_close_connection(SilcServer server,
                                  SilcSocketConnection sock)
 {
+  if (!server->sockets[sock->sock])
+    return;
+
   SILC_LOG_INFO(("Closing connection %s:%d [%s]", sock->hostname,
                   sock->port,
                   (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
@@ -2240,12 +2296,15 @@ void silc_server_free_client_data(SilcServer server,
      to the network before removing the client entry. */
   silc_server_packet_queue_purge(server, sock);
 
+  if (!client->id)
+    return;
+
   /* Send SIGNOFF notify to routers. */
   if (notify && !server->standalone && server->router)
     silc_server_send_notify_signoff(server, server->router->connection,
                                    server->server_type == SILC_SERVER ?
                                    FALSE : TRUE, client->id, signoff);
-
+    
   /* Remove client from all channels */
   if (notify)
     silc_server_remove_from_channels(server, NULL, client, 
@@ -2253,7 +2312,7 @@ void silc_server_free_client_data(SilcServer server,
   else
     silc_server_remove_from_channels(server, NULL, client, 
                                     FALSE, NULL, FALSE);
-
+    
   /* We will not delete the client entry right away. We will take it
      into history (for WHOWAS command) for 5 minutes */
   i->server = server;
@@ -2352,6 +2411,8 @@ void silc_server_free_sock_user_data(SilcServer server,
           become invalid now as well. */
        if (user_data->id)
          silc_server_remove_clients_by_server(server, user_data, TRUE);
+       if (server->server_type == SILC_SERVER)
+         silc_server_remove_channels_by_server(server, user_data);
       } else {
        /* Update the client entries of this server to the new backup
           router. This also removes the clients that *really* was owned
@@ -2359,6 +2420,9 @@ void silc_server_free_sock_user_data(SilcServer server,
        silc_server_update_clients_by_server(server, user_data, backup_router,
                                             TRUE, TRUE);
        silc_server_update_servers_by_server(server, user_data, backup_router);
+       if (server->server_type == SILC_SERVER)
+         silc_server_update_channels_by_server(server, user_data, 
+                                               backup_router);
       }
 
       /* Free the server entry */
@@ -2447,8 +2511,8 @@ void silc_server_remove_from_channels(SilcServer server,
        silc_schedule_task_del_by_context(server->schedule, channel->rekey);
       if (silc_idlist_del_channel(server->local_list, channel))
        server->stat.my_channels--;
-      else if (silc_idlist_del_channel(server->global_list, channel))
-       server->stat.my_channels--;
+      else 
+        silc_idlist_del_channel(server->global_list, channel);
       continue;
     }
 
@@ -2485,7 +2549,7 @@ void silc_server_remove_from_channels(SilcServer server,
        SilcChannelClientEntry chl2;
        SilcHashTableList htl2;
 
-       channel->id = NULL;
+       channel->disabled = TRUE;
 
        silc_hash_table_list(channel->user_list, &htl2);
        while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
@@ -2499,8 +2563,8 @@ void silc_server_remove_from_channels(SilcServer server,
       /* Remove the channel entry */
       if (silc_idlist_del_channel(server->local_list, channel))
        server->stat.my_channels--;
-      else if (silc_idlist_del_channel(server->global_list, channel))
-       server->stat.my_channels--;
+      else 
+        silc_idlist_del_channel(server->global_list, channel);
       continue;
     }
 
@@ -2567,8 +2631,8 @@ int silc_server_remove_from_one_channel(SilcServer server,
       silc_schedule_task_del_by_context(server->schedule, channel->rekey);
     if (silc_idlist_del_channel(server->local_list, channel))
       server->stat.my_channels--;
-    else if (silc_idlist_del_channel(server->global_list, channel))
-      server->stat.my_channels--;
+    else 
+      silc_idlist_del_channel(server->global_list, channel);
     silc_buffer_free(clidp);
     return FALSE;
   }
@@ -2605,7 +2669,7 @@ int silc_server_remove_from_one_channel(SilcServer server,
       SilcChannelClientEntry chl2;
       SilcHashTableList htl2;
       
-      channel->id = NULL;
+      channel->disabled = TRUE;
       
       silc_hash_table_list(channel->user_list, &htl2);
       while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
@@ -2619,8 +2683,8 @@ int silc_server_remove_from_one_channel(SilcServer server,
     /* Remove the channel entry */
     if (silc_idlist_del_channel(server->local_list, channel))
       server->stat.my_channels--;
-    else if (silc_idlist_del_channel(server->global_list, channel))
-      server->stat.my_channels--;
+    else 
+      silc_idlist_del_channel(server->global_list, channel);
     return FALSE;
   }
 
@@ -2841,8 +2905,10 @@ bool silc_server_create_channel_key(SilcServer server,
   }
 
   if (!channel->channel_key)
-    if (!silc_cipher_alloc(SILC_DEFAULT_CIPHER, &channel->channel_key))
+    if (!silc_cipher_alloc(SILC_DEFAULT_CIPHER, &channel->channel_key)) {
+      channel->channel_key = NULL;
       return FALSE;
+    }
 
   if (key_len)
     len = key_len;
@@ -2914,7 +2980,8 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
   SILC_LOG_DEBUG(("Start"));
 
   /* Decode channel key payload */
-  payload = silc_channel_key_payload_parse(key_payload);
+  payload = silc_channel_key_payload_parse(key_payload->data,
+                                          key_payload->len);
   if (!payload) {
     SILC_LOG_ERROR(("Bad channel key payload, dropped"));
     channel = NULL;
@@ -2963,6 +3030,7 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
 
   /* Create new cipher */
   if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
+    channel->channel_key = NULL;
     channel = NULL;
     goto out;
   }
@@ -3338,7 +3406,7 @@ void silc_server_announce_get_channels(SilcServer server,
        (*channel_ids)[i] = NULL;
        silc_server_announce_get_channel_users(server, channel,
                                               channel_users,
-                                              channel_users_modes[i]);
+                                              &(*channel_users_modes)[i]);
        (*channel_ids)[i] = channel->id;
        i++;
 
@@ -3379,11 +3447,12 @@ void silc_server_announce_channels(SilcServer server,
                                    &channel_ids, creation_time);
 
   /* Get channels and channel users in global list */
-  silc_server_announce_get_channels(server, server->global_list,
-                                   &channels, &channel_users,
-                                   &channel_users_modes,
-                                   &channel_users_modes_c,
-                                   &channel_ids, creation_time);
+  if (server->server_type != SILC_SERVER)
+    silc_server_announce_get_channels(server, server->global_list,
+                                     &channels, &channel_users,
+                                     &channel_users_modes,
+                                     &channel_users_modes_c,
+                                     &channel_ids, creation_time);
 
   if (channels) {
     silc_buffer_push(channels, channels->data - channels->head);