+Wed Mar 28 15:52:36 EEST 2001 Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+ * Added SilcKeyAgreementStatus type to the key agreement routines
+ to indicate the current status and error if one occured.
+ The status types are defined in the lib/silcclient/silcapi.h.
+
+ * Added new local command KEY that is used to set and unset private
+ keys for channels, set and unset private keys for private messages
+ with remote clients and to send key agreement requests and
+ negotiate the key agreement protocol with remote client. The
+ key agreement is supported only to negotiate private message keys,
+ it currently cannot be used to negotiate private keys for channels,
+ as it is not convenient for that purpose.
+
+ * Fixed a minor pending callback setting bug in the function
+ silc_client_get_client_by_id_resolve, now the function works.
+ Affected file lib/silcclient/idlist.c.
+
+ * Added function silc_net_get_local_port to get local bound
+ port by socket. Added to lib/silcutil/silcnet.[ch].
+
+ * Added `sockets' and `sockets_count' fields to the SilcClient
+ object. They hold the sockets of the listenning sockets in
+ the client. Listenning sockets may be for example the key
+ agreement server. Affected file lib/silcclient/client.[ch].
+ Added functions the silc_client_add_socket and the
+ silc_client_del_socket. They are exported to the application
+ as well.
+
+ * Added ~./silc/clientkeys to support other client's public keys.
+
+ * Renamed verify_server_key client operation to verify_public_key
+ and added one argument to indicate the type of the connection
+ (server, client etc.).
+
Tue Mar 27 22:22:38 EEST 2001 Pekka Riikonen <priikone@poseidon.pspt.fi>
* Added silc_server_connection_auth_request to handle the
specified. If the channel cannot be found then all
channels are listed.
+ /KEY msg|channel <nickname|channel>
+ set|unset|list|agreement|negotiate [<arguments>]
+
+ This command is used to set and unset private keys for
+ channels, set and unset private keys for private messages
+ with remote clients and to send key agreement requests and
+ negotiate the key agreement protocol with remote client.
+ The key agreement is supported only to negotiate private
+ message keys, it currently cannot be used to negotiate
+ private keys for channels, as it is not convenient for that
+ purpose.
+
+ Types:
+
+ msg The command is performed for private messages
+ affecting the <nickname>.
+
+ channel The command is performed for channel affecting
+ the <channel>.
+
+
+ Commands:
+
+ set [<key> [<cipher>] [<hmac>]]
+
+ Set the key into use. If the <key> is provided it
+ is used as the key material. If the <key> is not
+ provided the negotiated key material is used. If
+ the negotiation has not been performed this command
+ has no effect.
+
+ If the type is `msg' and the <key> is `*' then
+ random key will be generated automatically.
+
+ The <cipher> may be set for both private message
+ and channel private keys and the <hmac> may be set
+ only to the channel private keys.
+
+ unset [<number>]
+
+ Unset the key. The private key is not used after
+ this command. The key must be set again or the key
+ material must be re-negotiated to be able to use
+ the private keys again.
+
+ The channel may have several private keys set. The
+ <number> can be used to indicate what key is being
+ unset. If it is not provided all keys are removed.
+
+
+ list List all private keys that has been set.
+
+ If the type is `msg' and the <nickname> is ยด*' then
+ all private message keys that you've set will be
+ listed.
+
+ agreement [<hostname> [<port>]]
+
+ Send key agreement request to remote client. If
+ the <hostname> is provided it is sent in the request.
+ The receiver may use the hostname to start the
+ key agreement. If the <port> is also provided your
+ key agreement protocol server is bound to that
+ port. Note that it cannot be privileged port (<1023).
+ If the <hostname> and <port> is not provided then
+ the receiver will never initiate the key agreement.
+ In this case you must start the key agreement after
+ receiving the reply to the request, by giving the
+ /KEYAGR start command.
+
+ This command may be used to send reply to the
+ remote client. When receiving empty key agreement
+ you can reply to the sender with the hostname and
+ port of your key agreement server with this command.
+
+ negotiate [<hostname> [<port>]]
+
+ This may be called to start the key agreement with
+ <nickname>. This command has effect only if the
+ <nickname> has replied to your key agreement request.
+ You will see a notify on the screen when the reply
+ arrives. The <hostname> and <port> is the hostname
+ and port of the remote client's key agreement
+ server.
+
Features
========
TODO In SILC Client Library
===========================
- o TODO in commands (silc/local_command.c, lib/silcclient/command.c and
+ o TODO in commands (lib/silcclient/command.c and
silc/silclient/command_reply.c):
- o Local command to handle private message keys is not done
- o Local command to handle channel private keys is not done
- o Local command to handle key agreement protocol is not done
o RESTART command is not implemented
o Client library crashes if for example server timeouts protocol
When implementing this check that all commands handle the
situation correctly when it is called as pending command
(it should most likely check that cmd->pending == TRUE/FALSE).
+ o WHOIS and IDENTIFY command reply sending is buggy because
+ it may put status to be SILC_STATUS_LIST_START even though
+ there is only one valid entry (thus should be SILC_STATUS_OK).
+ This happens because it does not check the data.registered == FALSE
+ clients before setting the status. Good fix for this would be
+ to make sure that the clients table does not include any
+ unregistered clients in the first place.
o TODO in notify types (packet_receive.c):
}
/* Verifies received public key. If user decides to trust the key it is
- saved as trusted server key for later use. If user does not trust the
+ saved as public server key for later use. If user does not trust the
key this returns FALSE. */
-int silc_verify_server_key(SilcClient client,
+int silc_verify_public_key(SilcClient client,
SilcClientConnection conn,
+ SilcSocketType conn_type,
unsigned char *pk, unsigned int pk_len,
SilcSKEPKType pk_type)
{
char *hostname, *fingerprint;
struct passwd *pw;
struct stat st;
+ char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
+ conn_type == SILC_SOCKET_TYPE_ROUTER) ?
+ "server" : "client");
hostname = sock->hostname ? sock->hostname : sock->ip;
if (pk_type != SILC_SKE_PK_TYPE_SILC) {
- silc_say(client, conn, "We don't support server %s key type", hostname);
+ silc_say(client, conn, "We don't support %s %s key type",
+ entity, hostname);
return FALSE;
}
memset(filename, 0, sizeof(filename));
memset(file, 0, sizeof(file));
- snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname,
+ snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, hostname,
sock->port);
- snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s",
- pw->pw_dir, file);
+ snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s",
+ pw->pw_dir, entity, file);
/* Check wheter this key already exists */
if (stat(filename, &st) < 0) {
fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
- silc_say(client, conn, "Received server %s public key", hostname);
- silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
+ silc_say(client, conn, "Received %s %s public key", entity, hostname);
+ silc_say(client, conn, "Fingerprint for the %s %s key is", entity,
+ hostname);
silc_say(client, conn, "%s", fingerprint);
silc_free(fingerprint);
if (!silc_pkcs_load_public_key(filename, &public_key,
SILC_PKCS_FILE_BIN)) {
fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
- silc_say(client, conn, "Received server %s public key", hostname);
- silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
+ silc_say(client, conn, "Received %s %s public key", entity, hostname);
+ silc_say(client, conn, "Fingerprint for the %s %s key is",
+ entity, hostname);
silc_say(client, conn, "%s", fingerprint);
silc_free(fingerprint);
- silc_say(client, conn, "Could not load your local copy of the server %s key",
- hostname);
+ silc_say(client, conn, "Could not load your local copy of the %s %s key",
+ entity, hostname);
if (silc_client_ask_yes_no(client,
"Would you like to accept the key anyway (y/n)? "))
{
encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
if (!encpk) {
fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
- silc_say(client, conn, "Received server %s public key", hostname);
- silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
+ silc_say(client, conn, "Received %s %s public key", entity, hostname);
+ silc_say(client, conn, "Fingerprint for the %s %s key is",
+ entity, hostname);
silc_say(client, conn, "%s", fingerprint);
silc_free(fingerprint);
- silc_say(client, conn, "Your local copy of the server %s key is malformed",
- hostname);
+ silc_say(client, conn, "Your local copy of the %s %s key is malformed",
+ entity, hostname);
if (silc_client_ask_yes_no(client,
"Would you like to accept the key anyway (y/n)? "))
{
if (memcmp(encpk, pk, encpk_len)) {
fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
- silc_say(client, conn, "Received server %s public key", hostname);
- silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
+ silc_say(client, conn, "Received %s %s public key", entity, hostname);
+ silc_say(client, conn, "Fingerprint for the %s %s key is",
+ entity, hostname);
silc_say(client, conn, "%s", fingerprint);
silc_free(fingerprint);
- silc_say(client, conn, "Server %s key does not match with your local copy",
- hostname);
+ silc_say(client, conn, "%s %s key does not match with your local copy",
+ entity, hostname);
silc_say(client, conn, "It is possible that the key has expired or changed");
silc_say(client, conn, "It is also possible that some one is performing "
"man-in-the-middle attack");
return TRUE;
}
- silc_say(client, conn, "Will not accept server %s key", hostname);
+ silc_say(client, conn, "Will not accept %s %s key", entity, hostname);
return FALSE;
}
return TRUE;
}
- silc_say(client, conn, "Will not accept server %s key", hostname);
+ silc_say(client, conn, "Will not accept %s %s key", entity, hostname);
return FALSE;
}
SilcKeyAgreementCallback *completion,
void **context)
{
+ char host[256];
+
+ /* We will just display the info on the screen and return FALSE and user
+ will have to start the key agreement with a command. */
+
+ if (hostname) {
+ memset(host, 0, sizeof(host));
+ snprintf(host, sizeof(host) - 1, "(%s on port %d)", hostname, port);
+ }
+
+ silc_say(client, conn, "%s wants to perform key agreement %s",
+ client_entry->nickname, hostname ? host : "");
+
+ *completion = NULL;
+ *context = NULL;
return FALSE;
}
connect: silc_connect,
disconnect: silc_disconnect,
get_auth_method: silc_get_auth_method,
- verify_server_key: silc_verify_server_key,
+ verify_public_key: silc_verify_public_key,
ask_passphrase: silc_ask_passphrase,
failure: silc_failure,
key_agreement: silc_key_agreement,
int silc_client_check_silc_dir()
{
char filename[256], file_public_key[256], file_private_key[256];
- char servfilename[256];
+ char servfilename[256], clientfilename[256];
char *identifier;
struct stat st;
struct passwd *pw;
snprintf(filename, sizeof(filename) - 1, "%s/.silc/", pw->pw_dir);
snprintf(servfilename, sizeof(servfilename) - 1, "%s/.silc/serverkeys",
pw->pw_dir);
+ snprintf(clientfilename, sizeof(clientfilename) - 1, "%s/.silc/clientkeys",
+ pw->pw_dir);
/*
* Check ~/.silc directory
}
}
+ /*
+ * Check ~./silc/clientkeys directory
+ */
+ if ((stat(clientfilename, &st)) == -1) {
+ /* If dir doesn't exist */
+ if (errno == ENOENT) {
+ if (pw->pw_uid == geteuid()) {
+ if ((mkdir(clientfilename, 0755)) == -1) {
+ fprintf(stderr, "Couldn't create `%s' directory\n", clientfilename);
+ return FALSE;
+ }
+ } else {
+ fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
+ clientfilename);
+ return FALSE;
+ }
+ } else {
+ fprintf(stderr, "%s\n", strerror(errno));
+ return FALSE;
+ }
+ }
+
/*
* Check Public and Private keys
*/
SILC_CLIENT_LCMD(server, SERVER, "SERVER", 0, 2),
SILC_CLIENT_LCMD(msg, MSG, "MSG", 0, 3),
SILC_CLIENT_LCMD(away, AWAY, "AWAY", 0, 2),
+ SILC_CLIENT_LCMD(key, KEY, "KEY", 0, 7),
{ NULL, 0, NULL, 0, 0 },
};
SILC_CLIENT_LCMD_FUNC(clear)
{
SilcClientCommandContext cmd = (SilcClientCommandContext)context;
- SilcClient client = cmd->client;
- SilcClientInternal app = (SilcClientInternal)client->application;
-
-#if 0
- wclear((WINDOW *)app->screen);
- wrefresh((WINDOW *)app->screen);
-#endif
silc_client_command_free(cmd);
}
out:
silc_client_command_free(cmd);
}
+
+typedef struct {
+ int type; /* 1 = msg, 2 = channel */
+} *KeyInternal;
+
+static SilcSKEKeyMaterial *curr_key = NULL;
+
+/* Key agreement callback that is called after the key agreement protocol
+ has been performed. This is called also if error occured during the
+ key agreement protocol. The `key' is the allocated key material and
+ the caller is responsible of freeing it. The `key' is NULL if error
+ has occured. The application can freely use the `key' to whatever
+ purpose it needs. See lib/silcske/silcske.h for the definition of
+ the SilcSKEKeyMaterial structure. */
+
+static void keyagr_completion(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry client_entry,
+ SilcKeyAgreementStatus status,
+ SilcSKEKeyMaterial *key,
+ void *context)
+{
+ KeyInternal i = (KeyInternal)context;
+
+ curr_key = NULL;
+
+ switch(status) {
+ case SILC_KEY_AGREEMENT_OK:
+ silc_say(client, conn, "Key agreement compeleted successfully with %s",
+ client_entry->nickname);;
+
+ if (i->type == 1) {
+ if (!silc_client_ask_yes_no(client,
+ "Would you like to use the key with private messages (y/n)? ")) {
+ silc_say(client, conn, "You can set the key material into use later by giving /KEY msg set command");
+ curr_key = key;
+ break;
+ }
+
+ /* Set the private key for this client */
+ silc_client_del_private_message_key(client, conn, client_entry);
+ silc_client_add_private_message_key_ske(client, conn, client_entry,
+ NULL, key);
+ silc_say(client, conn, "The private messages with the %s are now protected with the private key", client_entry->nickname);
+ silc_ske_free_key_material(key);
+ }
+
+ break;
+
+ case SILC_KEY_AGREEMENT_ERROR:
+ silc_say(client, conn, "Error occured during key agreement with %s",
+ client_entry->nickname);
+ break;
+
+ case SILC_KEY_AGREEMENT_FAILURE:
+ silc_say(client, conn, "The key agreement failed with %s",
+ client_entry->nickname);
+ break;
+
+ case SILC_KEY_AGREEMENT_TIMEOUT:
+ silc_say(client, conn, "Timeout during key agreement. The key agreement was not performed with %s",
+ client_entry->nickname);
+ break;
+
+ default:
+ break;
+ }
+
+ if (i)
+ silc_free(i);
+}
+
+/* Local command KEY. This command is used to set and unset private
+ keys for channels, set and unset private keys for private messages
+ with remote clients and to send key agreement requests and
+ negotiate the key agreement protocol with remote client. The
+ key agreement is supported only to negotiate private message keys,
+ it currently cannot be used to negotiate private keys for channels,
+ as it is not convenient for that purpose. */
+
+SILC_CLIENT_LCMD_FUNC(key)
+{
+ SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+ SilcClientConnection conn = cmd->conn;
+ SilcClient client = cmd->client;
+ SilcClientEntry client_entry = NULL;
+ SilcChannelEntry channel_entry = NULL;
+ unsigned int num = 0;
+ char *nickname = NULL, *server = NULL;
+ int command = 0, port = 0, type = 0;
+ char *hostname = NULL;
+ KeyInternal internal = NULL;
+
+ if (!cmd->conn) {
+ silc_say(client, conn,
+ "You are not connected to a server, use /SERVER to connect");
+ goto out;
+ }
+
+ if (cmd->argc < 4) {
+ silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
+ "set|unset|agreement|negotiate [<arguments>]");
+ goto out;
+ }
+
+ /* Get type */
+ if (!strcasecmp(cmd->argv[1], "msg"))
+ type = 1;
+ if (!strcasecmp(cmd->argv[1], "channel"))
+ type = 2;
+
+ if (type == 0) {
+ silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
+ "set|unset|agreement|negotiate [<arguments>]");
+ goto out;
+ }
+
+ if (type == 1) {
+ if (cmd->argv[2][0] == '*') {
+ nickname = "*";
+ } else {
+ /* Parse the typed nickname. */
+ if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
+ silc_say(client, conn, "Bad nickname");
+ goto out;
+ }
+
+ /* Find client entry */
+ client_entry = silc_idlist_get_client(client, conn, nickname,
+ server, num, TRUE);
+ if (!client_entry) {
+ /* Client entry not found, it was requested thus mark this to be
+ pending command. */
+ silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
+ conn->cmd_ident,
+ NULL, silc_client_local_command_key,
+ context);
+ return;
+ }
+ }
+ }
+
+ if (type == 2) {
+ /* Get channel entry */
+ char *name;
+
+ if (cmd->argv[2][0] == '*') {
+ if (!conn->current_channel) {
+ cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
+ goto out;
+ }
+ name = conn->current_channel->channel_name;
+ } else {
+ name = cmd->argv[2];
+ }
+
+ channel_entry = silc_client_get_channel(client, conn, name);
+ if (!channel_entry) {
+ silc_say(client, conn, "You are not on that channel");
+ goto out;
+ }
+ }
+
+ /* Set command */
+ if (!strcasecmp(cmd->argv[3], "set")) {
+ command = 1;
+
+ if (cmd->argc == 3) {
+ if (curr_key && type == 1 && client_entry) {
+ silc_client_del_private_message_key(client, conn, client_entry);
+ silc_client_add_private_message_key_ske(client, conn, client_entry,
+ NULL, curr_key);
+ goto out;
+ }
+ }
+
+ if (cmd->argc >= 4) {
+ if (type == 1 && client_entry) {
+ /* Set private message key */
+
+ silc_client_del_private_message_key(client, conn, client_entry);
+
+ if (cmd->argc >= 5)
+ silc_client_add_private_message_key(client, conn, client_entry,
+ cmd->argv[5], cmd->argv[4],
+ cmd->argv_lens[4],
+ (cmd->argv[4][0] == '*' ?
+ TRUE : FALSE));
+ else
+ silc_client_add_private_message_key(client, conn, client_entry,
+ NULL, cmd->argv[4],
+ cmd->argv_lens[4],
+ (cmd->argv[4][0] == '*' ?
+ TRUE : FALSE));
+
+ /* Send the key to the remote client so that it starts using it
+ too. */
+ silc_client_send_private_message_key(client, conn, client_entry, TRUE);
+ } else if (type == 2) {
+ /* Set private channel key */
+ char *cipher = NULL, *hmac = NULL;
+
+ if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+ silc_say(client, conn,
+ "Private key mode is not set on this channel");
+ goto out;
+ }
+
+ if (cmd->argc >= 5)
+ cipher = cmd->argv[5];
+ if (cmd->argc >= 6)
+ hmac = cmd->argv[6];
+
+ if (!silc_client_add_channel_private_key(client, conn, channel_entry,
+ cipher, hmac,
+ cmd->argv[4],
+ cmd->argv_lens[4])) {
+ silc_say(client, conn, "Could not add channel private key");
+ goto out;
+ }
+ }
+ }
+
+ goto out;
+ }
+
+ /* Unset command */
+ if (!strcasecmp(cmd->argv[3], "unset")) {
+ command = 2;
+
+ if (type == 1 && client_entry) {
+ /* Unset private message key */
+ silc_client_del_private_message_key(client, conn, client_entry);
+ } else if (type == 2) {
+ /* Unset channel key(s) */
+ SilcChannelPrivateKey *keys;
+ unsigned int keys_count;
+ int number;
+
+ if (cmd->argc == 3)
+ silc_client_del_channel_private_keys(client, conn, channel_entry);
+
+ if (cmd->argc > 3) {
+ number = atoi(cmd->argv[4]);
+ keys = silc_client_list_channel_private_keys(client, conn,
+ channel_entry,
+ &keys_count);
+ if (!keys)
+ goto out;
+
+ if (!number || number > keys_count) {
+ silc_client_free_channel_private_keys(keys, keys_count);
+ goto out;
+ }
+
+ silc_client_del_channel_private_key(client, conn, channel_entry,
+ keys[number - 1]);
+ silc_client_free_channel_private_keys(keys, keys_count);
+ }
+
+ goto out;
+ }
+ }
+
+ /* List command */
+ if (!strcasecmp(cmd->argv[3], "list")) {
+ command = 3;
+
+ if (type == 1) {
+ SilcPrivateMessageKeys keys;
+ unsigned int keys_count;
+ int k, i, len;
+ char buf[1024];
+
+ keys = silc_client_list_private_message_keys(client, conn,
+ &keys_count);
+ if (!keys)
+ goto out;
+
+ /* list the private message key(s) */
+ if (nickname[0] == '*') {
+ silc_say(client, conn, "Private message keys");
+ silc_say(client, conn,
+ " Client Cipher Key");
+ for (k = 0; k < keys_count; k++) {
+ memset(buf, 0, sizeof(buf));
+ strncat(buf, " ", 2);
+ len = strlen(keys[k].client_entry->nickname);
+ strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
+ if (len < 30)
+ for (i = 0; i < 30 - len; i++)
+ strcat(buf, " ");
+ strcat(buf, " ");
+
+ len = strlen(keys[k].cipher);
+ strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
+ if (len < 14)
+ for (i = 0; i < 14 - len; i++)
+ strcat(buf, " ");
+ strcat(buf, " ");
+
+ if (keys[k].key)
+ strcat(buf, "<hidden>");
+ else
+ strcat(buf, "*generated*");
+
+ silc_say(client, conn, "%s", buf);
+ }
+ } else {
+ silc_say(client, conn, "Private message key",
+ client_entry->nickname);
+ silc_say(client, conn,
+ " Client Cipher Key");
+ for (k = 0; k < keys_count; k++) {
+ if (keys[k].client_entry != client_entry)
+ continue;
+
+ memset(buf, 0, sizeof(buf));
+ strncat(buf, " ", 2);
+ len = strlen(keys[k].client_entry->nickname);
+ strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
+ if (len < 30)
+ for (i = 0; i < 30 - len; i++)
+ strcat(buf, " ");
+ strcat(buf, " ");
+
+ len = strlen(keys[k].cipher);
+ strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
+ if (len < 14)
+ for (i = 0; i < 14 - len; i++)
+ strcat(buf, " ");
+ strcat(buf, " ");
+
+ if (keys[k].key)
+ strcat(buf, "<hidden>");
+ else
+ strcat(buf, "*generated*");
+
+ silc_say(client, conn, "%s", buf);
+ }
+ }
+
+ silc_client_free_private_message_keys(keys, keys_count);
+ } else if (type == 2) {
+ SilcChannelPrivateKey *keys;
+ unsigned int keys_count;
+ int k, i, len;
+ char buf[1024];
+
+ keys = silc_client_list_channel_private_keys(client, conn, channel_entry,
+ &keys_count);
+ if (!keys)
+ goto out;
+
+ silc_say(client, conn, "Channel %s private keys",
+ channel_entry->channel_name);
+ silc_say(client, conn,
+ " Cipher Hmac Key");
+ for (k = 0; k < keys_count; k++) {
+ memset(buf, 0, sizeof(buf));
+ strncat(buf, " ", 2);
+
+ len = strlen(keys[k]->cipher->cipher->name);
+ strncat(buf, keys[k]->cipher->cipher->name, len > 16 ? 16 : len);
+ if (len < 16)
+ for (i = 0; i < 16 - len; i++)
+ strcat(buf, " ");
+ strcat(buf, " ");
+
+ len = strlen(keys[k]->hmac->hmac->name);
+ strncat(buf, keys[k]->hmac->hmac->name, len > 16 ? 16 : len);
+ if (len < 16)
+ for (i = 0; i < 16 - len; i++)
+ strcat(buf, " ");
+ strcat(buf, " ");
+
+ strcat(buf, "<hidden>");
+
+ silc_say(client, conn, "%s", buf);
+ }
+
+ silc_client_free_channel_private_keys(keys, keys_count);
+ }
+
+ goto out;
+ }
+
+ /* Send command is used to send key agreement */
+ if (!strcasecmp(cmd->argv[3], "agreement")) {
+ command = 4;
+
+ if (cmd->argc >= 5)
+ hostname = cmd->argv[4];
+ if (cmd->argc >= 6)
+ port = atoi(cmd->argv[5]);
+
+ internal = silc_calloc(1, sizeof(*internal));
+ internal->type = type;
+ }
+
+ /* Start command is used to start key agreement (after receiving the
+ key_agreement client operation). */
+ if (!strcasecmp(cmd->argv[3], "negotiate")) {
+ command = 5;
+
+ if (cmd->argc >= 5)
+ hostname = cmd->argv[4];
+ if (cmd->argc >= 6)
+ port = atoi(cmd->argv[5]);
+
+ internal = silc_calloc(1, sizeof(*internal));
+ internal->type = type;
+ }
+
+ if (command == 0) {
+ silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
+ "set|unset|agreement|negotiate [<arguments>]");
+ goto out;
+ }
+
+ if (command == 4 && client_entry) {
+ silc_say(client, conn, "Sending key agreement to %s", cmd->argv[2]);
+ silc_client_send_key_agreement(client, conn, client_entry, hostname,
+ port, 120, keyagr_completion, internal);
+ goto out;
+ }
+
+ if (command == 5 && client_entry) {
+ silc_say(client, conn, "Starting key agreement with %s", cmd->argv[2]);
+ silc_client_perform_key_agreement(client, conn, client_entry, hostname,
+ port, keyagr_completion, internal);
+ goto out;
+ }
+
+ out:
+ if (nickname)
+ silc_free(nickname);
+ if (server)
+ silc_free(server);
+ silc_client_command_free(cmd);
+}
#define SILC_LOCAL_COMMAND_SERVER 4
#define SILC_LOCAL_COMMAND_MSG 5
#define SILC_LOCAL_COMMAND_AWAY 6
+#define SILC_LOCAL_COMMAND_KEY 7
/* Macros */
SILC_CLIENT_LCMD_FUNC(msg);
SILC_CLIENT_LCMD_FUNC(server);
SILC_CLIENT_LCMD_FUNC(away);
+SILC_CLIENT_LCMD_FUNC(key);
#endif
10.2.1.7:passwd:priikone:1335:1:1:0
[DenyConnection]
-[RedirectClient]
[motd]
./motd
connect: silc_connect,
disconnect: silc_disconnect,
get_auth_method: silc_get_auth_method,
- verify_server_key: silc_verify_server_key,
+ verify_public_key: silc_verify_public_key,
ask_passphrase: silc_ask_passphrase,
failure: silc_failure,
};
}
}
+/* Adds listener socket to the listener sockets table. This function is
+ used to add socket objects that are listeners to the client. This should
+ not be used to add other connection objects. */
+
+void silc_client_add_socket(SilcClient client, SilcSocketConnection sock)
+{
+ int i;
+
+ if (!client->sockets) {
+ client->sockets = silc_calloc(1, sizeof(*client->sockets));
+ client->sockets[0] = sock;
+ client->sockets_count = 1;
+ return;
+ }
+
+ for (i = 0; i < client->sockets_count; i++) {
+ if (client->sockets[i] == NULL) {
+ client->sockets[i] = sock;
+ return;
+ }
+ }
+
+ client->sockets = silc_realloc(client->sockets, sizeof(*client->sockets) *
+ (client->sockets_count + 1));
+ client->sockets[client->sockets_count] = sock;
+ client->sockets_count++;
+}
+
+/* Deletes listener socket from the listener sockets table. */
+
+void silc_client_del_socket(SilcClient client, SilcSocketConnection sock)
+{
+ int i;
+
+ if (!client->sockets)
+ return;
+
+ for (i = 0; i < client->sockets_count; i++) {
+ if (client->sockets[i] == sock) {
+ client->sockets[i] = NULL;
+ return;
+ }
+ }
+}
+
static int
silc_client_connect_to_server_internal(SilcClientInternalConnectContext *ctx)
{
/* Process the packet. This will call the parser that will then
decrypt and parse the packet. */
- silc_packet_receive_process(sock, conn->receive_key, conn->hmac,
- silc_client_packet_parse, client);
+ if (sock->type != SILC_SOCKET_TYPE_UNKNOWN)
+ silc_packet_receive_process(sock, conn->receive_key, conn->hmac,
+ silc_client_packet_parse, client);
+ else
+ silc_packet_receive_process(sock, NULL, NULL,
+ silc_client_packet_parse, client);
}
}
SILC_LOG_DEBUG(("Start"));
/* Decrypt the received packet */
- ret = silc_packet_decrypt(conn->receive_key, conn->hmac, buffer, packet,
- silc_client_packet_decrypt_check, parse_ctx);
+ if (sock->type != SILC_SOCKET_TYPE_UNKNOWN)
+ ret = silc_packet_decrypt(conn->receive_key, conn->hmac, buffer, packet,
+ silc_client_packet_decrypt_check, parse_ctx);
+ else
+ ret = silc_packet_decrypt(NULL, NULL, buffer, packet,
+ silc_client_packet_decrypt_check, parse_ctx);
+
if (ret < 0)
goto out;
break;
case SILC_PACKET_KEY_EXCHANGE:
- if (sock->protocol) {
+ if (sock->protocol && sock->protocol->protocol->type
+ == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
SilcClientKEInternalContext *proto_ctx =
(SilcClientKEInternalContext *)sock->protocol->context;
break;
case SILC_PACKET_KEY_EXCHANGE_1:
- if (sock->protocol) {
+ if (sock->protocol && sock->protocol->protocol->type
+ == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
+ SilcClientKEInternalContext *proto_ctx =
+ (SilcClientKEInternalContext *)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(client->timeout_queue, 0,
+ sock->protocol, sock->sock, 0, 0);
} else {
SILC_LOG_ERROR(("Received Key Exchange 1 packet but no key exchange "
"protocol active, packet dropped."));
}
break;
case SILC_PACKET_KEY_EXCHANGE_2:
- if (sock->protocol) {
+ if (sock->protocol && sock->protocol->protocol->type
+ == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
SilcClientKEInternalContext *proto_ctx =
(SilcClientKEInternalContext *)sock->protocol->context;
/* Set the packet context pointers */
packetdata.flags = 0;
packetdata.type = type;
- if (((SilcClientConnection)sock->user_data)->local_id_data)
+ if (sock->user_data &&
+ ((SilcClientConnection)sock->user_data)->local_id_data)
packetdata.src_id = ((SilcClientConnection)sock->user_data)->local_id_data;
else
packetdata.src_id = silc_calloc(SILC_ID_CLIENT_LEN, sizeof(unsigned char));
SilcClientConnection *conns;
unsigned int conns_count;
+ /* Table of listenning sockets in client. Client can have listeners
+ (like key agreement protocol server) and those sockets are saved here.
+ This table is checked always if the connection object cannot be found
+ from the `conns' table. */
+ SilcSocketConnection *sockets;
+ unsigned int sockets_count;
+
/* Generic cipher and hash objects. These can be used and referenced
by the application as well. */
SilcCipher none_cipher;
int __i; \
\
for (__i = 0; __i < (__x)->conns_count; __i++) \
- if ((__x)->conns[__i]->sock->sock == (__fd)) \
+ if ((__x)->conns[__i] && \
+ (__x)->conns[__i]->sock->sock == (__fd)) \
break; \
\
- if (__i >= (__x)->conns_count) \
+ if (__i >= (__x)->conns_count) { \
(__sock) = NULL; \
- (__sock) = (__x)->conns[__i]->sock; \
+ for (__i = 0; __i < (__x)->sockets_count; __i++) \
+ if ((__x)->sockets[__i] && \
+ (__x)->sockets[__i]->sock == (__fd)) \
+ (__sock) = (__x)->sockets[__i]; \
+ } else \
+ (__sock) = (__x)->conns[__i]->sock; \
} while(0)
/* Prototypes (some of the prototypes are defined in the silcapi.h) */
ske->sock->user_data = tmp;
}
+/* Timeout callback that is called to close the connection and free the
+ socket connection data. */
+
+SILC_TASK_CALLBACK(silc_client_key_agreement_close)
+{
+ SilcClientKeyAgreement ke = (SilcClientKeyAgreement)context;
+
+ silc_schedule_unset_listen_fd(ke->sock->sock);
+ silc_schedule_unset_listen_fd(ke->fd);
+ silc_net_close_connection(ke->sock->sock);
+ silc_net_close_connection(ke->fd);
+ silc_socket_free(ke->sock);
+ silc_free(ke);
+}
+
/* This callback is called after the key agreement protocol has been
performed. This calls the final completion callback for the application. */
protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
/* Error occured during protocol */
ke->client_entry->ke = NULL;
- ke->completion(ke->client, ke->conn, ke->client_entry, NULL, ke->context);
+ ke->completion(ke->client, ke->conn, ke->client_entry,
+ SILC_KEY_AGREEMENT_ERROR, NULL, ke->context);
silc_ske_free_key_material(ctx->keymat);
goto out;
}
/* Pass the negotiated key material to the application. The application
is responsible of freeing the key material. */
ke->client_entry->ke = NULL;
- ke->completion(ke->client, ke->conn, ke->client_entry, ctx->keymat,
- ke->context);
+ ke->completion(ke->client, ke->conn, ke->client_entry,
+ SILC_KEY_AGREEMENT_OK, ctx->keymat, ke->context);
out:
silc_protocol_free(protocol);
silc_task_unregister_by_callback(client->timeout_queue,
silc_client_failure_callback);
silc_task_unregister_by_fd(client->io_queue, ke->fd);
+ silc_schedule_unset_listen_fd(ke->fd);
+ silc_net_close_connection(ke->fd);
if (ke->timeout)
silc_task_unregister(client->timeout_queue, ke->timeout);
- silc_socket_free(ke->sock);
- silc_free(ke);
+ silc_client_del_socket(ke->client, ke->sock);
+
+ silc_task_register(client->timeout_queue, 0,
+ silc_client_key_agreement_close,
+ (void *)ke, 0, 1,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+
silc_free(ctx);
}
"Could not accept key agreement connection: ",
strerror(errno));
ke->client_entry->ke = NULL;
- ke->completion(ke->client, ke->conn, ke->client_entry, NULL, ke->context);
+ ke->completion(ke->client, ke->conn, ke->client_entry,
+ SILC_KEY_AGREEMENT_ERROR, NULL, ke->context);
silc_task_unregister_by_fd(client->io_queue, ke->fd);
+ silc_schedule_unset_listen_fd(ke->fd);
+ silc_net_close_connection(ke->fd);
if (ke->timeout)
silc_task_unregister(client->timeout_queue, ke->timeout);
silc_free(ke);
client->ops->say(client, conn,
"Could not resolve the remote IP or hostname");
ke->client_entry->ke = NULL;
- ke->completion(ke->client, ke->conn, ke->client_entry, NULL, ke->context);
+ ke->completion(ke->client, ke->conn, ke->client_entry,
+ SILC_KEY_AGREEMENT_ERROR, NULL, ke->context);
silc_task_unregister_by_fd(client->io_queue, ke->fd);
+ silc_schedule_unset_listen_fd(ke->fd);
+ silc_net_close_connection(ke->fd);
if (ke->timeout)
silc_task_unregister(client->timeout_queue, ke->timeout);
silc_free(ke);
if (!newsocket->hostname)
newsocket->hostname = strdup(newsocket->ip);
newsocket->port = silc_net_get_remote_port(sock);
+ silc_client_add_socket(client, newsocket);
/* Allocate internal context for key exchange protocol. This is
sent as context for the protocol. */
However, this doesn't set the scheduler for outgoing traffic, it
will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
later when outgoing data is available. */
+ context = (void *)client;
SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
}
SilcClientKeyAgreement ke = (SilcClientKeyAgreement)context;
ke->client_entry->ke = NULL;
- ke->completion(ke->client, ke->conn, ke->client_entry, NULL, ke->context);
+ ke->completion(ke->client, ke->conn, ke->client_entry,
+ SILC_KEY_AGREEMENT_TIMEOUT, NULL, ke->context);
- if (ke->sock)
+ if (ke->sock) {
+ silc_client_del_socket(ke->client, ke->sock);
silc_socket_free(ke->sock);
+ }
ke->client_entry->ke = NULL;
- silc_free(ke);
silc_task_unregister_by_callback(ke->client->timeout_queue,
silc_client_failure_callback);
- silc_task_unregister_by_fd(ke->client->io_queue, ke->fd);
+ if (ke->fd)
+ silc_task_unregister_by_fd(ke->client->io_queue, ke->fd);
+ silc_schedule_unset_listen_fd(ke->fd);
+ silc_net_close_connection(ke->fd);
+ silc_free(ke);
}
/* Sends key agreement request to the remote client indicated by the
void *context)
{
SilcSocketConnection sock = conn->sock;
- SilcClientKeyAgreement ke;
+ SilcClientKeyAgreement ke = NULL;
SilcBuffer buffer;
assert(client_entry);
client->ops->say(client, conn,
"Cannot create listener on %s on port %d: %s",
hostname, port, strerror(errno));
- completion(client, conn, client_entry, NULL, context);
+ completion(client, conn, client_entry, SILC_KEY_AGREEMENT_FAILURE,
+ NULL, context);
silc_free(ke);
return;
}
/* Register a timeout task that will be executed if the connector
will not start the key exchange protocol within the specified
timeout. */
- ke->timeout =
- silc_task_register(client->timeout_queue, 0,
- silc_client_key_agreement_timeout,
- (void *)ke, timeout_secs, 0,
- SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+ ke->timeout = silc_task_register(client->timeout_queue, 0,
+ silc_client_key_agreement_timeout,
+ (void *)ke, timeout_secs, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
}
/* Encode the key agreement payload */
- buffer = silc_key_agreement_payload_encode(hostname, port);
+ buffer = silc_key_agreement_payload_encode(hostname,
+ !ke ? port :
+ silc_net_get_local_port(ke->fd));
/* Send the key agreement packet to the client */
silc_client_packet_send(client, sock, SILC_PACKET_KEY_AGREEMENT,
silc_schedule_unset_listen_fd(fd);
silc_net_close_connection(fd);
silc_task_unregister(client->io_queue, ctx->task);
+ silc_free(ctx->host);
silc_free(ctx);
/* Call the completion callback */
ke->completion(ke->client, ke->conn, ke->client_entry,
- NULL, ke->context);
+ SILC_KEY_AGREEMENT_FAILURE, NULL, ke->context);
silc_free(ke);
}
return;
silc_schedule_unset_listen_fd(fd);
silc_task_unregister(client->io_queue, ctx->task);
- silc_free(ctx);
ke->fd = fd;
/* Now actually perform the key agreement protocol */
silc_client_perform_key_agreement_fd(ke->client, ke->conn,
- ke->client_entry, ke->fd,
+ ke->client_entry, ke->fd, ctx->host,
ke->completion, ke->context);
silc_free(ke);
+ silc_free(ctx->host);
+ silc_free(ctx);
}
/* Performs the actual key agreement protocol. Application may use this
{
SilcClientKeyAgreement ke;
+ SILC_LOG_DEBUG(("Start"));
+
assert(client_entry && hostname && port);
ke = silc_calloc(1, sizeof(*ke));
/* Connect to the remote client */
ke->fd = silc_client_connect_to_client(client, conn, port, hostname, ke);
if (ke->fd < 0) {
- completion(client, conn, client_entry, NULL, context);
+ completion(client, conn, client_entry, SILC_KEY_AGREEMENT_FAILURE,
+ NULL, context);
silc_free(ke);
return;
}
SilcClientConnection conn,
SilcClientEntry client_entry,
int sock,
+ char *hostname,
SilcKeyAgreementCallback completion,
void *context)
{
SilcClientKEInternalContext *proto_ctx;
SilcProtocol protocol;
+ SILC_LOG_DEBUG(("Start"));
+
assert(client_entry);
ke = silc_calloc(1, sizeof(*ke));
/* Allocate new socket connection object */
silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, (void *)conn, &ke->sock);
+ silc_client_add_socket(client, ke->sock);
+ ke->sock->hostname = strdup(hostname);
+ ke->sock->port = silc_net_get_remote_port(sock);
/* Allocate internal context for key exchange protocol. This is
sent as context for the protocol. */
assert(client_entry);
if (client_entry->ke) {
- if (client_entry->ke->sock)
+ if (client_entry->ke->sock) {
+ silc_client_del_socket(client_entry->ke->client, client_entry->ke->sock);
silc_socket_free(client_entry->ke->sock);
+ }
client_entry->ke = NULL;
silc_task_unregister_by_fd(client->io_queue, client_entry->ke->fd);
if (client_entry->ke->timeout)
return FALSE;
/* Generate key if not provided */
- if (!key && generate_key == TRUE) {
+ if (generate_key == TRUE) {
len = 32;
for (i = 0; i < len; i++) private_key[i] = silc_rng_get_byte(client->rng);
key = private_key;
/* Add pending callback */
silc_client_command_pending(conn, SILC_COMMAND_WHOIS,
- ++conn->cmd_ident,
+ conn->cmd_ident,
silc_client_get_client_by_id_destructor,
silc_client_command_get_client_by_id_callback,
(void *)i);
SILC_LOG_DEBUG(("Start"));
- /* Verify server key from user. */
- if (!client->ops->verify_server_key(client, ctx->sock->user_data,
+ /* Verify public key from user. */
+ if (!client->ops->verify_public_key(client, ctx->sock->user_data,
+ ctx->sock->type,
pk_data, pk_len, pk_type))
return SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
/* General definitions */
+/* Key agreement status types indicating the status of the protocol. */
+typedef enum {
+ SILC_KEY_AGREEMENT_OK, /* Everything is Ok */
+ SILC_KEY_AGREEMENT_ERROR, /* Unknown error occured */
+ SILC_KEY_AGREEMENT_FAILURE, /* The protocol failed */
+ SILC_KEY_AGREEMENT_TIMEOUT, /* The protocol timeout */
+} SilcKeyAgreementStatus;
+
/* Key agreement callback that is called after the key agreement protocol
has been performed. This is called also if error occured during the
key agreement protocol. The `key' is the allocated key material and
typedef void (*SilcKeyAgreementCallback)(SilcClient client,
SilcClientConnection conn,
SilcClientEntry client_entry,
+ SilcKeyAgreementStatus status,
SilcSKEKeyMaterial *key,
void *context);
unsigned char **auth_data,
unsigned int *auth_data_len);
- /* Verifies received public key. The public key has been received from
- a server. If user decides to trust the key may be saved as trusted
- server key for later use. If user does not trust the key this returns
- FALSE. If everything is Ok this returns TRUE. */
- int (*verify_server_key)(SilcClient client, SilcClientConnection conn,
- unsigned char *pk, unsigned int pk_len,
- SilcSKEPKType pk_type);
+ /* Verifies received public key. The `conn_type' indicates which entity
+ (server, client etc.) has sent the public key. If user decides to trust
+ the key may be saved as trusted public key for later use. If user does
+ not trust the key this returns FALSE. If everything is Ok this returns
+ TRUE. */
+ int (*verify_public_key)(SilcClient client, SilcClientConnection conn,
+ SilcSocketType conn_type, unsigned char *pk,
+ unsigned int pk_len, SilcSKEPKType pk_type);
/* Ask (interact, that is) a passphrase from user. Returns the passphrase
or NULL on error. */
/* Removes connection from client. Frees all memory. */
void silc_client_del_connection(SilcClient client, SilcClientConnection conn);
+/* Adds listener socket to the listener sockets table. This function is
+ used to add socket objects that are listeners to the client. This should
+ not be used to add other connection objects. */
+void silc_client_add_socket(SilcClient client, SilcSocketConnection sock);
+
+/* Deletes listener socket from the listener sockets table. */
+void silc_client_del_socket(SilcClient client, SilcSocketConnection sock);
+
/* Start SILC Key Exchange (SKE) protocol to negotiate shared secret
key material between client and server. This function can be called
directly if application is performing its own connecting and does not
SilcClientConnection conn,
SilcClientEntry client_entry,
int sock,
+ char *hostname,
SilcKeyAgreementCallback completion,
void *context);
SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
return -2;
}
- SILC_LOG_ERROR(("Cannot read from socket: %d", strerror(errno)));
+ SILC_LOG_ERROR(("Cannot read from socket: %d:%s", sock, strerror(errno)));
return -1;
}
return ntohs(remote.sin_port);
}
+/* Return local port by socket. */
+
+unsigned short silc_net_get_local_port(int sock)
+{
+ struct sockaddr_in local;
+ int len;
+
+ memset(&local, 0, sizeof(local));
+ len = sizeof(local);
+ if (getsockname(sock, (struct sockaddr *)&local, &len) < 0)
+ return 0;
+
+ return ntohs(local.sin_port);
+}
+
/* Return name of localhost. */
char *silc_net_localhost()
int silc_net_is_ip(const char *addr);
void silc_net_check_host_by_sock(int sock, char **hostname, char **ip);
unsigned short silc_net_get_remote_port(int sock);
+unsigned short silc_net_get_local_port(int sock);
char *silc_net_localhost();
#endif