updates.
[silc.git] / apps / silcd / command.c
index 6792f3c0f72ad40082c4ea82814b007ceabcbec7..4133e7ce0517a4e380f9353dc1d2bf2e7e970532 100644 (file)
@@ -59,7 +59,7 @@ SilcServerCommand silc_command_list[] =
   SILC_SERVER_CMD(join, JOIN, SILC_CF_LAG_STRICT | SILC_CF_REG),
   SILC_SERVER_CMD(motd, MOTD, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(umode, UMODE, SILC_CF_LAG | SILC_CF_REG),
-  SILC_SERVER_CMD(cmode, CMODE, SILC_CF_LAG | SILC_CF_REG),
+  SILC_SERVER_CMD(cmode, CMODE, SILC_CF_LAG_STRICT | SILC_CF_REG),
   SILC_SERVER_CMD(cumode, CUMODE, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(kick, KICK, SILC_CF_LAG_STRICT | SILC_CF_REG),
   SILC_SERVER_CMD(restart, RESTART, 
@@ -1686,7 +1686,7 @@ SILC_SERVER_CMD_FUNC(nick)
   nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
   /* Send NICK_CHANGE notify to the client's channels */
-  silc_server_send_notify_on_channels(server, client, 
+  silc_server_send_notify_on_channels(server, NULL, client, 
                                      SILC_NOTIFY_TYPE_NICK_CHANGE, 2,
                                      oidp->data, oidp->len, 
                                      nidp->data, nidp->len);
@@ -2010,8 +2010,102 @@ SILC_SERVER_CMD_FUNC(quit)
   silc_server_command_free(cmd);
 }
 
+/* Server side of command KILL. This command is used by router operator
+   to remove an client from the SILC Network temporarily. */
+
 SILC_SERVER_CMD_FUNC(kill)
 {
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  SilcClientEntry remote_client;
+  SilcClientID *client_id;
+  unsigned char *tmp, *comment;
+  unsigned int tmp_len;
+  SilcBuffer idp;
+
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_KILL, cmd, 1, 2);
+
+  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
+
+  if (server->server_type != SILC_ROUTER)
+    goto out;
+
+  /* Check whether client has the permissions. */
+  if (!(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                         SILC_STATUS_ERR_NO_ROUTER_PRIV);
+    goto out;
+  }
+
+  /* Get the client ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!tmp) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+  client_id = silc_id_payload_parse_id(tmp, tmp_len);
+  if (!client_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                         SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+    goto out;
+  }
+
+  /* Get the client entry */
+  remote_client = silc_idlist_find_client_by_id(server->local_list, 
+                                               client_id, NULL);
+  if (!remote_client) {
+    remote_client = silc_idlist_find_client_by_id(server->global_list, 
+                                                 client_id, NULL);
+    if (!remote_client) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                           SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+      goto out;
+    }
+  }
+
+  /* Get comment */
+  comment = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                       SILC_STATUS_OK);
+
+  /* Send KILLED notify to the channels. It is not sent to the client
+     as it will be sent differently destined directly to the client and not
+     to the channel. */
+  idp = silc_id_payload_encode(remote_client->id, SILC_ID_CLIENT);
+  silc_server_send_notify_on_channels(server, remote_client->connection, 
+                                     remote_client, SILC_NOTIFY_TYPE_KILLED,
+                                     comment ? 2 : 1,
+                                     idp->data, idp->len,
+                                     comment, comment ? strlen(comment) : 0);
+  silc_buffer_free(idp);
+
+  /* Remove the client from all channels. This generates new keys to the
+     channels as well. */
+  silc_server_remove_from_channels(server, NULL, remote_client, FALSE, 
+                                  NULL, TRUE);
+
+  /* Send KILLED notify to the client directly */
+  silc_server_send_notify_killed(server, remote_client->connection ? 
+                                remote_client->connection : 
+                                remote_client->router->connection, FALSE,
+                                remote_client->id, SILC_ID_CLIENT_LEN,
+                                comment);
+
+  /* Send KILLED notify to primary route */
+  if (!server->standalone)
+    silc_server_send_notify_killed(server, server->router->connection,
+                                  server->server_type == SILC_ROUTER ?
+                                  TRUE : FALSE,
+                                  remote_client->id, SILC_ID_CLIENT_LEN,
+                                  comment);
+
+ out:
+  silc_server_command_free(cmd);
 }
 
 /* Server side of command INFO. This sends information about us to 
@@ -3280,7 +3374,7 @@ SILC_SERVER_CMD_FUNC(kick)
   silc_buffer_free(idp);
 
   /* Remove the client from the channel. If the channel does not exist
-     after removing the client then the client kicked itself of the channel
+     after removing the client then the client kicked itself off the channel
      and we don't have to send anything after that. */
   if (!silc_server_remove_from_one_channel(server, NULL, channel, 
                                           target_client, FALSE))
@@ -3298,7 +3392,7 @@ SILC_SERVER_CMD_FUNC(kick)
   silc_server_create_channel_key(server, channel, 0);
 
   /* Send the channel key to the channel. The key of course is not sent
-     to the client who joined the channel. */
+     to the client who was kicked off the channel. */
   silc_server_send_channel_key(server, target_client->connection, channel, 
                               server->server_type == SILC_ROUTER ? 
                               FALSE : !server->standalone);
@@ -3307,12 +3401,138 @@ SILC_SERVER_CMD_FUNC(kick)
   silc_server_command_free(cmd);
 }
 
+/* Server side of OPER command. Client uses this comand to obtain server
+   operator privileges to this server/router. */
+
 SILC_SERVER_CMD_FUNC(oper)
 {
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  unsigned char *username, *auth;
+  unsigned int tmp_len;
+  SilcServerConfigSectionAdminConnection *admin;
+  SilcIDListData idata = (SilcIDListData)client;
+
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_OPER, cmd, 1, 2);
+
+  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
+
+  /* Get the username */
+  username = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!username) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  /* Get the admin configuration */
+  admin = silc_server_config_find_admin(server->config, cmd->sock->ip,
+                                       username, client->nickname);
+  if (!admin) {
+    admin = silc_server_config_find_admin(server->config, cmd->sock->hostname,
+                                         username, client->nickname);
+    if (!admin) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+                                           SILC_STATUS_ERR_AUTH_FAILED);
+      goto out;
+    }
+  }
+
+  /* Get the authentication payload */
+  auth = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (!auth) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  /* Verify the authentication data */
+  if (!silc_auth_verify_data(auth, tmp_len, admin->auth_meth, 
+                            admin->auth_data, admin->auth_data_len,
+                            idata->hash, client->id, SILC_ID_CLIENT)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+                                         SILC_STATUS_ERR_AUTH_FAILED);
+    goto out;
+  }
+
+  /* Client is now server operator */
+  client->mode |= SILC_UMODE_SERVER_OPERATOR;
+
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+                                       SILC_STATUS_OK);
+
+ out:
+  silc_server_command_free(cmd);
 }
 
+/* Server side of SILCOPER command. Client uses this comand to obtain router
+   operator privileges to this router. */
+
 SILC_SERVER_CMD_FUNC(silcoper)
 {
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  unsigned char *username, *auth;
+  unsigned int tmp_len;
+  SilcServerConfigSectionAdminConnection *admin;
+  SilcIDListData idata = (SilcIDListData)client;
+
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_SILCOPER, cmd, 1, 2);
+
+  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
+
+  /* Get the username */
+  username = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!username) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  /* Get the admin configuration */
+  admin = silc_server_config_find_admin(server->config, cmd->sock->ip,
+                                       username, client->nickname);
+  if (!admin) {
+    admin = silc_server_config_find_admin(server->config, cmd->sock->hostname,
+                                         username, client->nickname);
+    if (!admin) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+                                           SILC_STATUS_ERR_AUTH_FAILED);
+      goto out;
+    }
+  }
+
+  /* Get the authentication payload */
+  auth = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (!auth) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  /* Verify the authentication data */
+  if (!silc_auth_verify_data(auth, tmp_len, admin->auth_meth, 
+                            admin->auth_data, admin->auth_data_len,
+                            idata->hash, client->id, SILC_ID_CLIENT)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+                                         SILC_STATUS_ERR_AUTH_FAILED);
+    goto out;
+  }
+
+  /* Client is now router operator */
+  client->mode |= SILC_UMODE_ROUTER_OPERATOR;
+
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+                                       SILC_STATUS_OK);
+
+ out:
+  silc_server_command_free(cmd);
 }
 
 /* Server side command of CONNECT. Connects us to the specified remote
@@ -3323,7 +3543,7 @@ SILC_SERVER_CMD_FUNC(connect)
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
-  unsigned char *tmp;
+  unsigned char *tmp, *host;
   unsigned int tmp_len;
   unsigned int port = SILC_PORT;
 
@@ -3347,8 +3567,8 @@ SILC_SERVER_CMD_FUNC(connect)
   }
 
   /* Get the remote server */
-  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
-  if (!tmp) {
+  host = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!host) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
                                          SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
@@ -3360,7 +3580,7 @@ SILC_SERVER_CMD_FUNC(connect)
     SILC_GET32_MSB(port, tmp);
 
   /* Create the connection. It is done with timeout and is async. */
-  silc_server_create_connection(server, tmp, port);
+  silc_server_create_connection(server, host, port);
 
   /* Send reply to the sender */
   silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
@@ -3382,6 +3602,7 @@ SILC_SERVER_CMD_FUNC(close)
   SilcServer server = cmd->server;
   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
   SilcServerEntry server_entry;
+  SilcSocketConnection sock;
   unsigned char *tmp;
   unsigned int tmp_len;
   unsigned char *name;
@@ -3420,16 +3641,15 @@ SILC_SERVER_CMD_FUNC(close)
     goto out;
   }
 
-  /* Close the connection to the server */
-  silc_server_free_sock_user_data(server, server_entry->connection);
-  silc_server_disconnect_remote(server, server_entry->connection,
-                               "Server closed connection: "
-                               "Closed by operator");
-  
   /* Send reply to the sender */
   silc_server_command_send_status_reply(cmd, SILC_COMMAND_CLOSE,
                                        SILC_STATUS_OK);
 
+  /* Close the connection to the server */
+  sock = (SilcSocketConnection)server_entry->connection;
+  silc_server_free_sock_user_data(server, sock);
+  silc_server_close_connection(server, sock);
+  
  out:
   silc_server_command_free(cmd);
 }
@@ -3455,13 +3675,14 @@ SILC_SERVER_CMD_FUNC(shutdown)
     goto out;
   }
 
-  /* Then, gracefully, or not, bring the server down. */
-  silc_server_stop(server);
-
   /* Send reply to the sender */
   silc_server_command_send_status_reply(cmd, SILC_COMMAND_SHUTDOWN,
                                        SILC_STATUS_OK);
 
+  /* Then, gracefully, or not, bring the server down. */
+  silc_server_stop(server);
+  exit(0);
+
  out:
   silc_server_command_free(cmd);
 }