From 04a72185341ca11e316fdbcd0867713b9674a7b8 Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Thu, 6 Nov 2003 08:43:02 +0000 Subject: [PATCH] Committed WHOIS search by public key patch from pat. --- CHANGES | 11 ++ apps/irssi/docs/help/in/whois.in | 3 + apps/irssi/src/silc/core/silc-servers.c | 2 +- apps/silcd/command_reply.c | 11 ++ apps/silcd/packet_receive.c | 45 +++++++ apps/silcd/server.c | 21 ++++ apps/silcd/server_internal.h | 3 + apps/silcd/server_query.c | 152 ++++++++++++++++++++---- apps/silcd/server_util.c | 13 ++ lib/silcclient/command.c | 86 +++++++++++--- 10 files changed, 307 insertions(+), 40 deletions(-) diff --git a/CHANGES b/CHANGES index ea4b837b..93bc57d7 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,14 @@ +Wed Nov 5 19:36:30 CET 2003 Patrik Weiskircher + + * Added support for whois using attributes. + Affected files silcd/command_reply.c, silcd/packet_receive.c, + silcd/server.c, silcd/server_internal.h, silcd/server_query.c, + silcd/server_util.c + + * Added support for whois using public key attribute to /WHOIS i + client command. Affected files irssi/docs/help/in/whois.in, + irssi/src/silc/core/silc-servers.c, lib/silcclient/command.c + Wed Nov 5 23:37:36 EET 2003 Pekka Riikonen * Fixed UMODE setting in server when the client has anonymous diff --git a/apps/irssi/docs/help/in/whois.in b/apps/irssi/docs/help/in/whois.in index b47f9b9a..ee168145 100644 --- a/apps/irssi/docs/help/in/whois.in +++ b/apps/irssi/docs/help/in/whois.in @@ -19,6 +19,9 @@ MAY return following information about the user: o User's geographical location o Information about the device user is using (computer, PDA, etc) +If the -pubkey option is used WHOIS will only retrieve the clients +with the corresponding public key. + NOTE: It is also possible to receive other information. Note that all users do not want to send these informations or may send only some of the information. It also possible that none of these diff --git a/apps/irssi/src/silc/core/silc-servers.c b/apps/irssi/src/silc/core/silc-servers.c index 0eb2ff3a..d5885194 100644 --- a/apps/irssi/src/silc/core/silc-servers.c +++ b/apps/irssi/src/silc/core/silc-servers.c @@ -421,7 +421,7 @@ char *silc_server_get_channels(SILC_SERVER_REC *server) /* SYNTAX: SILCOPER [-pubkey] */ /* SYNTAX: TOPIC [] */ /* SYNTAX: UMODE +|- */ -/* SYNTAX: WHOIS [@] [-details] [] */ +/* SYNTAX: WHOIS [[@]] [-details] [-pubkey ] [] */ /* SYNTAX: WHOWAS [@] [] */ /* SYNTAX: CLOSE [] */ /* SYNTAX: SHUTDOWN */ diff --git a/apps/silcd/command_reply.c b/apps/silcd/command_reply.c index 1350aee1..5a0ce2e8 100644 --- a/apps/silcd/command_reply.c +++ b/apps/silcd/command_reply.c @@ -147,6 +147,12 @@ silc_server_command_process_error(SilcServerCommandReplyContext cmd, client = silc_idlist_find_client_by_id(server->global_list, client_id, FALSE, NULL); if (client) { + + if (client->data.public_key) + silc_hash_table_del_by_context(server->pk_hash, + client->data.public_key, + client); + silc_server_remove_from_channels(server, NULL, client, TRUE, NULL, TRUE, FALSE); silc_idlist_del_data(client); @@ -1277,6 +1283,11 @@ SILC_SERVER_CMD_REPLY_FUNC(getkey) goto out; } + if (server->server_type != SILC_SERVER) + if (!silc_hash_table_find_by_context(server->pk_hash, public_key, + client, NULL)) + silc_hash_table_add(server->pk_hash, public_key, client); + client->data.public_key = public_key; public_key = NULL; } else if (id_type == SILC_ID_SERVER) { diff --git a/apps/silcd/packet_receive.c b/apps/silcd/packet_receive.c index b9a0224f..1767b6fa 100644 --- a/apps/silcd/packet_receive.c +++ b/apps/silcd/packet_receive.c @@ -361,6 +361,11 @@ void silc_server_notify(SilcServer server, SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR); silc_schedule_task_del_by_context(server->schedule, client); + /* Remove from public key hash table. */ + if (client->data.public_key) + silc_hash_table_del_by_context(server->pk_hash, client->data.public_key, + client); + /* Remove the client from all channels. */ silc_server_remove_from_channels(server, NULL, client, TRUE, tmp, FALSE, FALSE); @@ -1360,6 +1365,12 @@ void silc_server_notify(SilcServer server, if (local) silc_server_del_from_watcher_list(server, client); + /* Remove from public key hash table. */ + if (client->data.public_key) + silc_hash_table_del_by_context(server->pk_hash, + client->data.public_key, + client); + /* Remove the client */ silc_idlist_del_data(client); silc_idlist_del_client(local ? server->local_list : @@ -1607,6 +1618,12 @@ void silc_server_notify(SilcServer server, silc_server_check_watcher_list(server, client, NULL, SILC_NOTIFY_TYPE_KILLED); + /* Remove from public key hash table. */ + if (client->data.public_key) + silc_hash_table_del_by_context(server->pk_hash, + client->data.public_key, + client); + /* Update statistics */ server->stat.clients--; if (server->stat.cell_clients) @@ -1789,6 +1806,11 @@ void silc_server_notify(SilcServer server, client = silc_idlist_find_client_by_id(server->global_list, client_id, FALSE, NULL); if (client) { + if (client->data.public_key) + silc_hash_table_del_by_context(server->pk_hash, + client->data.public_key, + client); + silc_server_remove_from_channels(server, NULL, client, TRUE, NULL, TRUE, FALSE); silc_idlist_del_data(client); @@ -2806,6 +2828,22 @@ static void silc_server_new_id_real(SilcServer server, /* Check if anyone is watching this nickname */ if (server->server_type == SILC_ROUTER && id_list == server->local_list) silc_server_check_watcher_list(server, entry, NULL, 0); + + if (entry->data.public_key) { + silc_hash_table_add(server->pk_hash, entry->data.public_key, entry); + } else { + /* We need to get the public key using GETKEY */ + SilcBuffer idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT); + SilcSocketConnection dest_sock; + + dest_sock = silc_server_get_client_route(server, NULL, 0, entry->id, + NULL, NULL); + silc_server_send_command(server, dest_sock ? dest_sock + : SILC_PRIMARY_ROUTE(server), + SILC_COMMAND_GETKEY, ++server->cmd_ident, + 1, 1, idp->data, idp->len); + silc_buffer_free(idp); + } } break; @@ -3798,6 +3836,13 @@ void silc_server_resume_client(SilcServer server, server->stat.cell_clients--; silc_server_remove_from_channels(server, NULL, client, FALSE, NULL, FALSE, FALSE); + + /* Remove from public key hash table. */ + if (client->data.public_key) + silc_hash_table_del_by_context(server->pk_hash, + client->data.public_key, + client); + silc_server_del_from_watcher_list(server, client); if (!silc_idlist_del_client(server->local_list, client)) silc_idlist_del_client(server->global_list, client); diff --git a/apps/silcd/server.c b/apps/silcd/server.c index 9860302b..1fdaaa49 100644 --- a/apps/silcd/server.c +++ b/apps/silcd/server.c @@ -148,6 +148,9 @@ void silc_server_free(SilcServer server) if (list) silc_idcache_list_free(list); + if (server->pk_hash) + silc_hash_table_free(server->pk_hash); + /* Delete all servers */ list = NULL; if (silc_idcache_get_all(server->local_list->servers, &list) && @@ -356,6 +359,15 @@ bool silc_server_init(SilcServer server) if (!server->watcher_list) goto err; + /* Init public key list */ + server->pk_hash = + silc_hash_table_alloc(0, silc_hash_public_key, NULL, + silc_hash_public_key_compare, NULL, + NULL, NULL, TRUE); + + if (!server->pk_hash) + goto err; + /* Create a listening server */ if (!silc_server_listen(server, server->config->server_info->primary == NULL ? NULL : @@ -1959,6 +1971,10 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) client->mode |= SILC_UMODE_ANONYMOUS; } + /* Add public key to hash list (for whois using attributes) */ + silc_hash_table_add(server->pk_hash, + entry->data.public_key, client); + id_entry = (void *)client; break; } @@ -3268,6 +3284,11 @@ void silc_server_free_client_data(SilcServer server, /* Remove this client from watcher list if it is */ silc_server_del_from_watcher_list(server, client); + /* Remove this client from the public key hash list */ + if (client->data.public_key) + silc_hash_table_del_by_context(server->pk_hash, + client->data.public_key, client); + /* Update statistics */ server->stat.my_clients--; server->stat.clients--; diff --git a/apps/silcd/server_internal.h b/apps/silcd/server_internal.h index 7cf42a8e..6c99d1fd 100644 --- a/apps/silcd/server_internal.h +++ b/apps/silcd/server_internal.h @@ -146,6 +146,9 @@ struct SilcServerStruct { /* SIM (SILC Module) list */ SilcDList sim; #endif + + /* Hash table for public keys of all clients */ + SilcHashTable pk_hash; }; /* Failure context. This is allocated when failure packet is received. diff --git a/apps/silcd/server_query.c b/apps/silcd/server_query.c index 2993c5f6..7ec5daf0 100644 --- a/apps/silcd/server_query.c +++ b/apps/silcd/server_query.c @@ -234,11 +234,15 @@ bool silc_server_query_command(SilcServer server, SilcCommand querycmd, switch (querycmd) { case SILC_COMMAND_WHOIS: - /* If we are normal server and query contains nickname, send it - directly to router. */ + /* If we are normal server and query contains nickname OR query + doesn't contain nickname or ids BUT attributes, send it to the + router */ if (server->server_type == SILC_SERVER && !server->standalone && cmd->sock != SILC_PRIMARY_ROUTE(server) && - silc_argument_get_arg_type(cmd->args, 1, NULL)) { + (silc_argument_get_arg_type(cmd->args, 1, NULL) || + (!silc_argument_get_arg_type(cmd->args, 1, NULL) && + !silc_argument_get_arg_type(cmd->args, 4, NULL) && + silc_argument_get_arg_type(cmd->args, 3, NULL)))) { silc_server_query_send_router(server, query); return TRUE; } @@ -357,13 +361,27 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query) switch (query->querycmd) { case SILC_COMMAND_WHOIS: + /* Get requested attributes if set */ + tmp = silc_argument_get_arg_type(cmd->args, 3, &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; + } + /* Get Client IDs if present. Take IDs always instead of nickname. */ tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len); if (!tmp) { /* Get nickname */ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len); - if (!tmp) { + if (!tmp && !query->attrs) { + /* No nickname, no ids and no attributes - send error */ silc_server_query_send_error(server, query, SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0); silc_server_query_free(query); @@ -371,8 +389,8 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query) } /* Get the nickname@server string and parse it */ - if (tmp_len > 128 || - !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) { + if (tmp && ((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); @@ -425,20 +443,7 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query) tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len); if (tmp && tmp_len == sizeof(SilcUInt32)) SILC_GET32_MSB(query->reply_count, tmp); - - /* Get requested attributes if set */ - tmp = silc_argument_get_arg_type(cmd->args, 3, &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; + break; case SILC_COMMAND_WHOWAS: /* Get nickname */ @@ -563,6 +568,103 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query) silc_server_query_process(server, query, TRUE); } +/* Context for holding clients searched by public key. */ +typedef struct { + SilcClientEntry **clients; + SilcUInt32 *clients_count; +} *SilcServerPublicKeyUser, SilcServerPublicKeyUserStruct; + +void silc_server_public_key_hash_foreach(void *key, void *context, + void *user_context) +{ + SilcServerPublicKeyUser uc = user_context; + SilcClientEntry entry = context; + + /* Nothing was found, just return */ + if (!context) + return; + + (*uc->clients) = silc_realloc((*uc->clients), + sizeof((**uc->clients)) * + ((*uc->clients_count) + 1)); + (*uc->clients)[(*uc->clients_count)++] = entry; +} + +/* If clients are set, limit the found clients using the attributes in + the query. If clients are not set, try to find some clients using + the attributes */ + +void silc_server_query_check_attributes(SilcServer server, + SilcServerQuery query, + SilcClientEntry **clients, + SilcUInt32 *clients_count) { + SilcClientEntry entry; + SilcAttributePayload attr; + SilcAttribute attribute; + SilcAttributeObjPk pk; + SilcPublicKey publickey; + int i; + bool found = FALSE, no_clients = FALSE; + + /* If no clients were found, we only check the attributes + if the user wasn't searching for nickname/ids */ + if (!*clients) { + no_clients = TRUE; + if (query->nickname || query->ids_count) + return; + } + + 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_USER_PUBLIC_KEY: + found = TRUE; + + if (!silc_attribute_get_object(attr, &pk, sizeof(pk))) + continue; + + if (!silc_pkcs_public_key_decode(pk.data, pk.data_len, + &publickey)) + continue; + + /* If no clients were set on calling this function, we + just search for clients, otherwise we try to limit + the clients */ + if (no_clients) { + SilcServerPublicKeyUserStruct usercontext; + + usercontext.clients = clients; + usercontext.clients_count = clients_count; + + silc_hash_table_find_foreach(server->pk_hash, publickey, + silc_server_public_key_hash_foreach, + &usercontext); + } else { + for (i = 0; i < *clients_count; i++) { + entry = (*clients)[i]; + + if (!entry->data.public_key) + continue; + + if (!silc_hash_table_find_by_context(server->pk_hash, publickey, + entry, NULL)) + (*clients)[i] = NULL; + } + } + silc_pkcs_public_key_free(publickey); + break; + } + } + + if (!found && !query->nickname && !query->ids) { + silc_server_query_send_error(server, query, + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0); + silc_server_query_free(query); + } +} + /* Processes the parsed query. This does the actual finding of the queried information and prepares for sending reply to the original sender of the query command. */ @@ -718,6 +820,11 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query, } } + /* Check the attributes to narrow down the search by using them. */ + if (query->attrs) + silc_server_query_check_attributes(server, query, &clients, + &clients_count); + SILC_LOG_DEBUG(("Querying %d clients", clients_count)); SILC_LOG_DEBUG(("Querying %d servers", servers_count)); SILC_LOG_DEBUG(("Querying %d channels", channels_count)); @@ -1151,6 +1258,9 @@ void silc_server_query_send_reply(SilcServer server, /* Mark all invalid entries */ for (i = 0, valid_count = 0; i < clients_count; i++) { entry = clients[i]; + if (!entry) + continue; + switch (query->querycmd) { case SILC_COMMAND_WHOIS: if (!entry->nickname || !entry->username || !entry->userinfo || @@ -1379,7 +1489,7 @@ void silc_server_query_send_reply(SilcServer server, /* Not one valid entry was found, send error. If nickname was used in query send error based on that, otherwise the query->errors already includes proper errors. */ - if (query->nickname) + if (query->nickname || (!query->nickname && !query->ids && query->attrs)) silc_server_query_add_error(server, query, TRUE, 1, SILC_STATUS_ERR_NO_SUCH_NICK); } diff --git a/apps/silcd/server_util.c b/apps/silcd/server_util.c index 2cc49078..4002efa1 100644 --- a/apps/silcd/server_util.c +++ b/apps/silcd/server_util.c @@ -211,6 +211,10 @@ bool silc_server_remove_clients_by_server(SilcServer server, SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR); SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR); + if (client->data.public_key) + silc_hash_table_del_by_context(server->pk_hash, + client->data.public_key, + client); silc_server_remove_clients_channels(server, entry, clients, client, channels); silc_server_del_from_watcher_list(server, client); @@ -271,6 +275,10 @@ bool silc_server_remove_clients_by_server(SilcServer server, SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR); SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR); + if (client->data.public_key) + silc_hash_table_del_by_context(server->pk_hash, + client->data.public_key, + client); silc_server_remove_clients_channels(server, entry, clients, client, channels); silc_server_del_from_watcher_list(server, client); @@ -1640,6 +1648,11 @@ void silc_server_kill_client(SilcServer server, silc_idlist_del_data(remote_client); } + if (remote_client->data.public_key) + silc_hash_table_del_by_context(server->pk_hash, + remote_client->data.public_key, + remote_client); + /* Remove remote client */ silc_idlist_del_data(remote_client); if (!silc_idlist_del_client(server->global_list, remote_client)) { diff --git a/lib/silcclient/command.c b/lib/silcclient/command.c index 220ed86e..f0dcf465 100644 --- a/lib/silcclient/command.c +++ b/lib/silcclient/command.c @@ -285,6 +285,9 @@ SILC_CLIENT_CMD_FUNC(whois) SilcClientConnection conn = cmd->conn; SilcBuffer buffer, attrs = NULL; unsigned char count[4], *tmp = NULL; + int i; + bool details = FALSE, nick = FALSE; + unsigned char *pubkey = NULL; if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); @@ -302,28 +305,75 @@ SILC_CLIENT_CMD_FUNC(whois) goto out; } - if (cmd->argc == 2) { - buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS, - ++conn->cmd_ident, 1, - 1, cmd->argv[1], - cmd->argv_lens[1]); - } else { - if (!strcasecmp(cmd->argv[2], "-details")) + for (i = 1; i < cmd->argc; i++) { + if (!strcasecmp(cmd->argv[i], "-details")) { + details = TRUE; + } else if (!strcasecmp(cmd->argv[i], "-pubkey") && cmd->argc > i + 1) { + pubkey = cmd->argv[i + 1]; + i++; + } else { + /* We assume that the first parameter is the nickname, if it isn't + -details or -pubkey. The last parameter should always be the count */ + if (i == 1) { + nick = TRUE; + } else if (i == cmd->argc - 1) { + int c = atoi(cmd->argv[i]); + SILC_PUT32_MSB(c, count); + tmp = count; + } + } + } + + if (details) { + /* if pubkey is set, add all attributes to the + attrs buffer, except public key */ + if (pubkey) { + attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO, + SILC_ATTRIBUTE_SERVICE, + SILC_ATTRIBUTE_STATUS_MOOD, + SILC_ATTRIBUTE_STATUS_FREETEXT, + SILC_ATTRIBUTE_STATUS_MESSAGE, + SILC_ATTRIBUTE_PREFERRED_LANGUAGE, + SILC_ATTRIBUTE_PREFERRED_CONTACT, + SILC_ATTRIBUTE_TIMEZONE, + SILC_ATTRIBUTE_GEOLOCATION, + SILC_ATTRIBUTE_DEVICE_INFO, 0); + } else { attrs = silc_client_attributes_request(0); + } + } - if (!attrs || cmd->argc > 3) { - int c = atoi(cmd->argc > 3 ? cmd->argv[3] : cmd->argv[2]); - SILC_PUT32_MSB(c, count); - tmp = count; + if (pubkey) { + SilcAttributeObjPk obj; + SilcPublicKey pk; + + if (!silc_pkcs_load_public_key(pubkey, &pk, SILC_PKCS_FILE_PEM)) { + if (!silc_pkcs_load_public_key(pubkey, &pk, SILC_PKCS_FILE_BIN)) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Could not load public key %s, check the filename", + pubkey); + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } } - buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS, - ++conn->cmd_ident, 3, - 1, cmd->argv[1], cmd->argv_lens[1], - 2, tmp ? tmp : NULL, tmp ? 4 : 0, - 3, attrs ? attrs->data : NULL, - attrs ? attrs->len : 0); + obj.type = "silc-rsa"; + obj.data = silc_pkcs_public_key_encode(pk, &obj.data_len); + + attrs = silc_attribute_payload_encode(attrs, + SILC_ATTRIBUTE_USER_PUBLIC_KEY, + SILC_ATTRIBUTE_FLAG_VALID, + &obj, sizeof(obj)); } + + buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS, + ++conn->cmd_ident, 3, + 1, nick ? cmd->argv[1] : NULL, + nick ? cmd->argv_lens[1] : 0, + 2, tmp ? tmp : NULL, tmp ? 4 : 0, + 3, attrs ? attrs->data : NULL, + attrs ? attrs->len : 0); + silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, buffer->data, buffer->len, TRUE); @@ -2734,7 +2784,7 @@ void silc_client_commands_register(SilcClient client) silc_list_init(client->internal->commands, struct SilcClientCommandStruct, next); - SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 3); + SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 5); SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3); SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3); SILC_CLIENT_CMD(nick, NICK, "NICK", 2); -- 2.43.0