+Thu Mar 8 21:39:03 EET 2001 Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+ * Added assert()s to buffer formatting and unformatting routines
+ to assert (if --enable-debug) when error occurs. Affected
+ file: lib/silcutil/silcbuffmt.c.
+
+ * Changed to auto-reconnect to check whether the remote host is
+ router and register the re-connect timeout if it is. It used
+ to check that whether we are normal server, but router must do
+ auto-reconnect with another router as well. Affected file
+ silcd/server.c.
+
+ * Removed the [<key len>] option from CMODE command as the cipher
+ name decides the key length, nowadays. See the defined ciphers
+ from the protocol specification.
+
+ * Added [<hmac>] option to the CMODE command to define the HMAC
+ for the channel. Added SILC_CMODE_HMAC channel mode.
+
+ * Added [<hmac>] option for the JOIN command so that user can
+ select which HMAC is used to compute the MACs of the channel
+ messages.
+
+ * Added Hmac field to the Channel Message Payload. The integrity
+ of plaintext channel messages are now protected by computing
+ MAC of the message and attaching the MAC to the payload. The
+ MAC is not encrypted. Now, it is clear that this causes some
+ overhead to the size of the packet but rationale for this is that
+ now the receiver can verify whether the channel message decrypted
+ correctly and also when private keys are set for the channel the
+ receiver can decrypt the packet with several keys and check from
+ the MAC which key decrypted the message correctly.
+
+ * Added silc_cipher_encrypt and silc_cipher_decrypt into the
+ lib/silccrypt/silccipher.[ch].
+
+ * Added silc_hash_len to return the digest length into the
+ lib/silcrypt/silchash.[ch].
+
+ * Rewrote parts of Silc Channel Payload interface in the
+ lib/silccore/silcchannel.[ch]. The encode function now also
+ encrypts the packet and parse function decrypts it.
+
+ * Channel message delivery between routers was broken after the
+ channel key distribution was fixed earlier. The channel key
+ was used be to distributed to other routers as well which is not
+ allowed by the protocol. Now this is fixed and channel keys
+ really are cell specific and the channel message delivery between
+ routers comply with the protocol specification.
+
+Wed Mar 7 20:58:50 EET 2001 Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+ * Fixed a minor formatting bug in the SKE's key material processing.
+ It actually might have processed the keys wrong way resulting
+ into wrong keys.
+
+ * Redefined the mandatory HMAC algorithms and added new algorithms.
+ Added hmac-sha1-96 and hmac-md5-96 which are normal hmac-sha1
+ and hmac-md5 truncated to 96 bits. The mandatory is now
+ hmac-sha1-96. Rest are optional (including the one that used
+ to be mandatory). Rationale for this is that the truncated HMAC
+ length is sufficient from security point of view and can actually
+ make the attack against the HMAC harder. Also, the truncated
+ HMAC causes less overhead to the packets. See the RFC2104 for
+ more information.
+
+ * Added new [hmac] configuration section. The SKE used to use
+ the hash names (md5 and sha1) in the SKE proposal as HMCAS which
+ is of course wrong. The official names that must be proposed in
+ the SKE are the ones defined in the protocol specification
+ (hmac-sha1-96 for example). The user can configure any hmac
+ using any hash function configured in the [hash] section. At
+ least, the mandatory must be configured.
+
+ Rewrote the HMAC interface in lib/silccrypt/silchmac.[ch].
+
+ * Added HMAC list to the SKE proposal list. It has now both
+ hash algorithm list and HMAC list. This makes the protocol
+ incompatible with previous versions. The SKE now seems to work
+ the way it is supposed to work, for the first time actually.
+
+ * Defined plain Hash algorithms to the protocol specification.
+ Added sha1 and md5.
+
+Tue Mar 6 15:36:11 EET 2001 Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+ * Implemented support for key agreement packets into the server.
+ Added functions silc_server_key_agreement and
+ silc_server_send_key_agreement. Other than these functions,
+ server has nothing to do with this packet.
+
+ * Added support for private message key packets into the server.
+ Added functions silc_server_private_message_key and
+ silc_server_send_private_message_key.
+
+ * Updated TODO.
+
+ * Changed the silc_[client|server]_protocol_ke_set_keys to be
+ called in the protocol's final callback instead in the END
+ protocol state. This makes a little more sense and in the same
+ time in client we can use the same protocol routines for normal
+ key exchange and to key agreement packet handling as well.
+
+ * Added to both client's and server's KE protocol context the
+ SilcSKEKeyMaterial pointer to save the key material. We will
+ bring the key material to the protocol's final callback by doing
+ this. The final callback must free the key material.
+
+ * Added SKE's packet_send callback into client's KE protocol
+ context so that the caller can choose what packet sending function
+ is used. This way we can use different packet sending when
+ doing normal SKE when doing key agreement packet handling (in
+ the key agreement packet handling we do not want to encrypt
+ the packets).
+
+ * Implemented the responder side of the key agreement routines
+ in the client. The client can now bind to specified port and
+ accept incoming key negotiation. The key material is passed
+ to the application after the protocol is over.
+
+ * Implemented the processing of incoming Key Agreement packet
+ in the client. Added function silc_client_key_agreement to
+ process the packet.
+
+ * Implemented the intiator side of the key agreement routines
+ in the client. The client can now initiate key agreement with
+ another remote client. The key material is passed to the
+ application after the protocol is over.
+
+ * Created client_keyagr.c to include all the key agreement
+ routines.
+
+ * Added macro SILC_TASK_CALLBACK_GLOBAL which is equal to the
+ SILC_TASK_CALLBACK except that it is not static.
+
+ * Created client_notify.c and moved the Notify packet handling
+ from the client.[ch] into that file.
+
+ * Created client_prvmsg.c and moved all private message and
+ private message key routines from the client.[ch] into that file.
+
+ * Create client_channel.c and moved all channel message and
+ channel private key routines from the client.[ch] into that file.
+
+ * Changed silc_client_get_client_by_id_resolve to resolve with
+ WHOIS command instead of IDENTIFY command, in the file
+ lib/silclient/idlist.c.
+
+Mon Mar 5 18:39:49 EET 2001 Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+ * Implemented the SKE's responder side to the Client library.
+
+ * When FAILURE is received to the protocol do not trust it
+ blindly. Register a timeout to wait whether the remote closes
+ the connection as it should do it, only after that process the
+ actual failure. This was changed to both client and server.
+
+ * Added client_internal.h to include some of the structures
+ there instead of client.h in lib/silcclient/.
+
+ * Added function silc_task_unregister_by_callback to unregister
+ timeouts by the callback function.
+
Sat Mar 3 19:15:43 EET 2001 Pekka Riikonen <priikone@poseidon.pspt.fi>
* Some "Incomplete WHOIS info" errors has been appearing on the
message sending. I guess the logic is done in server side but is
missing from client.
- o Private message key setting is missing and must be implemented.
- Currently private messages are encrypted with session keys. This
- is required by the protocol.
-
- o Channel private key setting is missing and must be implemented.
- Currently there cannot be private keys for channels. Normal channel
- keys (generated by server) are used. This is required by the protocol.
-
o I guess, public key authentication (when connecting to a server)
is not working currently. It is just matter of loading the keys
from file and using them (see corresponding code in server, it should
o Connection Authentication request resolving is missing and must be
done. This is required by the protocol.
- o Move ssh_client_notify_by_server to its own file (like notify.[ch]).
-
- o Key Exchange protocol's responder side is missing from client.
- Generally it is possible for the client to be responder so it should
- be implemented (See corresponding code from server). Error handling
- in the KE protocol is also in pretty bad shape in client.
-
TODO In SILC Server
===================
+ o Packet processing can be made faster. All packet function in the
+ packet_receive.c has same prototypes. Instead of calling those from
+ huge switch() make a table of callback functions that can be called
+ directly by the packet type.
+
+ o silc_server_send_key_agreement and silc_server_send_private_message_key
+ are one and same function (also silc_server_send_private_message is
+ almost same function). These should be unified to one generic named
+ function and use that.
+
o DNS/IP lookup blocks the server. This must be fixed. Check the
resolver stuff (resolver(3), resolver(5)). Either we have to do the
own resolver stuff (through scheduler, if possible without writing
not in distribution), but it is not used yet, and it requires some
tweaking on the Makefiles (we want static lib not shared).
- o Cipher API needs to be made more consistent. Some parts of the
- code generated with current Cipher API looks really bad. Same
- is with PKCS API, even worse actually. They need to be made
- cleaner. Introducing silc_cipher_encrypt/decrypt/set_key etc.
- functions (I actually don't understand why have I left these un-done).
-
o SIM support for SILC PKCS API needs to made so that they could be
used as SIM's. At the same time some work is required on prime
generation as the way it is done now sucks. Read from code for
}
+/* Asks whether the user would like to perform the key agreement protocol.
+ This is called after we have received an key agreement packet or an
+ reply to our key agreement packet. This returns TRUE if the user wants
+ the library to perform the key agreement protocol and FALSE if it is not
+ desired (application may start it later by calling the function
+ silc_client_perform_key_agreement). */
+
+int silc_key_agreement(SilcClient client, SilcClientConnection conn,
+ SilcClientEntry client_entry, char *hostname,
+ int port,
+ SilcKeyAgreementCallback *completion,
+ void **context)
+{
+
+ return FALSE;
+}
+
/* SILC client operations */
SilcClientOperations ops = {
say: silc_say,
verify_server_key: silc_verify_server_key,
ask_passphrase: silc_ask_passphrase,
failure: silc_failure,
+ key_agreement: silc_key_agreement,
};
unsigned int *auth_data_len);
void silc_failure(SilcClient client, SilcClientConnection conn,
SilcProtocol protocol, void *failure);
-
+int silc_key_agreement(SilcClient client, SilcClientConnection conn,
+ SilcClientEntry client_entry, char *hostname,
+ int port,
+ SilcKeyAgreementCallback *completion,
+ void **context);
#endif
SILC_CLIENT_CONFIG_SECTION_TYPE_PKCS, 2 },
{ "[hash]",
SILC_CLIENT_CONFIG_SECTION_TYPE_HASH_FUNCTION, 4 },
+ { "[hmac]",
+ SILC_CLIENT_CONFIG_SECTION_TYPE_HMAC, 3 },
{ "[connection]",
SILC_CLIENT_CONFIG_SECTION_TYPE_CONNECTION, 4 },
{ "[commands]",
check = TRUE;
break;
+ case SILC_CLIENT_CONFIG_SECTION_TYPE_HMAC:
+
+ if (!config->hmac) {
+ config->hmac = silc_calloc(1, sizeof(*config->hmac));
+ config->hmac->next = NULL;
+ config->hmac->prev = NULL;
+ } else {
+ if (!config->hmac->next) {
+ config->hmac->next =
+ silc_calloc(1, sizeof(*config->hmac->next));
+ config->hmac->next->next = NULL;
+ config->hmac->next->prev = config->hmac;
+ config->hmac = config->hmac->next;
+ }
+ }
+
+ /* Get HMAC name */
+ ret = silc_config_get_token(line, &config->hmac->alg_name);
+ if (ret < 0)
+ break;
+ if (ret == 0) {
+ fprintf(stderr, "%s:%d: HMAC name not defined\n",
+ config->filename, pc->linenum);
+ break;
+ }
+
+ /* Get Hash function name */
+ ret = silc_config_get_token(line, &config->hmac->sim_name);
+ if (ret < 0)
+ break;
+ if (ret == 0) {
+ fprintf(stderr, "%s:%d: Hash function name not defined\n",
+ config->filename, pc->linenum);
+ break;
+ }
+
+ /* Get MAC length */
+ ret = silc_config_get_token(line, &tmp);
+ if (ret < 0)
+ break;
+ if (ret == 0) {
+ fprintf(stderr, "%s:%d: HMAC's MAC length not defined\n",
+ config->filename, pc->linenum);
+ break;
+ }
+ config->hmac->key_len = atoi(tmp);
+ silc_free(tmp);
+
+ check = TRUE;
+ break;
+
case SILC_CLIENT_CONFIG_SECTION_TYPE_CONNECTION:
if (!config->conns) {
config->pkcs = config->pkcs->prev;
while (config->hash_func && config->hash_func->prev)
config->hash_func = config->hash_func->prev;
+ while (config->hmac && config->hmac->prev)
+ config->hmac = config->hmac->prev;
while (config->conns && config->conns->prev)
config->conns = config->conns->prev;
while (config->commands && config->commands->prev)
}
}
-/* Registers configured hash functions. These can then be allocated by the
+/* Registers configured hash funtions. These can then be allocated by the
client when needed. */
void silc_client_config_register_hashfuncs(SilcClientConfig config)
alg = config->hash_func;
while(alg) {
-
if (!alg->sim_name) {
- /* Hash module is supposed to be built in. Nothing to be done
- here except to test that the hash function really is built in. */
- SilcHash tmp = NULL;
-
- if (silc_hash_alloc(alg->alg_name, &tmp) == FALSE) {
- SILC_LOG_ERROR(("Unsupported hash function `%s'", alg->alg_name));
+ if (!silc_hash_is_supported(alg->alg_name)) {
+ SILC_LOG_ERROR(("Unsupported hash function `%s'",
+ alg->alg_name));
silc_client_stop(client);
exit(1);
}
- silc_free(tmp);
-
-#ifdef SILC_SIM
- } else {
- /* Load (try at least) the hash SIM module */
- SilcHashObject hash;
- SilcSimContext *sim;
+ }
+ alg = alg->next;
+ }
+}
- memset(&hash, 0, sizeof(hash));
- hash.name = alg->alg_name;
- hash.block_len = alg->block_len;
- hash.hash_len = alg->key_len;
+/* Registers configured HMACs. These can then be allocated by the
+ client when needed. */
- sim = silc_sim_alloc();
- sim->type = SILC_SIM_HASH;
- sim->libname = alg->sim_name;
+void silc_client_config_register_hmacs(SilcClientConfig config)
+{
+ SilcClientConfigSectionAlg *alg;
+ SilcClientInternal app = (SilcClientInternal)config->client;
+ SilcClient client = app->client;
- if ((silc_sim_load(sim))) {
- hash.init =
- silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
- SILC_HASH_SIM_INIT));
- SILC_LOG_DEBUG(("init=%p", hash.init));
- hash.update =
- silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
- SILC_HASH_SIM_UPDATE));
- SILC_LOG_DEBUG(("update=%p", hash.update));
- hash.final =
- silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
- SILC_HASH_SIM_FINAL));
- SILC_LOG_DEBUG(("final=%p", hash.final));
- hash.context_len =
- silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
- SILC_HASH_SIM_CONTEXT_LEN));
- SILC_LOG_DEBUG(("context_len=%p", hash.context_len));
+ SILC_LOG_DEBUG(("Registering configured HMACs"));
- /* Put the SIM to the table of all SIM's in client */
- app->sim = silc_realloc(app->sim,
- sizeof(*app->sim) *
- (app->sim_count + 1));
- app->sim[app->sim_count] = sim;
- app->sim_count++;
- } else {
- SILC_LOG_ERROR(("Error configuring hash functions"));
- silc_client_stop(client);
- exit(1);
- }
+ if (!config->hmac) {
+ SILC_LOG_ERROR(("HMACs are not configured. SILC cannot work without "
+ "HMACs"));
+ silc_client_stop(client);
+ exit(1);
+ }
- /* Register the cipher */
- silc_hash_register(&hash);
-#endif
+ alg = config->hmac;
+ while(alg) {
+ SilcHmacObject hmac;
+
+ if (!silc_hash_is_supported(alg->sim_name)) {
+ SILC_LOG_ERROR(("Unsupported hash function `%s'",
+ alg->sim_name));
+ silc_client_stop(client);
+ exit(1);
}
+
+ /* Register the HMAC */
+ memset(&hmac, 0, sizeof(hmac));
+ hmac.name = alg->alg_name;
+ hmac.len = alg->key_len;
+ silc_hmac_register(&hmac);
alg = alg->next;
}
}
-
SilcClientConfigSectionConnection *
silc_client_config_find_connection(SilcClientConfig config,
char *host, int port)
SilcClientConfigSectionAlg *cipher;
SilcClientConfigSectionAlg *pkcs;
SilcClientConfigSectionAlg *hash_func;
+ SilcClientConfigSectionAlg *hmac;
SilcClientConfigSectionConnection *conns;
SilcClientConfigSectionCommand *commands;
} SilcClientConfigObject;
SILC_CLIENT_CONFIG_SECTION_TYPE_CIPHER,
SILC_CLIENT_CONFIG_SECTION_TYPE_PKCS,
SILC_CLIENT_CONFIG_SECTION_TYPE_HASH_FUNCTION,
+ SILC_CLIENT_CONFIG_SECTION_TYPE_HMAC,
SILC_CLIENT_CONFIG_SECTION_TYPE_CONNECTION,
SILC_CLIENT_CONFIG_SECTION_TYPE_COMMAND = 253, /* Special section */
} SilcClientConfigSectionType;
void silc_client_config_register_ciphers(SilcClientConfig config);
void silc_client_config_register_pkcs(SilcClientConfig config);
void silc_client_config_register_hashfuncs(SilcClientConfig config);
+void silc_client_config_register_hmacs(SilcClientConfig config);
SilcClientConfigSectionConnection *
silc_client_config_find_connection(SilcClientConfig config,
char *host, int port);
/* $Id$ */
#include "clientincludes.h"
+#include "client_internal.h"
/* Local commands. */
SilcClientCommand silc_local_command_list[] =
silc_client_config_register_ciphers(app->config);
silc_client_config_register_pkcs(app->config);
silc_client_config_register_hashfuncs(app->config);
+ silc_client_config_register_hmacs(app->config);
/* Load public and private key */
if (silc_client_load_keys(silc) == FALSE)
md5::64:16
sha1::64:20
+#
+# Configured HMAC functions. The hash function used in the HMAC must
+# configured to the [hash] section.
+#
+# Format: <name>:<hash name>:<mac length>
+#
+[hmac]
+hmac-sha1-96:sha1:12
+hmac-md5-96:md5:12
+hmac-sha1:sha1:20
+hmac-md5:md5:16
+
#
# Configured PKCS.
#
md5::64:16
sha1::64:20
+[hmac]
+hmac-sha1-96:sha1:12
+hmac-md5-96:md5:12
+hmac-sha1:sha1:20
+hmac-md5:md5:16
+
#[pkcs]
#rsa::1024
#dss::1024
4, mode, 4,
5, tmp2, 4,
6, keyp->data, keyp->len,
- 8, channel->topic,
+ 9, channel->topic,
strlen(channel->topic));
}
SilcServerCommandContext cmd = (SilcServerCommandContext)context;
SilcServer server = cmd->server;
int tmp_len;
- char *tmp, *channel_name = NULL, *cipher = NULL;
+ char *tmp, *channel_name = NULL, *cipher, *hmac;
SilcChannelEntry channel;
unsigned int umode = 0;
int created = FALSE;
goto out;
}
- /* Get cipher name */
+ /* Get cipher and hmac name */
cipher = silc_argument_get_arg_type(cmd->args, 4, NULL);
+ hmac = silc_argument_get_arg_type(cmd->args, 5, NULL);
/* See if the channel exists */
channel = silc_idlist_find_channel_by_name(server->local_list,
the channel by ourselves. */
if (server->standalone) {
channel = silc_server_create_new_channel(server, server->id, cipher,
- channel_name, TRUE);
+ hmac, channel_name, TRUE);
umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
created = TRUE;
if (!channel) {
/* Channel really does not exist, create it */
channel = silc_server_create_new_channel(server, server->id, cipher,
- channel_name, TRUE);
+ hmac, channel_name, TRUE);
umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
created = TRUE;
}
if (!channel) {
/* Channel really does not exist, create it */
channel = silc_server_create_new_channel(server, server->id, cipher,
- channel_name, TRUE);
+ hmac, channel_name, TRUE);
umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
created = TRUE;
}
if (mode_mask & SILC_CHANNEL_MODE_CIPHER) {
if (!(channel->mode & SILC_CHANNEL_MODE_CIPHER)) {
/* Cipher to use protect the traffic */
- unsigned int key_len = 256;
- char *cp;
+ unsigned int key_len;
/* Get cipher */
tmp = silc_argument_get_arg_type(cmd->args, 8, NULL);
if (!tmp) {
silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
goto out;
}
- cp = strchr(tmp, ':');
- if (cp) {
- key_len = atoi(cp);
- *cp = '\0';
- }
-
/* XXX Duplicated code, make own function for this!! */
/* Delete old cipher and allocate the new one */
SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
goto out;
}
-
- key_len /= 8;
- if (key_len > 32)
- key_len = 32;
+ key_len = silc_cipher_get_key_len(channel->channel_key) / 8;
/* Re-generate channel key */
silc_server_create_channel_key(server, channel, key_len);
SilcChannelEntry entry;
unsigned int id_len, len;
unsigned char *id_string;
- char *channel_name, *tmp;
+ char *channel_name, *tmp, *hmac;
unsigned int mode, created;
SilcBuffer keyp;
if (!id)
goto out;
+ /* Get hmac */
+ hmac = silc_argument_get_arg_type(cmd->args, 10, NULL);
+
/* See whether we already have the channel. */
entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
if (!entry) {
/* Add the channel to our local list. */
entry = silc_idlist_add_channel(server->local_list, strdup(channel_name),
SILC_CHANNEL_MODE_NONE, id,
- server->router, NULL);
+ server->router, NULL, hmac);
if (!entry) {
silc_free(id);
goto out;
SilcChannelEntry
silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
SilcChannelID *id, SilcServerEntry router,
- SilcCipher channel_key)
+ SilcCipher channel_key, char *hmac)
{
SilcChannelEntry channel;
channel->id = id;
channel->router = router;
channel->channel_key = channel_key;
+ channel->hmac = hmac ? strdup(hmac) : strdup("hmac-sha1-96");
silc_list_init(channel->user_list, struct SilcChannelClientEntryStruct,
channel_list);
Current initial vector. Initial vector is received always along
with the channel packet. By default this is filled with NULL.
+ char *hmac;
+
+ HMAC of the channel. Server only saves the name of the HMAC as
+ it never actually needs to compute the MAC.
+
*/
struct SilcChannelEntryStruct {
char *channel_name;
unsigned char *key;
unsigned int key_len;
unsigned char iv[SILC_CIPHER_MAX_IV_SIZE];
+ char *hmac;
};
/*
SilcChannelEntry
silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
SilcChannelID *id, SilcServerEntry router,
- SilcCipher channel_key);
+ SilcCipher channel_key, char *hmac);
int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry);
SilcChannelEntry
silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
SILC_LOG_DEBUG(("Start"));
+ if (packet->src_id_type != SILC_ID_CLIENT ||
+ packet->dst_id_type != SILC_ID_CLIENT)
+ return;
+
if (!packet->dst_id)
- goto err;
+ return;
/* Decode destination Client ID */
id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT);
if (!id) {
SILC_LOG_ERROR(("Could not decode destination Client ID, dropped"));
- goto err;
+ return;
}
/* If the destination belongs to our server we don't have to route
return;
}
}
+}
+
+/* Received private message key packet.. This packet is never for us. It is to
+ the client in the packet's destination ID. Sending of this sort of packet
+ equals sending private message, ie. it is sent point to point from
+ one client to another. */
+
+void silc_server_private_message_key(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcClientID *id;
+ SilcServerEntry router;
+ SilcSocketConnection dst_sock;
+ SilcClientEntry client;
+ SilcIDListData idata;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ if (packet->src_id_type != SILC_ID_CLIENT ||
+ packet->dst_id_type != SILC_ID_CLIENT)
+ return;
+
+ if (!packet->dst_id)
+ return;
+
+ /* Decode destination Client ID */
+ id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT);
+ if (!id) {
+ SILC_LOG_ERROR(("Could not decode destination Client ID, dropped"));
+ return;
+ }
+
+ /* If the destination belongs to our server we don't have to route
+ the message anywhere but to send it to the local destination. */
+ client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
+ if (client) {
+ /* It exists, now deliver the message to the destination */
+ dst_sock = (SilcSocketConnection)client->connection;
+
+ /* If we are router and the client has router then the client is in
+ our cell but not directly connected to us. */
+ if (server->server_type == SILC_ROUTER && client->router) {
+ /* We are of course in this case the client's router thus the real
+ "router" of the client is the server who owns the client. Thus
+ we will send the packet to that server. */
+ router = (SilcServerEntry)client->router;
+ idata = (SilcIDListData)router;
+ silc_server_send_private_message_key(server, router->connection,
+ idata->send_key,
+ idata->hmac,
+ packet);
+ return;
+ }
+
+ /* Seems that client really is directly connected to us */
+ idata = (SilcIDListData)client;
+ silc_server_send_private_message_key(server, dst_sock,
+ idata->send_key,
+ idata->hmac, packet);
+ return;
+ }
+
+ /* Destination belongs to someone not in this server. If we are normal
+ server our action is to send the packet to our router. */
+ if (server->server_type == SILC_SERVER && !server->standalone) {
+ router = server->router;
+
+ /* Send to primary route */
+ if (router) {
+ dst_sock = (SilcSocketConnection)router->connection;
+ idata = (SilcIDListData)router;
+ silc_server_send_private_message_key(server, dst_sock,
+ idata->send_key,
+ idata->hmac, packet);
+ }
+ return;
+ }
+
+ /* We are router and we will perform route lookup for the destination
+ and send the packet to fastest route. */
+ if (server->server_type == SILC_ROUTER && !server->standalone) {
+ /* Check first that the ID is valid */
+ client = silc_idlist_find_client_by_id(server->global_list, id, NULL);
+ if (client) {
+ dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
+ router = (SilcServerEntry)dst_sock->user_data;
+ idata = (SilcIDListData)router;
- err:
- silc_server_send_error(server, sock,
- "No such nickname: Private message not sent");
+ /* Get fastest route and send packet. */
+ if (router)
+ silc_server_send_private_message_key(server, dst_sock,
+ idata->send_key,
+ idata->hmac, packet);
+ return;
+ }
+ }
}
/* Processes incoming command reply packet. The command reply packet may
sock->hostname));
silc_idlist_add_channel(server->global_list, channel_name, 0, channel_id,
- server->router->connection, NULL);
+ server->router->connection, NULL, NULL);
server->stat.channels++;
} else {
with the channel ID provided by the server. This creates a new
key to the channel as well that we will send to the server. */
if (!channel) {
- channel = silc_server_create_new_channel_with_id(server, NULL,
+ channel = silc_server_create_new_channel_with_id(server, NULL, NULL,
channel_name,
channel_id, FALSE);
if (!channel)
silc_buffer_free(buffer);
silc_free(new);
}
+
+/* Received key agreement packet. This packet is never for us. It is to
+ the client in the packet's destination ID. Sending of this sort of packet
+ equals sending private message, ie. it is sent point to point from
+ one client to another. */
+
+void silc_server_key_agreement(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcClientID *id;
+ SilcServerEntry router;
+ SilcSocketConnection dst_sock;
+ SilcClientEntry client;
+ SilcIDListData idata;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ if (packet->src_id_type != SILC_ID_CLIENT ||
+ packet->dst_id_type != SILC_ID_CLIENT)
+ return;
+
+ if (!packet->dst_id)
+ return;
+
+ /* Decode destination Client ID */
+ id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT);
+ if (!id) {
+ SILC_LOG_ERROR(("Could not decode destination Client ID, dropped"));
+ return;
+ }
+
+ /* If the destination belongs to our server we don't have to route
+ the message anywhere but to send it to the local destination. */
+ client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
+ if (client) {
+ /* It exists, now deliver the message to the destination */
+ dst_sock = (SilcSocketConnection)client->connection;
+
+ /* If we are router and the client has router then the client is in
+ our cell but not directly connected to us. */
+ if (server->server_type == SILC_ROUTER && client->router) {
+ /* We are of course in this case the client's router thus the real
+ "router" of the client is the server who owns the client. Thus
+ we will send the packet to that server. */
+ router = (SilcServerEntry)client->router;
+ idata = (SilcIDListData)router;
+ silc_server_send_key_agreement(server, router->connection,
+ idata->send_key,
+ idata->hmac,
+ packet);
+ return;
+ }
+
+ /* Seems that client really is directly connected to us */
+ idata = (SilcIDListData)client;
+ silc_server_send_key_agreement(server, dst_sock,
+ idata->send_key,
+ idata->hmac, packet);
+ return;
+ }
+
+ /* Destination belongs to someone not in this server. If we are normal
+ server our action is to send the packet to our router. */
+ if (server->server_type == SILC_SERVER && !server->standalone) {
+ router = server->router;
+
+ /* Send to primary route */
+ if (router) {
+ dst_sock = (SilcSocketConnection)router->connection;
+ idata = (SilcIDListData)router;
+ silc_server_send_key_agreement(server, dst_sock,
+ idata->send_key,
+ idata->hmac, packet);
+ }
+ return;
+ }
+
+ /* We are router and we will perform route lookup for the destination
+ and send the packet to fastest route. */
+ if (server->server_type == SILC_ROUTER && !server->standalone) {
+ /* Check first that the ID is valid */
+ client = silc_idlist_find_client_by_id(server->global_list, id, NULL);
+ if (client) {
+ dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
+ router = (SilcServerEntry)dst_sock->user_data;
+ idata = (SilcIDListData)router;
+
+ /* Get fastest route and send packet. */
+ if (router)
+ silc_server_send_key_agreement(server, dst_sock,
+ idata->send_key,
+ idata->hmac, packet);
+ return;
+ }
+ }
+}
void silc_server_private_message(SilcServer server,
SilcSocketConnection sock,
SilcPacketContext *packet);
+void silc_server_private_message_key(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet);
void silc_server_command_reply(SilcServer server,
SilcSocketConnection sock,
SilcPacketContext *packet);
void silc_server_remove_id_list(SilcServer server,
SilcSocketConnection sock,
SilcPacketContext *packet);
+void silc_server_key_agreement(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet);
#endif
silc_server_packet_send(server, sock, SILC_PACKET_HEARTBEAT, 0,
NULL, 0, FALSE);
}
+
+/* Routine used to send (relay, route) key agreement packets to some
+ destination. */
+
+void silc_server_send_key_agreement(SilcServer server,
+ SilcSocketConnection dst_sock,
+ SilcCipher cipher,
+ SilcHmac hmac,
+ SilcPacketContext *packet)
+{
+ silc_buffer_push(packet->buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len
+ + packet->dst_id_len + packet->padlen);
+ silc_packet_send_prepare(dst_sock, 0, 0, packet->buffer->len);
+ silc_buffer_put(dst_sock->outbuf, packet->buffer->data, packet->buffer->len);
+
+ /* Re-encrypt packet */
+ silc_packet_encrypt(cipher, hmac, dst_sock->outbuf, packet->buffer->len);
+
+ /* Send the packet */
+ silc_server_packet_send_real(server, dst_sock, FALSE);
+}
+
+/* Routine used to send (relay, route) private message key packets to some
+ destination. */
+
+void silc_server_send_private_message_key(SilcServer server,
+ SilcSocketConnection dst_sock,
+ SilcCipher cipher,
+ SilcHmac hmac,
+ SilcPacketContext *packet)
+{
+ silc_buffer_push(packet->buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len
+ + packet->dst_id_len + packet->padlen);
+ silc_packet_send_prepare(dst_sock, 0, 0, packet->buffer->len);
+ silc_buffer_put(dst_sock->outbuf, packet->buffer->data, packet->buffer->len);
+
+ /* Re-encrypt packet */
+ silc_packet_encrypt(cipher, hmac, dst_sock->outbuf, packet->buffer->len);
+
+ /* Send the packet */
+ silc_server_packet_send_real(server, dst_sock, FALSE);
+}
unsigned int argc, ...);
void silc_server_send_heartbeat(SilcServer server,
SilcSocketConnection sock);
+void silc_server_send_key_agreement(SilcServer server,
+ SilcSocketConnection dst_sock,
+ SilcCipher cipher,
+ SilcHmac hmac,
+ SilcPacketContext *packet);
+void silc_server_send_private_message_key(SilcServer server,
+ SilcSocketConnection dst_sock,
+ SilcCipher cipher,
+ SilcHmac hmac,
+ SilcPacketContext *packet);
#endif
/* Sets the negotiated key material into use for particular connection. */
-static int silc_server_protocol_ke_set_keys(SilcSKE ske,
- SilcSocketConnection sock,
- SilcSKEKeyMaterial *keymat,
- SilcCipher cipher,
- SilcPKCS pkcs,
- SilcHash hash,
- int is_responder)
+int silc_server_protocol_ke_set_keys(SilcSKE ske,
+ SilcSocketConnection sock,
+ SilcSKEKeyMaterial *keymat,
+ SilcCipher cipher,
+ SilcPKCS pkcs,
+ SilcHash hash,
+ SilcHmac hmac,
+ int is_responder)
{
SilcUnknownEntry conn_data;
SilcIDListData idata;
- SilcHash nhash;
SILC_LOG_DEBUG(("Setting new key into use"));
#endif
/* Save HMAC key to be used in the communication. */
- if (!silc_hash_alloc(hash->hash->name, &nhash)) {
+ if (!silc_hmac_alloc(hmac->hmac->name, NULL, &idata->hmac)) {
silc_cipher_free(idata->send_key);
silc_cipher_free(idata->receive_key);
silc_free(conn_data);
return FALSE;
}
- silc_hmac_alloc(nhash, &idata->hmac);
silc_hmac_set_key(idata->hmac, keymat->hmac_key, keymat->hmac_key_len);
sock->user_data = (void *)conn_data;
* End protocol
*/
SilcSKEKeyMaterial *keymat;
- int key_len = silc_cipher_get_key_len(ctx->ske->prop->cipher, NULL);
+ int key_len = silc_cipher_get_key_len(ctx->ske->prop->cipher);
int hash_len = ctx->ske->prop->hash->hash->hash_len;
- /* Send Ok to the other end if we are responder. If we are
- initiator we have sent this already. */
- if (ctx->responder == TRUE)
- silc_ske_end(ctx->ske, silc_server_protocol_ke_send_packet, context);
-
/* Process the key material */
keymat = silc_calloc(1, sizeof(*keymat));
- silc_ske_process_key_material(ctx->ske, 16, key_len, hash_len,
- keymat);
-
- /* Take the new keys into use. */
- if (!silc_server_protocol_ke_set_keys(ctx->ske, ctx->sock, keymat,
- ctx->ske->prop->cipher,
- ctx->ske->prop->pkcs,
- ctx->ske->prop->hash,
- ctx->responder)) {
+ status = silc_ske_process_key_material(ctx->ske, 16, key_len, hash_len,
+ keymat);
+ if (status != SILC_SKE_STATUS_OK) {
protocol->state = SILC_PROTOCOL_STATE_ERROR;
protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000);
silc_ske_free_key_material(keymat);
return;
}
+ ctx->keymat = keymat;
- silc_ske_free_key_material(keymat);
+ /* Send Ok to the other end if we are responder. If we are initiator
+ we have sent this already. */
+ if (ctx->responder == TRUE)
+ silc_ske_end(ctx->ske, silc_server_protocol_ke_send_packet, context);
/* Unregister the timeout task since the protocol has ended.
This was the timeout task to be executed if the protocol is
SilcTask timeout_task;
SilcPacketContext *packet;
SilcSKE ske;
+ SilcSKEKeyMaterial *keymat;
} SilcServerKEInternalContext;
/* Internal context for connection authentication protocol */
/* Prototypes */
void silc_server_protocols_register(void);
void silc_server_protocols_unregister(void);
+int silc_server_protocol_ke_set_keys(SilcSKE ske,
+ SilcSocketConnection sock,
+ SilcSKEKeyMaterial *keymat,
+ SilcCipher cipher,
+ SilcPKCS pkcs,
+ SilcHash hash,
+ SilcHmac hmac,
+ int is_responder);
#endif
SILC_TASK_CALLBACK(silc_server_packet_process);
SILC_TASK_CALLBACK(silc_server_packet_parse_real);
SILC_TASK_CALLBACK(silc_server_timeout_remote);
+SILC_TASK_CALLBACK(silc_server_failure_callback);
/* Allocates a new SILC server object. This has to be done before the server
can be used. After allocation one must call silc_server_init to initialize
silc_server_config_register_ciphers(server->config);
silc_server_config_register_pkcs(server->config);
silc_server_config_register_hashfuncs(server->config);
+ silc_server_config_register_hmacs(server->config);
/* Initialize random number generator for the server. */
server->rng = silc_rng_alloc();
is sent as argument for fast referencing in the future. */
silc_socket_alloc(sock[i], SILC_SOCKET_TYPE_SERVER, id_entry,
&newsocket);
- if (!newsocket)
- goto err0;
server->sockets[sock[i]] = newsocket;
if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
/* Error occured during protocol */
silc_protocol_free(protocol);
+ silc_ske_free_key_material(ctx->keymat);
if (ctx->packet)
silc_packet_context_free(ctx->packet);
if (ctx->ske)
if (ctx->dest_id)
silc_free(ctx->dest_id);
silc_free(ctx);
- if (sock)
- sock->protocol = NULL;
+ silc_task_unregister_by_callback(server->timeout_queue,
+ silc_server_failure_callback);
silc_server_disconnect_remote(server, sock, "Server closed connection: "
"Key exchange failed");
return;
}
+ /* 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,
+ ctx->ske->prop->cipher,
+ ctx->ske->prop->pkcs,
+ ctx->ske->prop->hash,
+ ctx->ske->prop->hmac,
+ ctx->responder)) {
+ silc_protocol_free(protocol);
+ silc_ske_free_key_material(ctx->keymat);
+ if (ctx->packet)
+ silc_packet_context_free(ctx->packet);
+ if (ctx->ske)
+ silc_ske_free(ctx->ske);
+ if (ctx->dest_id)
+ silc_free(ctx->dest_id);
+ silc_free(ctx);
+ silc_task_unregister_by_callback(server->timeout_queue,
+ silc_server_failure_callback);
+ silc_server_disconnect_remote(server, sock, "Server closed connection: "
+ "Key exchange failed");
+ return;
+ }
+ silc_ske_free_key_material(ctx->keymat);
+
/* Allocate internal context for the authentication protocol. This
is sent as context for the protocol. */
proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
/* Error occured during protocol */
silc_protocol_free(protocol);
+ silc_ske_free_key_material(ctx->keymat);
if (ctx->packet)
silc_packet_context_free(ctx->packet);
if (ctx->ske)
if (ctx->dest_id)
silc_free(ctx->dest_id);
silc_free(ctx);
- if (sock)
- sock->protocol = NULL;
+ silc_task_unregister_by_callback(server->timeout_queue,
+ silc_server_failure_callback);
silc_server_disconnect_remote(server, sock, "Server closed connection: "
"Key exchange failed");
server->stat.auth_failures++;
return;
}
+ /* 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,
+ ctx->ske->prop->cipher,
+ ctx->ske->prop->pkcs,
+ ctx->ske->prop->hash,
+ ctx->ske->prop->hmac,
+ ctx->responder)) {
+ silc_protocol_free(protocol);
+ silc_ske_free_key_material(ctx->keymat);
+ if (ctx->packet)
+ silc_packet_context_free(ctx->packet);
+ if (ctx->ske)
+ silc_ske_free(ctx->ske);
+ if (ctx->dest_id)
+ silc_free(ctx->dest_id);
+ silc_free(ctx);
+ silc_task_unregister_by_callback(server->timeout_queue,
+ silc_server_failure_callback);
+ silc_server_disconnect_remote(server, sock, "Server closed connection: "
+ "Key exchange failed");
+ server->stat.auth_failures++;
+ return;
+ }
+ silc_ske_free_key_material(ctx->keymat);
+
/* Allocate internal context for the authentication protocol. This
is sent as context for the protocol. */
proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
silc_free(ctx);
if (sock)
sock->protocol = NULL;
+ silc_task_unregister_by_callback(server->timeout_queue,
+ silc_server_failure_callback);
silc_server_disconnect_remote(server, sock, "Server closed connection: "
"Authentication failed");
server->stat.auth_failures++;
silc_server_perform_heartbeat,
server->timeout_queue);
+ silc_task_unregister_by_callback(server->timeout_queue,
+ silc_server_failure_callback);
silc_protocol_free(protocol);
if (ctx->packet)
silc_packet_context_free(ctx->packet);
/* If the closed connection was our primary router connection the
start re-connecting phase. */
- if (!server->standalone && server->server_type == SILC_SERVER &&
+ if (!server->standalone && sock->type == SILC_SOCKET_TYPE_ROUTER &&
sock == server->router->connection)
silc_task_register(server->timeout_queue, 0,
silc_server_connect_to_router,
- context, 0, 500000,
+ context, 1, 0,
SILC_TASK_TIMEOUT,
SILC_TASK_PRI_NORMAL);
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
if (sock->protocol) {
- /* XXX Audit the failure type */
- sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
- sock->protocol->execute(server->timeout_queue, 0,
- sock->protocol, sock->sock, 0, 0);
+ SilcServerFailureContext f;
+ f = silc_calloc(1, sizeof(*f));
+ f->server = server;
+ f->sock = sock;
+
+ /* We will wait 5 seconds to process this failure packet */
+ silc_task_register(server->timeout_queue, sock->sock,
+ silc_server_failure_callback, (void *)f, 5, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
}
break;
*/
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
+ silc_server_private_message_key(server, sock, packet);
break;
/*
break;
break;
+ case SILC_PACKET_KEY_AGREEMENT:
+ /*
+ * Received heartbeat.
+ */
+ SILC_LOG_DEBUG(("Key agreement packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_key_agreement(server, sock, packet);
+ break;
+
default:
SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
break;
SilcChannelEntry silc_server_create_new_channel(SilcServer server,
SilcServerID *router_id,
char *cipher,
+ char *hmac,
char *channel_name,
int broadcast)
{
if (!cipher)
cipher = "aes-256-cbc";
+ if (!hmac)
+ hmac = "hmac-sha1-96";
/* Allocate cipher */
if (!silc_cipher_alloc(cipher, &key))
silc_id_create_channel_id(router_id, server->rng, &channel_id);
entry = silc_idlist_add_channel(server->local_list, channel_name,
SILC_CHANNEL_MODE_NONE, channel_id,
- NULL, key);
+ NULL, key, hmac);
if (!entry) {
silc_free(channel_name);
return NULL;
}
/* Now create the actual key material */
- silc_server_create_channel_key(server, entry, 32);
+ silc_server_create_channel_key(server, entry,
+ silc_cipher_get_key_len(key) / 8);
/* Notify other routers about the new channel. We send the packet
to our primary route. */
SilcChannelEntry
silc_server_create_new_channel_with_id(SilcServer server,
char *cipher,
+ char *hmac,
char *channel_name,
SilcChannelID *channel_id,
int broadcast)
if (!cipher)
cipher = "aes-256-cbc";
+ if (!hmac)
+ hmac = "hmac-sha1-96";
/* Allocate cipher */
if (!silc_cipher_alloc(cipher, &key))
/* Create the channel */
entry = silc_idlist_add_channel(server->local_list, channel_name,
SILC_CHANNEL_MODE_NONE, channel_id,
- NULL, key);
+ NULL, key, hmac);
if (!entry) {
silc_free(channel_name);
return NULL;
}
/* Now create the actual key material */
- silc_server_create_channel_key(server, entry, 32);
+ silc_server_create_channel_key(server, entry,
+ silc_cipher_get_key_len(key) / 8);
/* Notify other routers about the new channel. We send the packet
to our primary route. */
unsigned int len;
if (!channel->channel_key)
- silc_cipher_alloc("aes-256-cbc", &channel->channel_key);
+ if (!silc_cipher_alloc("aes-256-cbc", &channel->channel_key))
+ return;
if (key_len)
len = key_len;
else if (channel->key_len)
len = channel->key_len / 8;
else
- len = sizeof(channel_key);
+ len = silc_cipher_get_key_len(channel->channel_key) / 8;
/* Create channel key */
for (i = 0; i < len; i++) channel_key[i] = silc_rng_get_byte(server->rng);
silc_buffer_free(channel_users);
}
}
+
+/* Failure timeout callback. If this is called then we will immediately
+ process the received failure. We always process the failure with timeout
+ since we do not want to blindly trust to received failure packets.
+ This won't be called (the timeout is cancelled) if the failure was
+ bogus (it is bogus if remote does not close the connection after sending
+ the failure). */
+
+SILC_TASK_CALLBACK(silc_server_failure_callback)
+{
+ SilcServerFailureContext f = (SilcServerFailureContext)context;
+
+ if (f->sock->protocol) {
+ f->sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
+ f->sock->protocol->execute(f->server->timeout_queue, 0,
+ f->sock->protocol, f->sock->sock, 0, 0);
+ }
+
+ silc_free(f);
+}
SilcChannelEntry silc_server_create_new_channel(SilcServer server,
SilcServerID *router_id,
char *cipher,
+ char *hmac,
char *channel_name,
int broadcast);
SilcChannelEntry
silc_server_create_new_channel_with_id(SilcServer server,
char *cipher,
+ char *hmac,
char *channel_name,
SilcChannelID *channel_id,
int broadcast);
SilcServer server;
} *SilcServerHBContext;
+/* Failure context. This is allocated when failure packet is received.
+ Failure packets are processed with timeout and data is saved in this
+ structure. */
+typedef struct {
+ SilcServer server;
+ SilcSocketConnection sock;
+ unsigned int failure;
+} *SilcServerFailureContext;
+
/* Macros */
/* Registers generic task for file descriptor for reading from network and
#include "serverincludes.h"
#include "server_internal.h"
-/* XXX
- All possible configuration sections for SILC server.
-
- <Cipher>
-
- Format:
-
- +<Cipher name>:<SIM path>
-
- <PKCS>
-
- Format:
-
- +<PKCS name>:<key length>
-
- <HashFunction>
-
- Format:
-
- +<Hash function name>:<SIM path>
-
- <ServerInfo>
-
- This section is used to set the server informations.
-
- Format:
-
- +<Server DNS name>:<Server IP>:<Geographic location>:<Port>
-
- <AdminInfo>
-
- This section is used to set the server's administrative information.
-
- Format:
-
- +<Location>:<Server type>:<Admin's name>:<Admin's email address>
-
- <ListenPort>
-
- This section is used to set ports the server is listenning.
-
- Format:
-
- +<Local IP/UNIX socket path>:<Remote IP>:<Port>
-
- <Identity>
-
- This section is used to set both the user and group which silcd
- sets itself upon starting.
-
- Format:
-
- <user>:<group>
-
- <Logging>
-
- This section is used to set various logging files, their paths
- and maximum sizes. All the other directives except those defined
- below are ignored in this section. Log files are purged after they
- reach the maximum set byte size.
-
- Format:
-
- +infologfile:<path>:<max byte size>
- +errorlogfile:<path>:<max byte size>
-
- <ConnectionClass>
-
- This section is used to define connection classes. These can be
- used to optimize the server and the connections.
-
- Format:
-
- +<Class number>:<Ping freq>:<Connect freq>:<Max links>
-
- <ClientAuth>
-
- This section is used to define client authentications.
-
- Format:
-
- +<Remote address or name>:<auth method>:<password/cert/key/???>:<Port>:<Class>
-
- <AdminAuth>
-
- This section is used to define the server's administration
- authentications.
-
- Format:
-
- +<Hostname>:<auth method>:<password/cert/key/???>:<Nickname hash>:<Class>
-
- <ServerConnection>
-
- This section is used to define the server connections to this
- server/router. Only routers can have normal server connections.
- Normal servers leave this section epmty. The remote server cannot be
- older than specified Version ID.
-
- Format:
-
- +<Remote address or name>:<auth method>:<password/key/???>:<Port>:<Version ID>:<Class>
-
- <RouterConnection>
-
- This section is used to define the router connections to this
- server/router. Both normal server and router can have router
- connections. Normal server usually has only one connection while
- a router can have multiple. The remote server cannot be older than
- specified Version ID.
-
- Format:
-
- +<Remote address or name>:<auth method>:<password/key/???>:
- <Port>:<Version ID>:<Class>:<Initiator>
-
- <DenyConnection>
-
- This section is used to deny specific connections to your server. This
- can be used to deny both clients and servers.
-
- Format:
-
- +<Remote address or name or nickname>:<Time interval>:<Comment>:<Port>
-
- <RedirectClient>
-
- This section is used to set the alternate servers that clients will be
- redirected to when our server is full.
-
- Format:
-
- +<Remote address or name>:<Port>
-
-*/
SilcServerConfigSection silc_server_config_sections[] = {
{ "[Cipher]",
SILC_CONFIG_SERVER_SECTION_TYPE_CIPHER, 4 },
{ "[PKCS]",
SILC_CONFIG_SERVER_SECTION_TYPE_PKCS, 2 },
- { "[HashFunction]",
+ { "[Hash]",
SILC_CONFIG_SERVER_SECTION_TYPE_HASH_FUNCTION, 4 },
+ { "[hmac]",
+ SILC_CONFIG_SERVER_SECTION_TYPE_HMAC, 3 },
{ "[ServerInfo]",
SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_INFO, 4 },
{ "[AdminInfo]",
checkmask |= (1L << pc->section->type);
break;
+ case SILC_CONFIG_SERVER_SECTION_TYPE_HMAC:
+
+ SILC_SERVER_CONFIG_LIST_ALLOC(config->hmac);
+
+ /* Get HMAC name */
+ ret = silc_config_get_token(line, &config->hmac->alg_name);
+ if (ret < 0)
+ break;
+ if (ret == 0) {
+ fprintf(stderr, "%s:%d: HMAC name not defined\n",
+ config->filename, pc->linenum);
+ break;
+ }
+
+ /* Get hash name */
+ ret = silc_config_get_token(line, &config->hmac->sim_name);
+ if (ret < 0)
+ break;
+ if (ret == 0) {
+ fprintf(stderr, "%s:%d: Hash function name not defined\n",
+ config->filename, pc->linenum);
+ break;
+ }
+
+ /* Get MAC length */
+ ret = silc_config_get_token(line, &tmp);
+ if (ret < 0)
+ break;
+ if (ret == 0) {
+ fprintf(stderr, "%s:%d: HMAC's MAC length not defined\n",
+ config->filename, pc->linenum);
+ break;
+ }
+ config->hmac->key_len = atoi(tmp);
+ silc_free(tmp);
+
+ check = TRUE;
+ checkmask |= (1L << pc->section->type);
+ break;
+
case SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_INFO:
if (!config->server_info)
config->pkcs = config->pkcs->prev;
while (config->hash_func && config->hash_func->prev)
config->hash_func = config->hash_func->prev;
+ while (config->hmac && config->hmac->prev)
+ config->hmac = config->hmac->prev;
while (config->listen_port && config->listen_port->prev)
config->listen_port = config->listen_port->prev;
while (config->logging && config->logging->prev)
silc_server_stop(server);
exit(1);
}
- silc_free(tmp);
+ silc_hash_free(tmp);
#ifdef SILC_SIM
} else {
exit(1);
}
- /* Register the cipher */
+ /* Register the hash function */
silc_hash_register(&hash);
#endif
}
}
}
+/* Registers configure HMACs. These can then be allocated by the server
+ when needed. */
+
+void silc_server_config_register_hmacs(SilcServerConfig config)
+{
+ SilcServerConfigSectionAlg *alg;
+ SilcServer server = (SilcServer)config->server;
+
+ SILC_LOG_DEBUG(("Registering configured HMACs"));
+
+ if (!config->hmac) {
+ SILC_LOG_ERROR(("HMACs are not configured. SILC cannot work without "
+ "HMACs"));
+ silc_server_stop(server);
+ exit(1);
+ }
+
+ alg = config->hmac;
+ while(alg) {
+ SilcHmacObject hmac;
+
+ if (!silc_hash_is_supported(alg->sim_name)) {
+ SILC_LOG_ERROR(("Unsupported hash function `%s'", alg->sim_name));
+ silc_server_stop(server);
+ exit(1);
+ }
+
+ /* Register the HMAC */
+ memset(&hmac, 0, sizeof(hmac));
+ hmac.name = alg->alg_name;
+ hmac.len = alg->key_len;
+ silc_hmac_register(&hmac);
+
+ alg = alg->next;
+ }
+}
+
/* Returns client authentication information from server configuration
by host (name or ip). */
SilcServerConfigSectionAlg *cipher;
SilcServerConfigSectionAlg *pkcs;
SilcServerConfigSectionAlg *hash_func;
+ SilcServerConfigSectionAlg *hmac;
SilcServerConfigSectionServerInfo *server_info;
SilcServerConfigSectionAdminInfo *admin_info;
SilcServerConfigSectionListenPort *listen_port;
SILC_CONFIG_SERVER_SECTION_TYPE_CIPHER,
SILC_CONFIG_SERVER_SECTION_TYPE_PKCS,
SILC_CONFIG_SERVER_SECTION_TYPE_HASH_FUNCTION,
+ SILC_CONFIG_SERVER_SECTION_TYPE_HMAC,
SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_INFO,
SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_INFO,
SILC_CONFIG_SERVER_SECTION_TYPE_LISTEN_PORT,
void silc_server_config_register_ciphers(SilcServerConfig config);
void silc_server_config_register_pkcs(SilcServerConfig config);
void silc_server_config_register_hashfuncs(SilcServerConfig config);
+void silc_server_config_register_hmacs(SilcServerConfig config);
SilcServerConfigSectionClientConnection *
silc_server_config_find_client_conn(SilcServerConfig config,
char *host, int port);
mars-128-cbc:../lib/silcsim/modules/mars.sim.so:16:16
none:../lib/silcsim/modules/none.sim.so:0:0
-[HashFunction]
+[Hash]
md5::64:16
sha1::64:20
+[hmac]
+hmac-sha1-96:sha1:12
+hmac-md5-96:md5:12
+hmac-sha1:sha1:20
+hmac-md5:md5:16
+
#[PKCS]
#rsa::1024
#dss::1024
Mun huone:Mun servo:Pekka Riikonen:priikone@poseidon.pspt.fi
[ServerInfo]
-lassi.kuo.fi.ssh.com:212.146.42.253:Kuopio, Finland:1334
+lassi.kuo.fi.ssh.com:212.146.42.253:Kuopio, Finland:1335
[ListenPort]
-212.146.42.253:212.146.42.253:1334
+212.146.42.253:212.146.42.253:1335
[Logging]
infologfile:silcd2.log:10000
[AdminConnection]
[ServerConnection]
-212.146.42.253:passwd:priikone:1333:1:1
+212.146.42.253:passwd:priikone:1336:1:1
[RouterConnection]
-212.146.42.253:passwd:priikone:1335:1:1:0
+212.146.42.253:passwd:priikone:1334:1:1:0
[DenyConnection]
[RedirectClient]
| Hash Alg Length | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
| |
-~ Hash Algorithms ~
+~ Hash Algorithms ~
+| |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| HMAC Length | |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
+| |
+~ HMACs ~
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Compression Alg Length | |
o Encryption Algorithms (variable length) - The list of
encryption algorithms.
-o Hash Alg Length (2 bytes) - The length of the Hash algorithms
+o Hash Alg Length (2 bytes) - The length of the Hash algorithm
list, not including any other field.
-o Hash Algorithms (variable length) - The list of Hash algorithms.
+o Hash Algorithms (variable length) - The list of Hash
+ algorithms. The hash algorithms are mainly used in the
+ SKE protocol.
+
+o HMAC Length (2 bytes) - The length of the HMAC list, not
+ including any other field.
+
+o HMACs (variable length) - The list of HMACs. The HMAC's
+ are used to compute the Message Authentication Codes (MAC)
+ of the SILC packets.
o Compression Alg Length (2 bytes) - The length of the
compression algorithms list, not including any other field.
certificate specification in [PGP]. See SPKI certificate
specification in [SPKI]. If this field includes zero (0)
or unsupported type number the protocol must be aborted
- sending SILC_PACKET_FAILURE message.
+ sending SILC_PACKET_FAILURE message and the connection should
+ be closed immediately.
o Public Data Length (2 bytes) - The length of the public
data computed by the responder, not including any other
If any of these phases is to fail SILC_PACKET_FAILURE is sent to
-indicate that the key exchange protocol failed. Any other packets must
-not be sent or accepted during the key exchange except the
-SILC_PACKET_KEY_EXCHANGE_*, SILC_PACKET_DISCONNECT, SILC_PACKET_FAILURE
-and/or SILC_PACKET_SUCCESS packets.
+indicate that the key exchange protocol has failed, and the connection
+should be closed immediately. Any other packets must not be sent or
+accepted during the key exchange except the SILC_PACKET_KEY_EXCHANGE_*,
+SILC_PACKET_FAILURE and SILC_PACKET_SUCCESS packets.
The result of this protocol is a shared secret key material KEY and
a hash value HASH. The key material itself is not fit to be used as
None of the provided hash functions were supported.
-7 SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY
+7 SILC_SKE_STATUS_UNSUPPORTED_HMAC
+
+ None of the provided HMACs were supported.
+
+
+8 SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY
Provided public key type is not supported.
-8 SILC_SKE_STATUS_INCORRECT_SIGNATURE
+9 SILC_SKE_STATUS_INCORRECT_SIGNATURE
Provided signature was incorrect.
-9 SILC_SKE_STATUS_BAD_VERSION
+10 SILC_SKE_STATUS_BAD_VERSION
Provided version string was not acceptable.
.in 3
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+~ MAC ~
+| |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| |
~ Initial Vector * ~
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
applied because this payload is encrypted separately from
other parts of the packet.
+o MAC (variable legnth) - The MAC computed from the
+ Message Length, Message Data, Padding Length and Padding
+ fields. This protects the integrity of the plaintext
+ channel message. The receiver can verify from the MAC
+ whether the message decrypted correctly. Also, if more than
+ one private key has been set for the channel, the receiver
+ can verify which of the keys decrypted the message
+ correctly. Note that, this field is encrypted and must
+ be added to the padding calculation.
+
o Initial Vector (variable length) - The initial vector
that has been used in packet encryption. It needs to be
used in the packet decryption as well. What this field
packet processing is special. Rest of the SILC Packet header is
decrypted with the same session key along with the padding of the
packet. After that the packet is protected with the channel specific
-key and hence can be decrypted only if the receiver is the client on
+key and thus can be decrypted only if the receiver is the client on
the channel. See section 2.7 Packet Padding Generation for more
information about padding on special packets.
3.10 Algorithms ............................................... 20
3.10.1 Ciphers ............................................ 20
3.10.2 Public Key Algorithms .............................. 21
- 3.10.3 MAC Algorithms ..................................... 21
- 3.10.4 Compression Algorithms ............................. 22
+ 3.10.3 Hash Functions ..................................... XXX
+ 3.10.4 MAC Algorithms ..................................... XXX
+ 3.10.5 Compression Algorithms ............................. XXX
3.11 SILC Public Key .......................................... 22
3.12 SILC Version Detection ................................... 24
4 SILC Procedures ............................................... 25
Additional public key algorithms may be defined to be used in SILC.
+.ti0
+3.10.3 Hash Functions
+
+Hash functions are used as part of MAC algorithms defined in the next
+section. They are also used in the SILC Key Exchange protocol defined
+in the [SILC3].
+
+Following Hash algorithm are defined in SILC protocol:
+
+sha1 SHA-1, length = 20 (mandatory)
+md5 MD5, length = 16 (optional)
+
+
.ti 0
-3.10.3 MAC Algorithms
+3.10.4 MAC Algorithms
Data integrity is protected by computing a message authentication code
(MAC) of the packet data. See [SILC2] for details how to compute the
Following MAC algorithms are defined in SILC protocol:
.in 6
-hmac-sha1 HMAC-SHA1, length = 20 (mandatory)
+hmac-sha1-96 HMAC-SHA1, length = 12 (mandatory)
+hmac-md5-96 HMAC-MD5, length = 12 (optional)
+hmac-sha1 HMAC-SHA1, length = 20 (optional)
hmac-md5 HMAC-MD5, length = 16 (optional)
none No MAC (optional)
.in 3
.ti 0
-3.10.4 Compression Algorithms
+3.10.5 Compression Algorithms
SILC protocol supports compression that may be applied to unencrypted
data. It is recommended to use compression on slow links as it may
how channel messages must be encrypted and decrypted when router is
processing them.
+When client receives the SILC_PACKET_CHANNEL_KEY packet with the
+Channel Key Payload it must process the key data to create encryption
+and decryption key, and to create the HMAC key that is used to compute
+the MACs of the channel messages. The processing is as follows:
+
+ channel_key = raw key data
+ HMAC key = hash(raw key data)
+
+The raw key data is the key data received in the Channel Key Payload.
+The hash() function is the hash function used in the HMAC of the channel.
+
.ti 0
4.5 Private Message Sending and Reception
14 SILC_COMMAND_JOIN
- Max Arguments: 4
+ Max Arguments: 5
Arguments: (1) <channel> (2) <Client ID>
(3) [<passphrase>] (4) [<cipher>]
+ (5) [<hmac>]
Join to channel/create new channel. This command is used to
join to a channel. If the channel does not exist the channel is
requested by sending the name of the requested <cipher>. This
is used only if the channel does not exist and is created. If
the channel already exists the cipher set previously for the
- channel will be used to secure the traffic.
+ channel will be used to secure the traffic. The computed MACs
+ of the channel message are produced by the default HMAC or by
+ the <hmac> provided for the command.
The server must check whether the user is allowed to join to
the requested channel. Various modes set to the channel affect
Reply messages to the command:
- Max Arguments: 9
+ Max Arguments: 10
Arguments: (1) <Status Payload> (2) <channel>
(3) <Channel ID> (4) <channel mode mask>
(5) <created> (6) <Channel Key Payload>
(7) [<ban mask>] (8) [<invite list>]
- (9) [<topic>]
+ (9) [<topic>] (10) [<hmac>]
This command replies with the channel name requested by the
client, channel ID of the channel and topic of the channel
17 SILC_COMMAND_CMODE
- Max Arguments: 7
+ Max Arguments: 8
Arguments: (1) <Channel ID> (2) <channel mode mask>
(3) [<user limit>] (4) [<passphrase>]
(5) [<ban mask>] (6) [<invite list>]
- (7) [<cipher>[:<key len>]]
+ (7) [<cipher>] (8) [<hmac>]
This command is used by client to set or change channel flags on
a channel. Channel has several modes that set various properties
Sets specific cipher to be used to protect channel
traffic. The <cipher> argument is the requested cipher.
When set or unset the server must re-generate new
- channel key. If <key len> argument is specified with
- <cipher> argument the new key is generated of <key len>
- length in bits. Only channel founder may set the cipher of
+ channel key. Only channel founder may set the cipher of
the channel. When unset the new key is generated using
default cipher for the channel.
to set/unset this mode.
+ 0x0400 SILC_CMODE_HMAC
+
+ Sets specific hmac to be used to compute the MACs of the
+ channel message. The <hmac> argument is the requested hmac.
+ Only channel founder may set the hmac of the channel.
+
+ Typical implementation would use [+|-]h on user interface
+ to set/unset this mode.
+
+
To make the mode system work, client must keep the channel mode
mask locally so that the mode setting and unsetting would work
without problems. The client receives the initial channel mode
# If the hash function is builtin the <module path> maybe omitted.
#
[hash]
-md5::64:16
sha1::64:20
+md5::64:16
+
+#
+# Configured HMAC functions. The hash function used in the HMAC must
+# configured to the [hash] section.
+#
+# Format: <name>:<hash name>:<mac length>
+#
+[hmac]
+hmac-sha1-96:sha1:12
+hmac-md5-96:md5:12
+hmac-sha1:sha1:20
+hmac-md5:md5:16
#
# Configured PKCS.
#
# If the hash function is builtin the <module path> maybe omitted.
#
-[HashFunction]
-md5::64:16
+[Hash]
sha1::64:20
+md5::64:16
+
+#
+# Configured HMAC functions. The hash function used in the HMAC must
+# configured to the [hash] section.
+#
+# Format: <name>:<hash name>:<mac length>
+#
+[hmac]
+hmac-sha1-96:sha1:12
+hmac-md5-96:md5:12
+hmac-sha1:sha1:20
+hmac-md5:md5:16
#
# Configured PKCS.
libsilcclient_a_SOURCES = \
client.c \
+ client_keyagr.c \
+ client_notify.c \
+ client_prvmsg.c \
+ client_channel.c \
command.c \
command_reply.c \
idlist.c \
interface. The library provides the API for the application which it can
use to implement generally whatever user interface it wants.
-The `ops.h' file defines the function prototypes that application must
+The `silcapi.h' file defines the function prototypes that application must
implement in order to be able to create the user interface with the
library. The idea is that the application can implement whatever user
interface routines in the functions and display the data whatever way
/* $Id$ */
#include "clientlibincludes.h"
+#include "client_internal.h"
/* Static task callback prototypes */
SILC_TASK_CALLBACK(silc_client_connect_to_server_start);
SILC_TASK_CALLBACK(silc_client_connect_to_server_second);
SILC_TASK_CALLBACK(silc_client_connect_to_server_final);
-SILC_TASK_CALLBACK(silc_client_packet_process);
SILC_TASK_CALLBACK(silc_client_packet_parse_real);
static void silc_client_packet_parse(SilcPacketParserContext *parser_context);
}
}
-/* Internal context for connection process. This is needed as we
- doing asynchronous connecting. */
-typedef struct {
- SilcClient client;
- SilcClientConnection conn;
- SilcTask task;
- int sock;
- char *host;
- int port;
- int tries;
-} SilcClientInternalConnectContext;
-
static int
silc_client_connect_to_server_internal(SilcClientInternalConnectContext *ctx)
{
/* Allocate new socket connection object */
silc_socket_alloc(fd, SILC_SOCKET_TYPE_SERVER, (void *)conn, &conn->sock);
- if (conn->sock == NULL) {
- client->ops->say(client, conn,
- "Error: Could not allocate connection socket");
- return FALSE;
- }
conn->nickname = strdup(client->username);
conn->sock->hostname = conn->remote_host;
proto_ctx->sock = conn->sock;
proto_ctx->rng = client->rng;
proto_ctx->responder = FALSE;
+ proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
/* Perform key exchange protocol. silc_client_connect_to_server_final
will be called after the protocol is finished. */
/* Error occured during protocol */
SILC_LOG_DEBUG(("Error during KE protocol"));
silc_protocol_free(protocol);
+ silc_ske_free_key_material(ctx->keymat);
if (ctx->ske)
silc_ske_free(ctx->ske);
if (ctx->dest_id)
silc_free(ctx->dest_id);
ctx->sock->protocol = NULL;
+ silc_task_unregister_by_callback(client->timeout_queue,
+ silc_client_failure_callback);
/* Notify application of failure */
client->ops->connect(client, ctx->sock->user_data, FALSE);
return;
}
+ /* 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. */
+ silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
+ ctx->ske->prop->cipher,
+ ctx->ske->prop->pkcs,
+ ctx->ske->prop->hash,
+ ctx->ske->prop->hmac);
+ silc_ske_free_key_material(ctx->keymat);
+
/* Allocate internal context for the authentication protocol. This
is sent as context for the protocol. */
proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
if (ctx->dest_id)
silc_free(ctx->dest_id);
conn->sock->protocol = NULL;
+ silc_task_unregister_by_callback(client->timeout_queue,
+ silc_client_failure_callback);
/* Notify application of failure */
client->ops->connect(client, ctx->sock->user_data, FALSE);
conn->remote_id_data = silc_id_id2str(ctx->dest_id, SILC_ID_SERVER);
conn->remote_id_data_len = SILC_ID_SERVER_LEN;
+ silc_task_unregister_by_callback(client->timeout_queue,
+ silc_client_failure_callback);
silc_protocol_free(protocol);
if (ctx->auth_data)
silc_free(ctx->auth_data);
is used directly only in special cases. Normal cases should use
silc_server_packet_send. Returns < 0 on error. */
-static int silc_client_packet_send_real(SilcClient client,
- SilcSocketConnection sock,
- int force_send)
+int silc_client_packet_send_real(SilcClient client,
+ SilcSocketConnection sock,
+ int force_send)
{
int ret;
/* Packet processing callback. This is used to send and receive packets
from network. This is generic task. */
-SILC_TASK_CALLBACK(silc_client_packet_process)
+SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process)
{
SilcClient client = (SilcClient)context;
SilcSocketConnection sock = NULL;
* error and call the protocol callback. This fill cause error on
* protocol and it will call the final callback.
*/
- if (sock->protocol) {
- sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
- sock->protocol->execute(client->timeout_queue, 0,
- sock->protocol, sock->sock, 0, 0);
-
- /* XXX We have only two protocols currently thus we know what this
- failure indication is. */
- if (buffer->len >= 4) {
- unsigned int failure;
-
- SILC_GET32_MSB(failure, buffer->data);
-
- /* Notify application */
- client->ops->failure(client, sock->user_data, sock->protocol,
- (void *)failure);
- }
- }
+ silc_client_process_failure(client, sock, packet);
break;
case SILC_PACKET_REJECT:
break;
SILC_LOG_DEBUG(("Heartbeat packet"));
break;
+ case SILC_PACKET_KEY_AGREEMENT:
+ /*
+ * Received key agreement packet
+ */
+ SILC_LOG_DEBUG(("Key agreement packet"));
+ silc_client_key_agreement(client, sock, packet);
+ break;
+
default:
SILC_LOG_DEBUG(("Incorrect packet type %d, packet dropped", type));
break;
silc_client_packet_send_real(client, sock, force_send);
}
-/* Sends packet to the `channel'. Packet to channel is always encrypted
- differently from "normal" packets. SILC header of the packet is
- encrypted with the next receiver's key and the rest of the packet is
- encrypted with the channel specific key. Padding and HMAC is computed
- with the next receiver's key. The `data' is the channel message. If
- the `force_send' is TRUE then the packet is sent immediately. */
-
-void silc_client_send_channel_message(SilcClient client,
- SilcClientConnection conn,
- SilcChannelEntry channel,
- unsigned char *data,
- unsigned int data_len,
- int force_send)
-{
- int i;
- SilcSocketConnection sock = conn->sock;
- SilcBuffer payload;
- SilcPacketContext packetdata;
- SilcCipher cipher;
- SilcHmac hmac;
- unsigned char *id_string;
- unsigned int block_len;
-
- SILC_LOG_DEBUG(("Sending packet to channel"));
-
- if (!channel || !channel->key) {
- client->ops->say(client, conn,
- "Cannot talk to channel: key does not exist");
- return;
- }
-
- /* Generate IV */
- block_len = silc_cipher_get_block_len(channel->channel_key);
- if (channel->iv[0] == '\0')
- for (i = 0; i < block_len; i++) channel->iv[i] =
- silc_rng_get_byte(client->rng);
- else
- silc_hash_make(client->md5hash, channel->iv, block_len, channel->iv);
-
- /* Encode the channel payload */
- payload = silc_channel_payload_encode(data_len, data, block_len,
- channel->iv, client->rng);
- if (!payload) {
- client->ops->say(client, conn,
- "Error: Could not create packet to be sent to channel");
- return;
- }
-
- /* Get data used in packet header encryption, keys and stuff. Rest
- of the packet (the payload) is, however, encrypted with the
- specified channel key. */
- cipher = conn->send_key;
- hmac = conn->hmac;
- id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
-
- /* Set the packet context pointers. The destination ID is always
- the Channel ID of the channel. Server and router will handle the
- distribution of the packet. */
- packetdata.flags = 0;
- packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
- packetdata.src_id = conn->local_id_data;
- packetdata.src_id_len = SILC_ID_CLIENT_LEN;
- packetdata.src_id_type = SILC_ID_CLIENT;
- packetdata.dst_id = id_string;
- packetdata.dst_id_len = SILC_ID_CHANNEL_LEN;
- packetdata.dst_id_type = SILC_ID_CHANNEL;
- packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN +
- packetdata.src_id_len + packetdata.dst_id_len;
- packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
- packetdata.src_id_len +
- packetdata.dst_id_len));
-
- /* Prepare outgoing data buffer for packet sending */
- silc_packet_send_prepare(sock,
- SILC_PACKET_HEADER_LEN +
- packetdata.src_id_len +
- packetdata.dst_id_len,
- packetdata.padlen,
- payload->len);
-
- packetdata.buffer = sock->outbuf;
-
- /* Encrypt payload of the packet. This is encrypted with the channel key. */
- channel->channel_key->cipher->encrypt(channel->channel_key->context,
- payload->data, payload->data,
- payload->len - block_len, /* -IV_LEN */
- channel->iv);
-
- /* Put the actual encrypted payload data into the buffer. */
- silc_buffer_put(sock->outbuf, payload->data, payload->len);
-
- /* Create the outgoing packet */
- silc_packet_assemble(&packetdata);
-
- /* Encrypt the header and padding of the packet. This is encrypted
- with normal session key shared with our server. */
- silc_packet_encrypt(cipher, hmac, sock->outbuf, SILC_PACKET_HEADER_LEN +
- packetdata.src_id_len + packetdata.dst_id_len +
- packetdata.padlen);
-
- SILC_LOG_HEXDUMP(("Packet to channel, len %d", sock->outbuf->len),
- sock->outbuf->data, sock->outbuf->len);
-
- /* Now actually send the packet */
- silc_client_packet_send_real(client, sock, force_send);
- silc_buffer_free(payload);
- silc_free(id_string);
-}
-
-/* Sends private message to remote client. If private message key has
- not been set with this client then the message will be encrypted using
- normal session keys. Private messages are special packets in SILC
- network hence we need this own function for them. This is similiar
- to silc_client_packet_send_to_channel except that we send private
- message. The `data' is the private message. If the `force_send' is
- TRUE the packet is sent immediately. */
-
-void silc_client_send_private_message(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry client_entry,
- unsigned char *data,
- unsigned int data_len,
- int force_send)
-{
- SilcSocketConnection sock = conn->sock;
- SilcBuffer buffer;
- SilcPacketContext packetdata;
- unsigned int nick_len;
- SilcCipher cipher;
- SilcHmac hmac;
-
- SILC_LOG_DEBUG(("Sending private message"));
-
- /* Create private message payload */
- nick_len = strlen(conn->nickname);
- buffer = silc_buffer_alloc(2 + nick_len + data_len);
- silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
- silc_buffer_format(buffer,
- SILC_STR_UI_SHORT(nick_len),
- SILC_STR_UI_XNSTRING(conn->nickname,
- nick_len),
- SILC_STR_UI_XNSTRING(data, data_len),
- SILC_STR_END);
-
- /* If we don't have private message specific key then private messages
- are just as any normal packet thus call normal packet sending. If
- the key exist then the encryption process is a bit different and
- will be done in the rest of this function. */
- if (!client_entry->send_key) {
- silc_client_packet_send(client, sock, SILC_PACKET_PRIVATE_MESSAGE,
- client_entry->id, SILC_ID_CLIENT, NULL, NULL,
- buffer->data, buffer->len, force_send);
- goto out;
- }
-
- /* We have private message specific key */
-
- /* Get data used in the encryption */
- cipher = client_entry->send_key;
- hmac = conn->hmac;
-
- /* Set the packet context pointers. */
- packetdata.flags = SILC_PACKET_FLAG_PRIVMSG_KEY;
- packetdata.type = SILC_PACKET_PRIVATE_MESSAGE;
- packetdata.src_id = conn->local_id_data;
- packetdata.src_id_len = SILC_ID_CLIENT_LEN;
- packetdata.src_id_type = SILC_ID_CLIENT;
- packetdata.dst_id = silc_id_id2str(client_entry->id, SILC_ID_CLIENT);
- packetdata.dst_id_len = SILC_ID_CLIENT_LEN;
- packetdata.dst_id_type = SILC_ID_CLIENT;
- packetdata.truelen = buffer->len + SILC_PACKET_HEADER_LEN +
- packetdata.src_id_len + packetdata.dst_id_len;
- packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
- packetdata.src_id_len +
- packetdata.dst_id_len));
-
- /* Prepare outgoing data buffer for packet sending */
- silc_packet_send_prepare(sock,
- SILC_PACKET_HEADER_LEN +
- packetdata.src_id_len +
- packetdata.dst_id_len,
- packetdata.padlen,
- buffer->len);
-
- packetdata.buffer = sock->outbuf;
-
- /* Encrypt payload of the packet. Encrypt with private message specific
- key */
- cipher->cipher->encrypt(cipher->context, buffer->data, buffer->data,
- buffer->len, cipher->iv);
-
- /* Put the actual encrypted payload data into the buffer. */
- silc_buffer_put(sock->outbuf, buffer->data, buffer->len);
-
- /* Create the outgoing packet */
- silc_packet_assemble(&packetdata);
-
- /* Encrypt the header and padding of the packet. */
- cipher = conn->send_key;
- silc_packet_encrypt(cipher, hmac, sock->outbuf, SILC_PACKET_HEADER_LEN +
- packetdata.src_id_len + packetdata.dst_id_len +
- packetdata.padlen);
-
- SILC_LOG_HEXDUMP(("Private message packet, len %d", sock->outbuf->len),
- sock->outbuf->data, sock->outbuf->len);
-
- /* Now actually send the packet */
- silc_client_packet_send_real(client, sock, force_send);
- silc_free(packetdata.dst_id);
-
- out:
- silc_free(buffer);
-}
-
/* Closes connection to remote end. Free's all allocated data except
for some information such as nickname etc. that are valid at all time. */
silc_free(msg);
}
-/* Called when notify is received and some async operation (such as command)
- is required before processing the notify message. This calls again the
- silc_client_notify_by_server and reprocesses the original notify packet. */
+/* Processes the received new Client ID from server. Old Client ID is
+ deleted from cache and new one is added. */
-static void silc_client_notify_by_server_pending(void *context)
+void silc_client_receive_new_id(SilcClient client,
+ SilcSocketConnection sock,
+ SilcIDPayload idp)
{
- SilcPacketContext *p = (SilcPacketContext *)context;
- silc_client_notify_by_server(p->context, p->sock, p);
-}
+ SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+ int connecting = FALSE;
-/* Destructor for the pending command callback */
+ if (!conn->local_entry)
+ connecting = TRUE;
-static void silc_client_notify_by_server_destructor(void *context)
-{
- silc_packet_context_free((SilcPacketContext *)context);
-}
+ /* Delete old ID from ID cache */
+ silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, conn->local_id);
+
+ /* Save the new ID */
+ if (conn->local_id)
+ silc_free(conn->local_id);
+ if (conn->local_id_data)
+ silc_free(conn->local_id_data);
-/* Resolve client information from server by Client ID. */
+ conn->local_id = silc_id_payload_get_id(idp);
+ conn->local_id_data = silc_id_payload_get_data(idp);
+ conn->local_id_data_len = silc_id_payload_get_len(idp);;
-static void silc_client_notify_by_server_resolve(SilcClient client,
- SilcClientConnection conn,
- SilcPacketContext *packet,
- SilcClientID *client_id)
-{
- SilcPacketContext *p = silc_packet_context_dup(packet);
- SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
-
- p->context = (void *)client;
- p->sock = conn->sock;
-
- silc_client_send_command(client, conn, SILC_COMMAND_WHOIS, ++conn->cmd_ident,
- 1, 3, idp->data, idp->len);
- silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
- silc_client_notify_by_server_destructor,
- silc_client_notify_by_server_pending, p);
- silc_buffer_free(idp);
-}
+ if (!conn->local_entry)
+ conn->local_entry = silc_calloc(1, sizeof(*conn->local_entry));
+
+ conn->local_entry->nickname = conn->nickname;
+ if (!conn->local_entry->username) {
+ conn->local_entry->username =
+ silc_calloc(strlen(client->username) + strlen(client->hostname) + 1,
+ sizeof(conn->local_entry->username));
+ sprintf(conn->local_entry->username, "%s@%s", client->username,
+ client->hostname);
+ }
+ conn->local_entry->server = strdup(conn->remote_host);
+ conn->local_entry->id = conn->local_id;
+
+ /* Put it to the ID cache */
+ silc_idcache_add(conn->client_cache, conn->nickname, SILC_ID_CLIENT,
+ conn->local_id, (void *)conn->local_entry, TRUE);
-/* Received notify message from server */
+ /* Notify application of successful connection. We do it here now that
+ we've received the Client ID and are allowed to send traffic. */
+ if (connecting)
+ client->ops->connect(client, conn, TRUE);
+}
-void silc_client_notify_by_server(SilcClient client,
- SilcSocketConnection sock,
- SilcPacketContext *packet)
+/* Processed received Channel ID for a channel. This is called when client
+ joins to channel and server replies with channel ID. The ID is cached.
+ Returns the created channel entry. */
+
+SilcChannelEntry silc_client_new_channel_id(SilcClient client,
+ SilcSocketConnection sock,
+ char *channel_name,
+ unsigned int mode,
+ SilcIDPayload idp)
{
- SilcBuffer buffer = packet->buffer;
SilcClientConnection conn = (SilcClientConnection)sock->user_data;
- SilcNotifyPayload payload;
- SilcNotifyType type;
- SilcArgumentPayload args;
-
- SilcClientID *client_id = NULL;
- SilcChannelID *channel_id = NULL;
- SilcClientEntry client_entry;
- SilcClientEntry client_entry2;
SilcChannelEntry channel;
- SilcChannelUser chu;
- SilcIDCacheEntry id_cache = NULL;
- unsigned char *tmp;
- unsigned int tmp_len, mode;
-
- payload = silc_notify_payload_parse(buffer);
- if (!payload)
- goto out;
-
- type = silc_notify_get_type(payload);
- args = silc_notify_get_args(payload);
- if (!args)
- goto out;
-
- switch(type) {
- case SILC_NOTIFY_TYPE_NONE:
- /* Notify application */
- client->ops->notify(client, conn, type,
- silc_argument_get_arg_type(args, 1, NULL));
- break;
- case SILC_NOTIFY_TYPE_INVITE:
- /*
- * Someone invited me to a channel. Find Client and Channel entries
- * for the application.
- */
-
- /* Get Client ID */
- tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
- if (!tmp)
- goto out;
-
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
- if (!client_id)
- goto out;
-
- /* Find Client entry and if not found query it */
- client_entry = silc_client_get_client_by_id(client, conn, client_id);
- if (!client_entry) {
- silc_client_notify_by_server_resolve(client, conn, packet, client_id);
- goto out;
- }
-
- /* Get Channel ID */
- tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
- if (!tmp)
- goto out;
-
- channel_id = silc_id_payload_parse_id(tmp, tmp_len);
- if (!channel_id)
- goto out;
-
- /* XXX Will ALWAYS fail because currently we don't have way to resolve
- channel information for channel that we're not joined to. */
- /* XXX ways to fix: use (extended) LIST command, or define the channel
- name to the notfy type when name resolving is not mandatory. */
- /* Find channel entry */
- if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
- SILC_ID_CHANNEL, &id_cache))
- goto out;
-
- channel = (SilcChannelEntry)id_cache->context;
-
- /* Notify application */
- client->ops->notify(client, conn, type, client_entry, channel);
- break;
-
- case SILC_NOTIFY_TYPE_JOIN:
- /*
- * Someone has joined to a channel. Get their ID and nickname and
- * cache them for later use.
- */
-
- /* Get Client ID */
- tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
- if (!tmp)
- goto out;
-
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
- if (!client_id)
- goto out;
-
- /* Find Client entry and if not found query it */
- client_entry = silc_client_get_client_by_id(client, conn, client_id);
- if (!client_entry) {
- silc_client_notify_by_server_resolve(client, conn, packet, client_id);
- goto out;
- }
-
- /* If nickname or username hasn't been resolved, do so */
- if (!client_entry->nickname || !client_entry->username) {
- silc_client_notify_by_server_resolve(client, conn, packet, client_id);
- goto out;
- }
+ SILC_LOG_DEBUG(("New channel ID"));
- /* Get Channel ID */
- tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
- if (!tmp)
- goto out;
+ channel = silc_calloc(1, sizeof(*channel));
+ channel->channel_name = channel_name;
+ channel->id = silc_id_payload_get_id(idp);
+ channel->mode = mode;
+ silc_list_init(channel->clients, struct SilcChannelUserStruct, next);
- channel_id = silc_id_payload_parse_id(tmp, tmp_len);
- if (!channel_id)
- goto out;
+ conn->current_channel = channel;
- /* Get channel entry */
- if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
- SILC_ID_CHANNEL, &id_cache))
- break;
+ /* Put it to the ID cache */
+ silc_idcache_add(conn->channel_cache, channel_name, SILC_ID_CHANNEL,
+ (void *)channel->id, (void *)channel, TRUE);
- channel = (SilcChannelEntry)id_cache->context;
+ return channel;
+}
- /* Add client to channel */
- chu = silc_calloc(1, sizeof(*chu));
- chu->client = client_entry;
- silc_list_add(channel->clients, chu);
+/* Removes a client entry from all channel it has joined. This really is
+ a performance killer (client_entry should have pointers to channel
+ entry list). */
- /* XXX add support for multiple same nicks on same channel. Check
- for them here */
+void silc_client_remove_from_channels(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry client_entry)
+{
+ SilcIDCacheEntry id_cache;
+ SilcIDCacheList list;
+ SilcChannelEntry channel;
+ SilcChannelUser chu;
- /* Notify application. The channel entry is sent last as this notify
- is for channel but application don't know it from the arguments
- sent by server. */
- client->ops->notify(client, conn, type, client_entry, channel);
- break;
+ if (!silc_idcache_find_by_id(conn->channel_cache, SILC_ID_CACHE_ANY,
+ SILC_ID_CHANNEL, &list))
+ return;
- case SILC_NOTIFY_TYPE_LEAVE:
- /*
- * Someone has left a channel. We will remove it from the channel but
- * we'll keep it in the cache in case we'll need it later.
- */
+ silc_idcache_list_first(list, &id_cache);
+ channel = (SilcChannelEntry)id_cache->context;
+
+ while (channel) {
- /* Get Client ID */
- tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
- if (!tmp)
- goto out;
-
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
- if (!client_id)
- goto out;
-
- /* Find Client entry */
- client_entry =
- silc_client_get_client_by_id(client, conn, client_id);
- if (!client_entry)
- goto out;
-
- /* Get channel entry */
- channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
- SILC_ID_CHANNEL);
- if (!channel_id)
- goto out;
- if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
- SILC_ID_CHANNEL, &id_cache))
- break;
-
- channel = (SilcChannelEntry)id_cache->context;
-
/* Remove client from channel */
silc_list_start(channel->clients);
while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
}
}
- /* Notify application. The channel entry is sent last as this notify
- is for channel but application don't know it from the arguments
- sent by server. */
- client->ops->notify(client, conn, type, client_entry, channel);
- break;
-
- case SILC_NOTIFY_TYPE_SIGNOFF:
- /*
- * Someone left SILC. We'll remove it from all channels and from cache.
- */
-
- /* Get Client ID */
- tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
- if (!tmp)
- goto out;
-
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
- if (!client_id)
- goto out;
-
- /* Find Client entry */
- client_entry =
- silc_client_get_client_by_id(client, conn, client_id);
- if (!client_entry)
- goto out;
-
- /* Remove from all channels */
- silc_client_remove_from_channels(client, conn, client_entry);
-
- /* Remove from cache */
- silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT,
- client_entry->id);
-
- /* Get signoff message */
- tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
- if (tmp_len > 128)
- tmp = NULL;
-
- /* Notify application */
- client->ops->notify(client, conn, type, client_entry, tmp);
-
- /* Free data */
- if (client_entry->nickname)
- silc_free(client_entry->nickname);
- if (client_entry->server)
- silc_free(client_entry->server);
- if (client_entry->id)
- silc_free(client_entry->id);
- if (client_entry->send_key)
- silc_cipher_free(client_entry->send_key);
- if (client_entry->receive_key)
- silc_cipher_free(client_entry->receive_key);
- break;
-
- case SILC_NOTIFY_TYPE_TOPIC_SET:
- /*
- * Someone set the topic on a channel.
- */
-
- /* Get Client ID */
- tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
- if (!tmp)
- goto out;
-
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
- if (!client_id)
- goto out;
-
- /* Find Client entry */
- client_entry =
- silc_client_get_client_by_id(client, conn, client_id);
- if (!client_entry)
- goto out;
-
- /* Get topic */
- tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
- if (!tmp)
- goto out;
-
- /* Get channel entry */
- channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
- SILC_ID_CHANNEL);
- if (!channel_id)
- goto out;
- if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
- SILC_ID_CHANNEL, &id_cache))
+ if (!silc_idcache_list_next(list, &id_cache))
break;
-
+
channel = (SilcChannelEntry)id_cache->context;
+ }
- /* Notify application. The channel entry is sent last as this notify
- is for channel but application don't know it from the arguments
- sent by server. */
- client->ops->notify(client, conn, type, client_entry, tmp, channel);
- break;
-
- case SILC_NOTIFY_TYPE_NICK_CHANGE:
- /*
- * Someone changed their nickname. If we don't have entry for the new
- * ID we will query it and return here after it's done. After we've
- * returned we fetch the old entry and free it and notify the
- * application.
- */
-
- /* Get new Client ID */
- tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
- if (!tmp)
- goto out;
-
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
- if (!client_id)
- goto out;
-
- /* Ignore my ID */
- if (!SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
- break;
+ silc_idcache_list_free(list);
+}
- /* Find Client entry and if not found query it */
- client_entry2 =
- silc_client_get_client_by_id(client, conn, client_id);
- if (!client_entry2) {
- silc_client_notify_by_server_resolve(client, conn, packet, client_id);
- goto out;
- }
- silc_free(client_id);
-
- /* Get old Client ID */
- tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
- if (!tmp)
- goto out;
-
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
- if (!client_id)
- goto out;
-
- /* Find old Client entry */
- client_entry =
- silc_client_get_client_by_id(client, conn, client_id);
- if (!client_entry)
- goto out;
-
- /* Remove the old from cache */
- silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT,
- client_entry->id);
-
- /* Replace old ID entry with new one on all channels. */
- silc_client_replace_from_channels(client, conn, client_entry,
- client_entry2);
-
- /* Notify application */
- client->ops->notify(client, conn, type, client_entry, client_entry2);
-
- /* Free data */
- if (client_entry->nickname)
- silc_free(client_entry->nickname);
- if (client_entry->server)
- silc_free(client_entry->server);
- if (client_entry->id)
- silc_free(client_entry->id);
- if (client_entry->send_key)
- silc_cipher_free(client_entry->send_key);
- if (client_entry->receive_key)
- silc_cipher_free(client_entry->receive_key);
- break;
-
- case SILC_NOTIFY_TYPE_CMODE_CHANGE:
- /*
- * Someone changed a channel mode
- */
-
- /* Get Client ID */
- tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
- if (!tmp)
- goto out;
-
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
- if (!client_id)
- goto out;
-
- /* Find Client entry */
- client_entry =
- silc_client_get_client_by_id(client, conn, client_id);
- if (!client_entry)
- goto out;
-
- /* Get the mode */
- tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
- if (!tmp)
- goto out;
-
- SILC_GET32_MSB(mode, tmp);
-
- /* Get channel entry */
- channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
- SILC_ID_CHANNEL);
- if (!channel_id)
- goto out;
- if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
- SILC_ID_CHANNEL, &id_cache))
- break;
-
- channel = (SilcChannelEntry)id_cache->context;
-
- /* Save the new mode */
- channel->mode = mode;
-
- /* Notify application. The channel entry is sent last as this notify
- is for channel but application don't know it from the arguments
- sent by server. */
- client->ops->notify(client, conn, type, client_entry, mode, channel);
- break;
-
- case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
- /*
- * Someone changed user's mode on a channel
- */
-
- /* Get Client ID */
- tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
- if (!tmp)
- goto out;
-
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
- if (!client_id)
- goto out;
-
- /* Find Client entry */
- client_entry =
- silc_client_get_client_by_id(client, conn, client_id);
- if (!client_entry)
- goto out;
-
- /* Get the mode */
- tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
- if (!tmp)
- goto out;
-
- SILC_GET32_MSB(mode, tmp);
-
- /* Get target Client ID */
- tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
- if (!tmp)
- goto out;
-
- silc_free(client_id);
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
- if (!client_id)
- goto out;
-
- /* Find target Client entry */
- client_entry2 =
- silc_client_get_client_by_id(client, conn, client_id);
- if (!client_entry2)
- goto out;
-
- /* Get channel entry */
- channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
- SILC_ID_CHANNEL);
- if (!channel_id)
- goto out;
- if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
- SILC_ID_CHANNEL, &id_cache))
- break;
-
- channel = (SilcChannelEntry)id_cache->context;
-
- /* Save the mode */
- silc_list_start(channel->clients);
- while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
- if (chu->client == client_entry) {
- chu->mode = mode;
- break;
- }
- }
-
- /* Notify application. The channel entry is sent last as this notify
- is for channel but application don't know it from the arguments
- sent by server. */
- client->ops->notify(client, conn, type, client_entry, mode,
- client_entry2, channel);
- break;
-
- case SILC_NOTIFY_TYPE_MOTD:
- /*
- * Received Message of the day
- */
-
- /* Get motd */
- tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
- if (!tmp)
- goto out;
-
- /* Notify application */
- client->ops->notify(client, conn, type, tmp);
- break;
-
- case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
- /*
- * Router has enforced a new ID to a channel. Let's change the old
- * ID to the one provided here.
- */
-
- /* Get the old ID */
- tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
- if (!tmp)
- goto out;
- channel_id = silc_id_payload_parse_id(tmp, tmp_len);
- if (!channel_id)
- goto out;
-
- /* Get the channel entry */
- if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
- SILC_ID_CHANNEL, &id_cache))
- break;
-
- channel = (SilcChannelEntry)id_cache->context;
-
- /* Free the old ID */
- silc_free(channel_id);
- silc_free(channel->id);
-
- /* Get the new ID */
- tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
- if (!tmp)
- goto out;
- channel->id = silc_id_payload_parse_id(tmp, tmp_len);
- if (!channel->id)
- goto out;
-
- id_cache->id = (void *)channel->id;
-
- /* Notify application */
- client->ops->notify(client, conn, type, channel, channel);
- break;
-
- case SILC_NOTIFY_TYPE_KICKED:
- /*
- * A client (maybe me) was kicked from a channel
- */
-
- /* Get Client ID */
- tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
- if (!tmp)
- goto out;
-
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
- if (!client_id)
- goto out;
-
- /* Find Client entry */
- client_entry =
- silc_client_get_client_by_id(client, conn, client_id);
- if (!client_entry)
- goto out;
-
- /* Get channel entry */
- channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
- SILC_ID_CHANNEL);
- if (!channel_id)
- goto out;
- if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
- SILC_ID_CHANNEL, &id_cache))
- break;
-
- channel = (SilcChannelEntry)id_cache->context;
-
- /* Get comment */
- tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-
- /* Notify application. The channel entry is sent last as this notify
- is for channel but application don't know it from the arguments
- sent by server. */
- client->ops->notify(client, conn, type, client_entry, tmp, channel);
-
- /* If I was kicked from channel, remove the channel */
- if (client_entry == conn->local_entry) {
- if (conn->current_channel == channel)
- conn->current_channel = NULL;
- silc_idcache_del_by_id(conn->channel_cache,
- SILC_ID_CHANNEL, channel->id);
- silc_free(channel->channel_name);
- silc_free(channel->id);
- silc_free(channel->key);
- silc_cipher_free(channel->channel_key);
- silc_free(channel);
- }
- break;
-
- default:
- break;
- }
-
- out:
- silc_notify_payload_free(payload);
- if (client_id)
- silc_free(client_id);
- if (channel_id)
- silc_free(channel_id);
-}
-
-/* Processes the received new Client ID from server. Old Client ID is
- deleted from cache and new one is added. */
-
-void silc_client_receive_new_id(SilcClient client,
- SilcSocketConnection sock,
- SilcIDPayload idp)
-{
- SilcClientConnection conn = (SilcClientConnection)sock->user_data;
- int connecting = FALSE;
-
- if (!conn->local_entry)
- connecting = TRUE;
-
- /* Delete old ID from ID cache */
- silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, conn->local_id);
-
- /* Save the new ID */
- if (conn->local_id)
- silc_free(conn->local_id);
- if (conn->local_id_data)
- silc_free(conn->local_id_data);
-
- conn->local_id = silc_id_payload_get_id(idp);
- conn->local_id_data = silc_id_payload_get_data(idp);
- conn->local_id_data_len = silc_id_payload_get_len(idp);;
-
- if (!conn->local_entry)
- conn->local_entry = silc_calloc(1, sizeof(*conn->local_entry));
-
- conn->local_entry->nickname = conn->nickname;
- if (!conn->local_entry->username) {
- conn->local_entry->username =
- silc_calloc(strlen(client->username) + strlen(client->hostname) + 1,
- sizeof(conn->local_entry->username));
- sprintf(conn->local_entry->username, "%s@%s", client->username,
- client->hostname);
- }
- conn->local_entry->server = strdup(conn->remote_host);
- conn->local_entry->id = conn->local_id;
-
- /* Put it to the ID cache */
- silc_idcache_add(conn->client_cache, conn->nickname, SILC_ID_CLIENT,
- conn->local_id, (void *)conn->local_entry, TRUE);
-
- /* Notify application of successful connection. We do it here now that
- we've received the Client ID and are allowed to send traffic. */
- if (connecting)
- client->ops->connect(client, conn, TRUE);
-}
-
-/* Processed received Channel ID for a channel. This is called when client
- joins to channel and server replies with channel ID. The ID is cached. */
-
-void silc_client_new_channel_id(SilcClient client,
- SilcSocketConnection sock,
- char *channel_name,
- unsigned int mode, SilcIDPayload idp)
-{
- SilcClientConnection conn = (SilcClientConnection)sock->user_data;
- SilcChannelEntry channel;
-
- SILC_LOG_DEBUG(("New channel ID"));
-
- channel = silc_calloc(1, sizeof(*channel));
- channel->channel_name = channel_name;
- channel->id = silc_id_payload_get_id(idp);
- channel->mode = mode;
- silc_list_init(channel->clients, struct SilcChannelUserStruct, next);
-
- conn->current_channel = channel;
-
- /* Put it to the ID cache */
- silc_idcache_add(conn->channel_cache, channel_name, SILC_ID_CHANNEL,
- (void *)channel->id, (void *)channel, TRUE);
-}
-
-/* Saves channel key from encoded `key_payload'. This is used when we
- receive Channel Key Payload and when we are processing JOIN command
- reply. */
-
-void silc_client_save_channel_key(SilcClientConnection conn,
- SilcBuffer key_payload,
- SilcChannelEntry channel)
-{
- unsigned char *id_string, *key, *cipher;
- unsigned int tmp_len;
- SilcChannelID *id;
- SilcIDCacheEntry id_cache = NULL;
- SilcChannelKeyPayload payload;
-
- payload = silc_channel_key_payload_parse(key_payload);
- if (!payload)
- return;
-
- id_string = silc_channel_key_get_id(payload, &tmp_len);
- if (!id_string) {
- silc_channel_key_payload_free(payload);
- return;
- }
-
- id = silc_id_str2id(id_string, tmp_len, SILC_ID_CHANNEL);
- if (!id) {
- silc_channel_key_payload_free(payload);
- return;
- }
-
- /* Find channel. */
- if (!channel) {
- if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id,
- SILC_ID_CHANNEL, &id_cache))
- goto out;
-
- /* Get channel entry */
- channel = (SilcChannelEntry)id_cache->context;
- }
-
- /* Save the key */
- key = silc_channel_key_get_key(payload, &tmp_len);
- cipher = silc_channel_key_get_cipher(payload, NULL);
- channel->key_len = tmp_len * 8;
- channel->key = silc_calloc(tmp_len, sizeof(*channel->key));
- memcpy(channel->key, key, tmp_len);
-
- if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
- conn->client->ops->say(conn->client, conn,
- "Cannot talk to channel: unsupported cipher %s", cipher);
- goto out;
- }
- channel->channel_key->cipher->set_key(channel->channel_key->context,
- key, channel->key_len);
-
- /* Client is now joined to the channel */
- channel->on_channel = TRUE;
-
- out:
- silc_free(id);
- silc_channel_key_payload_free(payload);
-}
-
-/* Processes received key for channel. The received key will be used
- to protect the traffic on the channel for now on. Client must receive
- the key to the channel before talking on the channel is possible.
- This is the key that server has generated, this is not the channel
- private key, it is entirely local setting. */
-
-void silc_client_receive_channel_key(SilcClient client,
- SilcSocketConnection sock,
- SilcBuffer packet)
-{
- SILC_LOG_DEBUG(("Received key for channel"));
-
- /* Save the key */
- silc_client_save_channel_key(sock->user_data, packet, NULL);
-}
-
-/* Process received message to a channel (or from a channel, really). This
- decrypts the channel message with channel specific key and parses the
- channel payload. Finally it displays the message on the screen. */
-
-void silc_client_channel_message(SilcClient client,
- SilcSocketConnection sock,
- SilcPacketContext *packet)
-{
- SilcClientConnection conn = (SilcClientConnection)sock->user_data;
- SilcBuffer buffer = packet->buffer;
- SilcChannelPayload payload = NULL;
- SilcChannelID *id = NULL;
- SilcChannelEntry channel;
- SilcChannelUser chu;
- SilcIDCacheEntry id_cache = NULL;
- SilcClientID *client_id = NULL;
- int found = FALSE;
- unsigned int block_len;
-
- SILC_LOG_DEBUG(("Start"));
-
- /* Sanity checks */
- if (packet->dst_id_type != SILC_ID_CHANNEL)
- goto out;
-
- client_id = silc_id_str2id(packet->src_id, packet->src_id_len,
- SILC_ID_CLIENT);
- if (!client_id)
- goto out;
- id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL);
- if (!id)
- goto out;
-
- /* Find the channel entry from channels on this connection */
- if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id,
- SILC_ID_CHANNEL, &id_cache))
- goto out;
-
- channel = (SilcChannelEntry)id_cache->context;
-
- /* Decrypt the channel message payload. Push the IV out of the way,
- since it is not encrypted (after pushing buffer->tail has the IV). */
- block_len = silc_cipher_get_block_len(channel->channel_key);
- silc_buffer_push_tail(buffer, block_len);
- channel->channel_key->cipher->decrypt(channel->channel_key->context,
- buffer->data, buffer->data,
- buffer->len, buffer->tail);
- silc_buffer_pull_tail(buffer, block_len);
-
- /* Parse the channel message payload */
- payload = silc_channel_payload_parse(buffer);
- if (!payload)
- goto out;
-
- /* Find client entry */
- silc_list_start(channel->clients);
- while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
- if (!SILC_ID_CLIENT_COMPARE(chu->client->id, client_id)) {
- found = TRUE;
- break;
- }
- }
-
- /* Pass the message to application */
- client->ops->channel_message(client, conn, found ? chu->client : NULL,
- channel, silc_channel_get_data(payload, NULL));
-
- out:
- if (id)
- silc_free(id);
- if (client_id)
- silc_free(client_id);
- if (payload)
- silc_channel_payload_free(payload);
-}
-
-/* Private message received. This processes the private message and
- finally displays it on the screen. */
-
-void silc_client_private_message(SilcClient client,
- SilcSocketConnection sock,
- SilcPacketContext *packet)
-{
- SilcClientConnection conn = (SilcClientConnection)sock->user_data;
- SilcBuffer buffer = packet->buffer;
- SilcIDCacheEntry id_cache;
- SilcClientID *remote_id = NULL;
- SilcClientEntry remote_client;
- unsigned short nick_len;
- unsigned char *nickname, *message = NULL;
- int ret;
-
- if (packet->src_id_type != SILC_ID_CLIENT)
- goto out;
-
- /* Get nickname */
- ret = silc_buffer_unformat(buffer,
- SILC_STR_UI16_NSTRING_ALLOC(&nickname, &nick_len),
- SILC_STR_END);
- if (ret == -1)
- return;
-
- silc_buffer_pull(buffer, 2 + nick_len);
-
- message = silc_calloc(buffer->len + 1, sizeof(char));
- memcpy(message, buffer->data, buffer->len);
-
- remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
- SILC_ID_CLIENT);
- if (!remote_id)
- goto out;
-
- /* Check whether we know this client already */
- if (!silc_idcache_find_by_id_one(conn->client_cache, remote_id,
- SILC_ID_CLIENT, &id_cache))
- {
- /* Allocate client entry */
- remote_client = silc_calloc(1, sizeof(*remote_client));
- remote_client->id = remote_id;
- silc_parse_nickname(nickname, &remote_client->nickname,
- &remote_client->server, &remote_client->num);
-
- /* Save the client to cache */
- silc_idcache_add(conn->client_cache, remote_client->nickname,
- SILC_ID_CLIENT, remote_client->id, remote_client,
- TRUE);
- } else {
- remote_client = (SilcClientEntry)id_cache->context;
- }
-
- /* Pass the private message to application */
- client->ops->private_message(client, conn, remote_client, message);
-
- /* See if we are away (gone). If we are away we will reply to the
- sender with the set away message. */
- if (conn->away && conn->away->away) {
- /* If it's me, ignore */
- if (!SILC_ID_CLIENT_COMPARE(remote_id, conn->local_id))
- goto out;
-
- /* Send the away message */
- silc_client_send_private_message(client, conn, remote_client,
- conn->away->away,
- strlen(conn->away->away), TRUE);
- }
-
- out:
- if (remote_id)
- silc_free(remote_id);
-
- if (message) {
- memset(message, 0, buffer->len);
- silc_free(message);
- }
- silc_free(nickname);
-}
-
-/* Removes a client entry from all channel it has joined. This really is
- a performance killer (client_entry should have pointers to channel
- entry list). */
-
-void silc_client_remove_from_channels(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry client_entry)
-{
- SilcIDCacheEntry id_cache;
- SilcIDCacheList list;
- SilcChannelEntry channel;
- SilcChannelUser chu;
-
- if (!silc_idcache_find_by_id(conn->channel_cache, SILC_ID_CACHE_ANY,
- SILC_ID_CHANNEL, &list))
- return;
-
- silc_idcache_list_first(list, &id_cache);
- channel = (SilcChannelEntry)id_cache->context;
-
- while (channel) {
-
- /* Remove client from channel */
- silc_list_start(channel->clients);
- while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
- if (chu->client == client_entry) {
- silc_list_del(channel->clients, chu);
- silc_free(chu);
- break;
- }
- }
-
- if (!silc_idcache_list_next(list, &id_cache))
- break;
-
- channel = (SilcChannelEntry)id_cache->context;
- }
-
- silc_idcache_list_free(list);
-}
-
-/* Replaces `old' client entries from all channels to `new' client entry.
- This can be called for example when nickname changes and old ID entry
- is replaced from ID cache with the new one. If the old ID entry is only
- updated, then this fucntion needs not to be called. */
+/* Replaces `old' client entries from all channels to `new' client entry.
+ This can be called for example when nickname changes and old ID entry
+ is replaced from ID cache with the new one. If the old ID entry is only
+ updated, then this fucntion needs not to be called. */
void silc_client_replace_from_channels(SilcClient client,
SilcClientConnection conn,
return strdup(string);
}
-/* Function that actually employes the received private message key */
-
-static void silc_client_private_message_key_cb(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry *clients,
- unsigned int clients_count,
- void *context)
-{
- SilcPacketContext *packet = (SilcPacketContext *)context;
- unsigned char *key;
- unsigned short key_len;
- unsigned char *cipher;
- int ret;
-
- if (!clients)
- goto out;
-
- /* Parse the private message key payload */
- ret = silc_buffer_unformat(packet->buffer,
- SILC_STR_UI16_NSTRING(&key, &key_len),
- SILC_STR_UI16_STRING(&cipher),
- SILC_STR_END);
- if (!ret)
- goto out;
-
- if (key_len > packet->buffer->len)
- goto out;
-
- /* Now take the key in use */
- if (!silc_client_add_private_message_key(client, conn, clients[0],
- cipher, key, key_len, FALSE))
- goto out;
-
- /* Print some info for application */
- client->ops->say(client, conn,
- "Received private message key from %s%s%s %s%s%s",
- clients[0]->nickname,
- clients[0]->server ? "@" : "",
- clients[0]->server ? clients[0]->server : "",
- clients[0]->username ? "(" : "",
- clients[0]->username ? clients[0]->username : "",
- clients[0]->username ? ")" : "");
-
- out:
- silc_packet_context_free(packet);
-}
-
-/* Processes incoming Private Message Key payload. The libary always
- accepts the key and takes it into use. */
-
-void silc_client_private_message_key(SilcClient client,
- SilcSocketConnection sock,
- SilcPacketContext *packet)
-{
- SilcClientID *remote_id;
-
- if (packet->src_id_type != SILC_ID_CLIENT)
- return;
-
- remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
- SILC_ID_CLIENT);
- if (!remote_id)
- return;
-
- silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
- silc_client_private_message_key_cb,
- silc_packet_context_dup(packet));
- silc_free(remote_id);
-}
-
-/* Adds private message key to the client library. The key will be used to
- encrypt all private message between the client and the remote client
- indicated by the `client_entry'. If the `key' is NULL and the boolean
- value `generate_key' is TRUE the library will generate random key.
- The `key' maybe for example pre-shared-key, passphrase or similar.
- The `cipher' MAY be provided but SHOULD be NULL to assure that the
- requirements of the SILC protocol are met. The API, however, allows
- to allocate any cipher.
-
- It is not necessary to set key for normal private message usage. If the
- key is not set then the private messages are encrypted using normal
- session keys. Setting the private key, however, increases the security.
-
- Returns FALSE if the key is already set for the `client_entry', TRUE
- otherwise. */
-
-int silc_client_add_private_message_key(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry client_entry,
- char *cipher,
- unsigned char *key,
- unsigned int key_len,
- int generate_key)
-{
- unsigned char private_key[32];
- unsigned int len;
- int i;
- SilcSKEKeyMaterial *keymat;
-
- assert(client_entry);
-
- /* Return FALSE if key already set */
- if (client_entry->send_key && client_entry->receive_key)
- return FALSE;
-
- if (!cipher)
- cipher = "aes-256-cbc";
-
- /* Check the requested cipher */
- if (!silc_cipher_is_supported(cipher))
- return FALSE;
-
- /* Generate key if not provided */
- if (!key && generate_key == TRUE) {
- len = 32;
- for (i = 0; i < len; i++) private_key[i] = silc_rng_get_byte(client->rng);
- key = private_key;
- key_len = len;
- client_entry->generated = TRUE;
- }
-
- /* Save the key */
- client_entry->key = silc_calloc(key_len, sizeof(*client_entry->key));
- memcpy(client_entry->key, key, key_len);
- client_entry->key_len = key_len;
-
- /* Produce the key material as the protocol defines */
- keymat = silc_calloc(1, sizeof(*keymat));
- if (silc_ske_process_key_material_data(key, key_len, 16, 256, 16,
- client->md5hash, keymat)
- != SILC_SKE_STATUS_OK)
- return FALSE;
-
- /* Allocate the ciphers */
- silc_cipher_alloc(cipher, &client_entry->send_key);
- silc_cipher_alloc(cipher, &client_entry->receive_key);
-
- /* Set the keys */
- silc_cipher_set_key(client_entry->send_key, keymat->send_enc_key,
- keymat->enc_key_len);
- silc_cipher_set_iv(client_entry->send_key, keymat->send_iv);
- silc_cipher_set_key(client_entry->receive_key, keymat->receive_enc_key,
- keymat->enc_key_len);
- silc_cipher_set_iv(client_entry->receive_key, keymat->receive_iv);
-
- /* Free the key material */
- silc_ske_free_key_material(keymat);
-
- return TRUE;
-}
-
-/* Same as above but takes the key material from the SKE key material
- structure. This structure is received if the application uses the
- silc_client_send_key_agreement to negotiate the key material. The
- `cipher' SHOULD be provided as it is negotiated also in the SKE
- protocol. */
-
-int silc_client_add_private_message_key_ske(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry client_entry,
- char *cipher,
- SilcSKEKeyMaterial *key)
-{
- assert(client_entry);
-
- /* Return FALSE if key already set */
- if (client_entry->send_key && client_entry->receive_key)
- return FALSE;
-
- if (!cipher)
- cipher = "aes-256-cbc";
-
- /* Check the requested cipher */
- if (!silc_cipher_is_supported(cipher))
- return FALSE;
-
- /* Allocate the ciphers */
- silc_cipher_alloc(cipher, &client_entry->send_key);
- silc_cipher_alloc(cipher, &client_entry->receive_key);
-
- /* Set the keys */
- silc_cipher_set_key(client_entry->send_key, key->send_enc_key,
- key->enc_key_len);
- silc_cipher_set_iv(client_entry->send_key, key->send_iv);
- silc_cipher_set_key(client_entry->receive_key, key->receive_enc_key,
- key->enc_key_len);
- silc_cipher_set_iv(client_entry->receive_key, key->receive_iv);
+/* Failure timeout callback. If this is called then we will immediately
+ process the received failure. We always process the failure with timeout
+ since we do not want to blindly trust to received failure packets.
+ This won't be called (the timeout is cancelled) if the failure was
+ bogus (it is bogus if remote does not close the connection after sending
+ the failure). */
- return TRUE;
-}
-
-/* Sends private message key payload to the remote client indicated by
- the `client_entry'. If the `force_send' is TRUE the packet is sent
- immediately. Returns FALSE if error occurs, TRUE otherwise. The
- application should call this function after setting the key to the
- client.
-
- Note that the key sent using this function is sent to the remote client
- through the SILC network. The packet is protected using normal session
- keys. */
-
-int silc_client_send_private_message_key(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry client_entry,
- int force_send)
+SILC_TASK_CALLBACK_GLOBAL(silc_client_failure_callback)
{
- SilcSocketConnection sock = conn->sock;
- SilcBuffer buffer;
- int cipher_len;
+ SilcClientFailureContext *f = (SilcClientFailureContext *)context;
- if (!client_entry->send_key || !client_entry->key)
- return FALSE;
-
- SILC_LOG_DEBUG(("Sending private message key"));
-
- cipher_len = strlen(client_entry->send_key->cipher->name);
-
- /* Create private message key payload */
- buffer = silc_buffer_alloc(2 + client_entry->key_len);
- silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
- silc_buffer_format(buffer,
- SILC_STR_UI_SHORT(client_entry->key_len),
- SILC_STR_UI_XNSTRING(client_entry->key,
- client_entry->key_len),
- SILC_STR_UI_SHORT(cipher_len),
- SILC_STR_UI_XNSTRING(client_entry->send_key->cipher->name,
- cipher_len),
- SILC_STR_END);
-
- /* Send the packet */
- silc_client_packet_send(client, sock, SILC_PACKET_PRIVATE_MESSAGE_KEY,
- client_entry->id, SILC_ID_CLIENT, NULL, NULL,
- buffer->data, buffer->len, force_send);
- silc_free(buffer);
-
- return TRUE;
-}
-
-/* Removes the private message from the library. The key won't be used
- after this to protect the private messages with the remote `client_entry'
- client. Returns FALSE on error, TRUE otherwise. */
-
-int silc_client_del_private_message_key(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry client_entry)
-{
- assert(client_entry);
-
- if (!client_entry->send_key && !client_entry->receive_key)
- return FALSE;
-
- silc_cipher_free(client_entry->send_key);
- silc_cipher_free(client_entry->receive_key);
-
- if (client_entry->key) {
- memset(client_entry->key, 0, client_entry->key_len);
- silc_free(client_entry->key);
+ if (f->sock->protocol) {
+ f->sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
+ f->sock->protocol->execute(f->client->timeout_queue, 0,
+ f->sock->protocol, f->sock->sock, 0, 0);
+
+ /* Notify application */
+ f->client->ops->failure(f->client, f->sock->user_data, f->sock->protocol,
+ (void *)f->failure);
}
- client_entry->send_key = NULL;
- client_entry->receive_key = NULL;
- client_entry->key = NULL;
-
- return TRUE;
+ silc_free(f);
}
-/* Returns array of set private message keys associated to the connection
- `conn'. Returns allocated SilcPrivateMessageKeys array and the array
- count to the `key_count' argument. The array must be freed by the caller
- by calling the silc_client_free_private_message_keys function. Note:
- the keys returned in the array is in raw format. It might not be desired
- to show the keys as is. The application might choose not to show the keys
- at all or to show the fingerprints of the keys. */
+/* Registers failure timeout to process the received failure packet
+ with timeout. */
-SilcPrivateMessageKeys
-silc_client_list_private_message_keys(SilcClient client,
- SilcClientConnection conn,
- unsigned int *key_count)
+void silc_client_process_failure(SilcClient client,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
{
- SilcPrivateMessageKeys keys;
- unsigned int count = 0;
- SilcIDCacheEntry id_cache;
- SilcIDCacheList list;
- SilcClientEntry entry;
-
- if (!silc_idcache_find_by_id(conn->client_cache, SILC_ID_CACHE_ANY,
- SILC_ID_CLIENT, &list))
- return NULL;
-
- if (!silc_idcache_list_count(list)) {
- silc_idcache_list_free(list);
- return NULL;
- }
-
- keys = silc_calloc(silc_idcache_list_count(list), sizeof(*keys));
-
- silc_idcache_list_first(list, &id_cache);
- while (id_cache) {
- entry = (SilcClientEntry)id_cache->context;
-
- if (entry->send_key) {
- keys[count].client_entry = entry;
- keys[count].cipher = entry->send_key->cipher->name;
- keys[count].key = entry->generated == FALSE ? entry->key : NULL;
- keys[count].key_len = entry->generated == FALSE ? entry->key_len : 0;
- count++;
- }
+ SilcClientFailureContext *f;
+ unsigned int failure = 0;
- if (!silc_idcache_list_next(list, &id_cache))
- break;
+ if (sock->protocol) {
+ if (packet->buffer->len >= 4)
+ SILC_GET32_MSB(failure, packet->buffer->data);
+
+ f = silc_calloc(1, sizeof(*f));
+ f->client = client;
+ f->sock = sock;
+ f->failure = failure;
+
+ /* We will wait 5 seconds to process this failure packet */
+ silc_task_register(client->timeout_queue, sock->sock,
+ silc_client_failure_callback, (void *)f, 5, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
}
-
- if (key_count)
- *key_count = count;
-
- return keys;
-}
-
-/* Frees the SilcPrivateMessageKeys array returned by the function
- silc_client_list_private_message_keys. */
-
-void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
- unsigned int key_count)
-{
- silc_free(keys);
-}
-
-/* Adds private key for channel. This may be set only if the channel's mode
- mask includes the SILC_CHANNEL_MODE_PRIVKEY. This returns FALSE if the
- mode is not set. When channel has private key then the messages are
- encrypted using that key. All clients on the channel must also know the
- key in order to decrypt the messages. However, it is possible to have
- several private keys per one channel. In this case only some of the
- clients on the channel may now the one key and only some the other key.
-
- The private key for channel is optional. If it is not set then the
- channel messages are encrypted using the channel key generated by the
- server. However, setting the private key (or keys) for the channel
- significantly adds security. If more than one key is set the library
- will automatically try all keys at the message decryption phase. Note:
- setting many keys slows down the decryption phase as all keys has to
- be tried in order to find the correct decryption key. However, setting
- a few keys does not have big impact to the decryption performace.
-
- NOTE: that this is entirely local setting. The key set using this function
- is not sent to the network at any phase.
-
- NOTE: If the key material was originated by the SKE protocol (using
- silc_client_send_key_agreement) then the `key' MUST be the
- key->send_enc_key as this is dictated by the SILC protocol. However,
- currently it is not expected that the SKE key material would be used
- as channel private key. However, this API allows it. */
-
-int silc_client_add_channel_private_key(SilcClient client,
- SilcClientConnection conn,
- SilcChannelEntry channel,
- char *cipher,
- unsigned char *key,
- unsigned int key_len)
-{
-
- return TRUE;
-}
-
-/* Removes all private keys from the `channel'. The old channel key is used
- after calling this to protect the channel messages. Returns FALSE on
- on error, TRUE otherwise. */
-
-int silc_client_del_channel_private_keys(SilcClient client,
- SilcClientConnection conn,
- SilcChannelEntry channel)
-{
-
- return TRUE;
-}
-
-/* Removes and frees private key `key' from the channel `channel'. The `key'
- is retrieved by calling the function silc_client_list_channel_private_keys.
- The key is not used after this. If the key was last private key then the
- old channel key is used hereafter to protect the channel messages. This
- returns FALSE on error, TRUE otherwise. */
-
-int silc_client_del_channel_private_key(SilcClient client,
- SilcClientConnection conn,
- SilcChannelEntry channel,
- SilcChannelPrivateKey key)
-{
-
- return TRUE;
-}
-
-/* Returns array (pointers) of private keys associated to the `channel'.
- The caller must free the array by calling the function
- silc_client_free_channel_private_keys. The pointers in the array may be
- used to delete the specific key by giving the pointer as argument to the
- function silc_client_del_channel_private_key. */
-
-SilcChannelPrivateKey *
-silc_client_list_channel_private_keys(SilcClient client,
- SilcClientConnection conn,
- SilcChannelEntry channel,
- unsigned int key_count)
-{
-
- return NULL;
-}
-
-/* Frees the SilcChannelPrivateKey array. */
-
-void silc_client_free_channel_private_keys(SilcChannelPrivateKey *keys,
- unsigned int key_count)
-{
-
-}
-
-/* Sends key agreement request to the remote client indicated by the
- `client_entry'. If the caller provides the `hostname' and the `port'
- arguments then the library will bind the client to that hostname and
- that port for the key agreement protocol. It also sends the `hostname'
- and the `port' in the key agreement packet to the remote client. This
- would indicate that the remote client may initiate the key agreement
- protocol to the `hostname' on the `port'.
-
- If the `hostname' and `port' is not provided then empty key agreement
- packet is sent to the remote client. The remote client may reply with
- the same packet including its hostname and port. If the library receives
- the reply from the remote client the `key_agreement' client operation
- callback will be called to verify whether the user wants to perform the
- key agreement or not.
-
- NOTE: If the application provided the `hostname' and the `port' and the
- remote side initiates the key agreement protocol it is not verified
- from the user anymore whether the protocol should be executed or not.
- By setting the `hostname' and `port' the user gives permission to
- perform the protocol (we are responder in this case).
-
- NOTE: If the remote side decides not to initiate the key agreement
- or decides not to reply with the key agreement packet then we cannot
- perform the key agreement at all. If the key agreement protocol is
- performed the `completion' callback with the `context' will be called.
- If remote side decides to ignore the request the `completion' will never
- be called and the caller is responsible of freeing the `context' memory.
- The application can do this by setting, for example, timeout. */
-
-void silc_client_send_key_agreement(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry client_entry,
- char *hostname,
- int port,
- SilcKeyAgreementCallback completion,
- void *context)
-{
-
-}
-
-/* Performs the actual key agreement protocol. Application may use this
- to initiate the key agreement protocol. This can be called for example
- after the application has received the `key_agreement' client operation,
- and did not return TRUE from it.
-
- The `hostname' is the remote hostname (or IP address) and the `port'
- is the remote port. The `completion' callblack with the `context' will
- be called after the key agreement protocol.
-
- NOTE: If the application returns TRUE in the `key_agreement' client
- operation the library will automatically start the key agreement. In this
- case the application must not call this function. However, application
- may choose to just ignore the `key_agreement' client operation (and
- merely just print information about it on the screen) and call this
- function when the user whishes to do so (by, for example, giving some
- specific command). Thus, the API provides both, automatic and manual
- initiation of the key agreement. Calling this function is the manual
- initiation and returning TRUE in the `key_agreement' client operation
- is the automatic initiation. */
-
-void silc_client_perform_key_agreement(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry client_entry,
- char *hostname,
- int port,
- SilcKeyAgreementCallback completion,
- void *context)
-{
-
-}
-
-/* This function can be called to unbind the hostname and the port for
- the key agreement protocol. However, this function has effect only
- before the key agreement protocol has been performed. After it has
- been performed the library will automatically unbind the port. The
- `client_entry' is the client to which we sent the key agreement
- request. */
-
-void silc_client_abort_key_agreement(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry client_entry)
-{
-
}
#ifndef CLIENT_H
#define CLIENT_H
-/* Forward declaration for client */
-typedef struct SilcClientObject *SilcClient;
-
-/* Forward declaration for client connection */
-typedef struct SilcClientConnectionObject *SilcClientConnection;
+/* Forward declarations */
+typedef struct SilcClientStruct *SilcClient;
+typedef struct SilcClientConnectionStruct *SilcClientConnection;
+typedef struct SilcClientPingStruct SilcClientPing;
+typedef struct SilcClientAwayStruct SilcClientAway;
+typedef struct SilcClientKeyAgreementStruct *SilcClientKeyAgreement;
#include "idlist.h"
#include "command.h"
#include "silcapi.h"
-/* Structure to hold ping time information. Every PING command will
- add entry of this structure and is removed after reply to the ping
- as been received. */
-typedef struct SilcClientPingStruct {
- time_t start_time;
- void *dest_id;
- char *dest_name;
-} SilcClientPing;
-
-/* Structure to hold away messages set by user. This is mainly created
- for future extensions where away messages could be set according filters
- such as nickname and hostname. For now only one away message can
- be set in one connection. */
-typedef struct SilcClientAwayStruct {
- char *away;
- struct SilcClientAwayStruct *next;
-} SilcClientAway;
-
/* Connection structure used in client to associate all the important
connection specific data to this structure. */
-struct SilcClientConnectionObject {
+struct SilcClientConnectionStruct {
/*
* Local data
*/
};
/* Main client structure. */
-struct SilcClientObject {
+struct SilcClientStruct {
/*
* Public data. All the following pointers must be set by the allocator
* of this structure.
void silc_client_error_by_server(SilcClient client,
SilcSocketConnection sock,
SilcBuffer message);
-void silc_client_notify_by_server(SilcClient client,
- SilcSocketConnection sock,
- SilcPacketContext *packet);
void silc_client_receive_new_id(SilcClient client,
SilcSocketConnection sock,
SilcIDPayload idp);
-void silc_client_new_channel_id(SilcClient client,
- SilcSocketConnection sock,
- char *channel_name,
- unsigned int mode, SilcIDPayload idp);
+SilcChannelEntry silc_client_new_channel_id(SilcClient client,
+ SilcSocketConnection sock,
+ char *channel_name,
+ unsigned int mode,
+ SilcIDPayload idp);
void silc_client_save_channel_key(SilcClientConnection conn,
SilcBuffer key_payload,
SilcChannelEntry channel);
void silc_client_channel_message(SilcClient client,
SilcSocketConnection sock,
SilcPacketContext *packet);
-void silc_client_private_message(SilcClient client,
- SilcSocketConnection sock,
- SilcPacketContext *packet);
void silc_client_remove_from_channels(SilcClient client,
SilcClientConnection conn,
SilcClientEntry client_entry);
char *silc_client_chmode(unsigned int mode);
char *silc_client_chumode(unsigned int mode);
char *silc_client_chumode_char(unsigned int mode);
-
+void silc_client_process_failure(SilcClient client,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet);
+void silc_client_key_agreement(SilcClient client,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet);
+void silc_client_notify_by_server(SilcClient client,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet);
+void silc_client_private_message(SilcClient client,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet);
#endif
--- /dev/null
+/*
+
+ client_channel.c
+
+ Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+ Copyright (C) 1997 - 2001 Pekka Riikonen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+*/
+/* $Id$ */
+/* This file includes channel message sending and receiving routines,
+ channel key receiving and setting, and channel private key handling
+ routines. */
+
+#include "clientlibincludes.h"
+#include "client_internal.h"
+
+/* Sends packet to the `channel'. Packet to channel is always encrypted
+ differently from "normal" packets. SILC header of the packet is
+ encrypted with the next receiver's key and the rest of the packet is
+ encrypted with the channel specific key. Padding and HMAC is computed
+ with the next receiver's key. The `data' is the channel message. If
+ the `force_send' is TRUE then the packet is sent immediately. */
+
+void silc_client_send_channel_message(SilcClient client,
+ SilcClientConnection conn,
+ SilcChannelEntry channel,
+ unsigned char *data,
+ unsigned int data_len,
+ int force_send)
+{
+ int i;
+ SilcSocketConnection sock = conn->sock;
+ SilcBuffer payload;
+ SilcPacketContext packetdata;
+ SilcCipher cipher;
+ SilcHmac hmac;
+ unsigned char *id_string;
+ unsigned int iv_len;
+
+ SILC_LOG_DEBUG(("Sending packet to channel"));
+
+ if (!channel || !channel->key || !channel->hmac) {
+ client->ops->say(client, conn,
+ "Cannot talk to channel: key does not exist");
+ return;
+ }
+
+ /* Generate IV */
+ iv_len = silc_cipher_get_block_len(channel->channel_key);
+ if (channel->iv[0] == '\0')
+ for (i = 0; i < iv_len; i++) channel->iv[i] =
+ silc_rng_get_byte(client->rng);
+ else
+ silc_hash_make(client->md5hash, channel->iv, iv_len, channel->iv);
+
+ /* Encode the channel payload. This also encrypts the message payload. */
+ payload = silc_channel_payload_encode(data_len, data, iv_len,
+ channel->iv, channel->channel_key,
+ channel->hmac, client->rng);
+
+ /* Get data used in packet header encryption, keys and stuff. */
+ cipher = conn->send_key;
+ hmac = conn->hmac;
+ id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+
+ /* Set the packet context pointers. The destination ID is always
+ the Channel ID of the channel. Server and router will handle the
+ distribution of the packet. */
+ packetdata.flags = 0;
+ packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
+ packetdata.src_id = conn->local_id_data;
+ packetdata.src_id_len = SILC_ID_CLIENT_LEN;
+ packetdata.src_id_type = SILC_ID_CLIENT;
+ packetdata.dst_id = id_string;
+ packetdata.dst_id_len = SILC_ID_CHANNEL_LEN;
+ packetdata.dst_id_type = SILC_ID_CHANNEL;
+ packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len + packetdata.dst_id_len;
+ packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len +
+ packetdata.dst_id_len));
+
+ /* Prepare outgoing data buffer for packet sending */
+ silc_packet_send_prepare(sock,
+ SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len +
+ packetdata.dst_id_len,
+ packetdata.padlen,
+ payload->len);
+
+ packetdata.buffer = sock->outbuf;
+
+ /* Put the channel message payload to the outgoing data buffer */
+ silc_buffer_put(sock->outbuf, payload->data, payload->len);
+
+ /* Create the outgoing packet */
+ silc_packet_assemble(&packetdata);
+
+ /* Encrypt the header and padding of the packet. This is encrypted
+ with normal session key shared with our server. */
+ silc_packet_encrypt(cipher, hmac, sock->outbuf, SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len + packetdata.dst_id_len +
+ packetdata.padlen);
+
+ SILC_LOG_HEXDUMP(("Packet to channel, len %d", sock->outbuf->len),
+ sock->outbuf->data, sock->outbuf->len);
+
+ /* Now actually send the packet */
+ silc_client_packet_send_real(client, sock, force_send);
+ silc_buffer_free(payload);
+ silc_free(id_string);
+}
+
+/* Process received message to a channel (or from a channel, really). This
+ decrypts the channel message with channel specific key and parses the
+ channel payload. Finally it displays the message on the screen. */
+
+void silc_client_channel_message(SilcClient client,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+ SilcBuffer buffer = packet->buffer;
+ SilcChannelPayload payload = NULL;
+ SilcChannelID *id = NULL;
+ SilcChannelEntry channel;
+ SilcChannelUser chu;
+ SilcIDCacheEntry id_cache = NULL;
+ SilcClientID *client_id = NULL;
+ int found = FALSE;
+ unsigned char *message;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ /* Sanity checks */
+ if (packet->dst_id_type != SILC_ID_CHANNEL)
+ goto out;
+
+ client_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+ SILC_ID_CLIENT);
+ if (!client_id)
+ goto out;
+ id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL);
+ if (!id)
+ goto out;
+
+ /* Find the channel entry from channels on this connection */
+ if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id,
+ SILC_ID_CHANNEL, &id_cache))
+ goto out;
+
+ channel = (SilcChannelEntry)id_cache->context;
+
+ /* Parse the channel message payload. This also decrypts the payload */
+ payload = silc_channel_payload_parse(buffer, channel->channel_key,
+ channel->hmac);
+ if (!payload)
+ goto out;
+
+ message = silc_channel_get_data(payload, NULL);
+
+ /* Find client entry */
+ silc_list_start(channel->clients);
+ while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+ if (!SILC_ID_CLIENT_COMPARE(chu->client->id, client_id)) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ /* Pass the message to application */
+ client->ops->channel_message(client, conn, found ? chu->client : NULL,
+ channel, message);
+
+ out:
+ if (id)
+ silc_free(id);
+ if (client_id)
+ silc_free(client_id);
+ if (payload)
+ silc_channel_payload_free(payload);
+}
+
+/* Saves channel key from encoded `key_payload'. This is used when we
+ receive Channel Key Payload and when we are processing JOIN command
+ reply. */
+
+void silc_client_save_channel_key(SilcClientConnection conn,
+ SilcBuffer key_payload,
+ SilcChannelEntry channel)
+{
+ unsigned char *id_string, *key, *cipher, hash[32];
+ unsigned int tmp_len;
+ SilcChannelID *id;
+ SilcIDCacheEntry id_cache = NULL;
+ SilcChannelKeyPayload payload;
+
+ payload = silc_channel_key_payload_parse(key_payload);
+ if (!payload)
+ return;
+
+ id_string = silc_channel_key_get_id(payload, &tmp_len);
+ if (!id_string) {
+ silc_channel_key_payload_free(payload);
+ return;
+ }
+
+ id = silc_id_str2id(id_string, tmp_len, SILC_ID_CHANNEL);
+ if (!id) {
+ silc_channel_key_payload_free(payload);
+ return;
+ }
+
+ /* Find channel. */
+ if (!channel) {
+ if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id,
+ SILC_ID_CHANNEL, &id_cache))
+ goto out;
+
+ /* Get channel entry */
+ channel = (SilcChannelEntry)id_cache->context;
+ }
+
+ /* Save the key */
+ key = silc_channel_key_get_key(payload, &tmp_len);
+ cipher = silc_channel_key_get_cipher(payload, NULL);
+ channel->key_len = tmp_len * 8;
+ channel->key = silc_calloc(tmp_len, sizeof(*channel->key));
+ memcpy(channel->key, key, tmp_len);
+
+ if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
+ conn->client->ops->say(conn->client, conn,
+ "Cannot talk to channel: unsupported cipher %s", cipher);
+ goto out;
+ }
+
+ /* Set the cipher key */
+ silc_cipher_set_key(channel->channel_key, key, channel->key_len);
+
+ /* Generate HMAC key from the channel key data and set it */
+ if (!channel->hmac)
+ silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac);
+ silc_hash_make(channel->hmac->hash, key, tmp_len, hash);
+ silc_hmac_set_key(channel->hmac, hash, silc_hash_len(channel->hmac->hash));
+ memset(hash, 0, sizeof(hash));
+
+ /* Client is now joined to the channel */
+ channel->on_channel = TRUE;
+
+ out:
+ silc_free(id);
+ silc_channel_key_payload_free(payload);
+}
+
+/* Processes received key for channel. The received key will be used
+ to protect the traffic on the channel for now on. Client must receive
+ the key to the channel before talking on the channel is possible.
+ This is the key that server has generated, this is not the channel
+ private key, it is entirely local setting. */
+
+void silc_client_receive_channel_key(SilcClient client,
+ SilcSocketConnection sock,
+ SilcBuffer packet)
+{
+ SILC_LOG_DEBUG(("Received key for channel"));
+
+ /* Save the key */
+ silc_client_save_channel_key(sock->user_data, packet, NULL);
+}
+
+/* Adds private key for channel. This may be set only if the channel's mode
+ mask includes the SILC_CHANNEL_MODE_PRIVKEY. This returns FALSE if the
+ mode is not set. When channel has private key then the messages are
+ encrypted using that key. All clients on the channel must also know the
+ key in order to decrypt the messages. However, it is possible to have
+ several private keys per one channel. In this case only some of the
+ clients on the channel may now the one key and only some the other key.
+
+ The private key for channel is optional. If it is not set then the
+ channel messages are encrypted using the channel key generated by the
+ server. However, setting the private key (or keys) for the channel
+ significantly adds security. If more than one key is set the library
+ will automatically try all keys at the message decryption phase. Note:
+ setting many keys slows down the decryption phase as all keys has to
+ be tried in order to find the correct decryption key. However, setting
+ a few keys does not have big impact to the decryption performace.
+
+ NOTE: that this is entirely local setting. The key set using this function
+ is not sent to the network at any phase.
+
+ NOTE: If the key material was originated by the SKE protocol (using
+ silc_client_send_key_agreement) then the `key' MUST be the
+ key->send_enc_key as this is dictated by the SILC protocol. However,
+ currently it is not expected that the SKE key material would be used
+ as channel private key. However, this API allows it. */
+
+int silc_client_add_channel_private_key(SilcClient client,
+ SilcClientConnection conn,
+ SilcChannelEntry channel,
+ char *cipher,
+ unsigned char *key,
+ unsigned int key_len)
+{
+
+ return TRUE;
+}
+
+/* Removes all private keys from the `channel'. The old channel key is used
+ after calling this to protect the channel messages. Returns FALSE on
+ on error, TRUE otherwise. */
+
+int silc_client_del_channel_private_keys(SilcClient client,
+ SilcClientConnection conn,
+ SilcChannelEntry channel)
+{
+
+ return TRUE;
+}
+
+/* Removes and frees private key `key' from the channel `channel'. The `key'
+ is retrieved by calling the function silc_client_list_channel_private_keys.
+ The key is not used after this. If the key was last private key then the
+ old channel key is used hereafter to protect the channel messages. This
+ returns FALSE on error, TRUE otherwise. */
+
+int silc_client_del_channel_private_key(SilcClient client,
+ SilcClientConnection conn,
+ SilcChannelEntry channel,
+ SilcChannelPrivateKey key)
+{
+
+ return TRUE;
+}
+
+/* Returns array (pointers) of private keys associated to the `channel'.
+ The caller must free the array by calling the function
+ silc_client_free_channel_private_keys. The pointers in the array may be
+ used to delete the specific key by giving the pointer as argument to the
+ function silc_client_del_channel_private_key. */
+
+SilcChannelPrivateKey *
+silc_client_list_channel_private_keys(SilcClient client,
+ SilcClientConnection conn,
+ SilcChannelEntry channel,
+ unsigned int *key_count)
+{
+
+ return NULL;
+}
+
+/* Frees the SilcChannelPrivateKey array. */
+
+void silc_client_free_channel_private_keys(SilcChannelPrivateKey *keys,
+ unsigned int key_count)
+{
+
+}
--- /dev/null
+/*
+
+ client_internal.h
+
+ Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+ Copyright (C) 1997 - 2001 Pekka Riikonen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+*/
+
+#ifndef CLIENT_INTERNAL_H
+#define CLIENT_INTERNAL_H
+
+/* Internal context for connection process. This is needed as we
+ doing asynchronous connecting. */
+typedef struct {
+ SilcClient client;
+ SilcClientConnection conn;
+ SilcTask task;
+ int sock;
+ char *host;
+ int port;
+ int tries;
+ void *context;
+} SilcClientInternalConnectContext;
+
+/* Structure to hold ping time information. Every PING command will
+ add entry of this structure and is removed after reply to the ping
+ as been received. */
+struct SilcClientPingStruct {
+ time_t start_time;
+ void *dest_id;
+ char *dest_name;
+};
+
+/* Structure to hold away messages set by user. This is mainly created
+ for future extensions where away messages could be set according filters
+ such as nickname and hostname. For now only one away message can
+ be set in one connection. */
+struct SilcClientAwayStruct {
+ char *away;
+ struct SilcClientAwayStruct *next;
+};
+
+/* Failure context. This is allocated when failure packet is received.
+ Failure packets are processed with timeout and data is saved in this
+ structure. */
+typedef struct {
+ SilcClient client;
+ SilcSocketConnection sock;
+ unsigned int failure;
+} SilcClientFailureContext;
+
+/* Protypes */
+
+SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process);
+SILC_TASK_CALLBACK_GLOBAL(silc_client_failure_callback);
+int silc_client_packet_send_real(SilcClient client,
+ SilcSocketConnection sock,
+ int force_send);
+
+#endif
--- /dev/null
+/*
+
+ client_keyagr.c
+
+ Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+ Copyright (C) 2001 Pekka Riikonen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+*/
+/* $Id$ */
+/* This file includes the Key Agreement packet processing and actual
+ key agreement routines. This file has nothing to do with the actual
+ connection key exchange protocol, it is implemented in the client.c
+ and in protocol.c. This file implements the client-to-client key
+ agreement as defined by the SILC protocol. */
+
+#include "clientlibincludes.h"
+#include "client_internal.h"
+
+SILC_TASK_CALLBACK(silc_client_key_agreement_final);
+SILC_TASK_CALLBACK(silc_client_process_key_agreement);
+SILC_TASK_CALLBACK(silc_client_key_agreement_timeout);
+SILC_TASK_CALLBACK(silc_client_perform_key_agreement_start);
+
+/* Key agreement context */
+struct SilcClientKeyAgreementStruct {
+ SilcClient client;
+ SilcClientConnection conn;
+ int fd; /* Listening/connection socket */
+ SilcSocketConnection sock; /* Remote socket connection */
+ SilcClientEntry client_entry; /* Destination client */
+ SilcKeyAgreementCallback completion; /* Key agreement completion */
+ void *context; /* User context */
+ SilcTask timeout; /* Timeout task */
+};
+
+/* Packet sending function used by the SKE in the key agreement process. */
+
+static void silc_client_key_agreement_send_packet(SilcSKE ske,
+ SilcBuffer packet,
+ SilcPacketType type,
+ void *context)
+{
+ SilcProtocol protocol = (SilcProtocol)context;
+ SilcClientKEInternalContext *ctx =
+ (SilcClientKEInternalContext *)protocol->context;
+ void *tmp;
+
+ /* Send the packet immediately. We will assure that the packet is not
+ encrypted by setting the socket's user_data pointer to NULL. The
+ silc_client_packet_send would take the keys (wrong keys that is,
+ because user_data is the current SilcClientConnection) from it and
+ we cannot allow that. The packets are never encrypted when doing SKE
+ with another client. */
+ tmp = ske->sock->user_data;
+ ske->sock->user_data = NULL;
+ silc_client_packet_send(ctx->client, ske->sock, type, NULL, 0, NULL, NULL,
+ packet->data, packet->len, TRUE);
+ ske->sock->user_data = tmp;
+}
+
+/* This callback is called after the key agreement protocol has been
+ performed. This calls the final completion callback for the application. */
+
+SILC_TASK_CALLBACK(silc_client_key_agreement_final)
+{
+ SilcProtocol protocol = (SilcProtocol)context;
+ SilcClientKEInternalContext *ctx =
+ (SilcClientKEInternalContext *)protocol->context;
+ SilcClient client = (SilcClient)ctx->client;
+ SilcClientKeyAgreement ke = (SilcClientKeyAgreement)ctx->context;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+ 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);
+ 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);
+
+ out:
+ silc_protocol_free(protocol);
+ if (ctx->ske)
+ silc_ske_free(ctx->ske);
+ if (ctx->dest_id)
+ silc_free(ctx->dest_id);
+ silc_task_unregister_by_callback(client->timeout_queue,
+ silc_client_failure_callback);
+ silc_task_unregister_by_fd(client->io_queue, ke->fd);
+ if (ke->timeout)
+ silc_task_unregister(client->timeout_queue, ke->timeout);
+ silc_socket_free(ke->sock);
+ silc_free(ke);
+ silc_free(ctx);
+}
+
+/* Key agreement callback that is called when remote end has initiated
+ the key agreement protocol. This accepts the incoming TCP/IP connection
+ for the key agreement protocol. */
+
+SILC_TASK_CALLBACK(silc_client_process_key_agreement)
+{
+ SilcClientKeyAgreement ke = (SilcClientKeyAgreement)context;
+ SilcClient client = ke->client;
+ SilcClientConnection conn = ke->conn;
+ SilcSocketConnection newsocket;
+ SilcClientKEInternalContext *proto_ctx;
+ int sock;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ sock = silc_net_accept_connection(ke->fd);
+ if (sock < 0) {
+ client->ops->say(client, conn,
+ "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);
+ silc_task_unregister_by_fd(client->io_queue, ke->fd);
+ if (ke->timeout)
+ silc_task_unregister(client->timeout_queue, ke->timeout);
+ silc_free(ke);
+ return;
+ }
+
+ /* Set socket options */
+ silc_net_set_socket_nonblock(sock);
+ silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
+
+ /* Create socket for this connection (it is of type UNKNOWN since this
+ really is not a real SILC connection. It is only for the key
+ agreement protocol). */
+ silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, (void *)conn, &newsocket);
+ ke->sock = newsocket;
+
+ /* Perform name and address lookups for the remote host. */
+ silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
+ if (!newsocket->hostname && !newsocket->ip) {
+ 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);
+ silc_task_unregister_by_fd(client->io_queue, ke->fd);
+ if (ke->timeout)
+ silc_task_unregister(client->timeout_queue, ke->timeout);
+ silc_free(ke);
+ return;
+ }
+ if (!newsocket->hostname)
+ newsocket->hostname = strdup(newsocket->ip);
+ newsocket->port = silc_net_get_remote_port(sock);
+
+ /* Allocate internal context for key exchange protocol. This is
+ sent as context for the protocol. */
+ proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+ proto_ctx->client = client;
+ proto_ctx->sock = newsocket;
+ proto_ctx->rng = client->rng;
+ proto_ctx->responder = TRUE;
+ proto_ctx->context = context;
+ proto_ctx->send_packet = silc_client_key_agreement_send_packet;
+
+ /* Prepare the connection for key exchange protocol. We allocate the
+ protocol but will not start it yet. The connector will be the
+ initiator of the protocol thus we will wait for initiation from
+ there before we start the protocol. */
+ silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
+ &newsocket->protocol, proto_ctx,
+ silc_client_key_agreement_final);
+
+ /* Register the connection for network input and output. This sets
+ that scheduler will listen for incoming packets for this connection
+ and sets that outgoing packets may be sent to this connection as well.
+ 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. */
+ SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
+}
+
+/* Timeout occured during key agreement. This means that the key agreement
+ protocol was not completed in the specified timeout. We will call the
+ completion callback. */
+
+SILC_TASK_CALLBACK(silc_client_key_agreement_timeout)
+{
+ SilcClientKeyAgreement ke = (SilcClientKeyAgreement)context;
+
+ ke->client_entry->ke = NULL;
+ ke->completion(ke->client, ke->conn, ke->client_entry, NULL, ke->context);
+
+ if (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);
+}
+
+/* Sends key agreement request to the remote client indicated by the
+ `client_entry'. If the caller provides the `hostname' and the `port'
+ arguments then the library will bind the client to that hostname and
+ that port for the key agreement protocol. It also sends the `hostname'
+ and the `port' in the key agreement packet to the remote client. This
+ would indicate that the remote client may initiate the key agreement
+ protocol to the `hostname' on the `port'.
+
+ If the `hostname' and `port' is not provided then empty key agreement
+ packet is sent to the remote client. The remote client may reply with
+ the same packet including its hostname and port. If the library receives
+ the reply from the remote client the `key_agreement' client operation
+ callback will be called to verify whether the user wants to perform the
+ key agreement or not.
+
+ NOTE: If the application provided the `hostname' and the `port' and the
+ remote side initiates the key agreement protocol it is not verified
+ from the user anymore whether the protocol should be executed or not.
+ By setting the `hostname' and `port' the user gives permission to
+ perform the protocol (we are responder in this case).
+
+ NOTE: If the remote side decides not to initiate the key agreement
+ or decides not to reply with the key agreement packet then we cannot
+ perform the key agreement at all. If the key agreement protocol is
+ performed the `completion' callback with the `context' will be called.
+ If remote side decides to ignore the request the `completion' will be
+ called after the specified timeout, `timeout_secs'.
+
+ NOTE: There can be only one active key agreement for one client entry.
+ Before setting new one, the old one must be finished (it is finished
+ after calling the completion callback) or the function
+ silc_client_abort_key_agreement must be called. */
+
+void silc_client_send_key_agreement(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry client_entry,
+ char *hostname,
+ int port,
+ unsigned long timeout_secs,
+ SilcKeyAgreementCallback completion,
+ void *context)
+{
+ SilcSocketConnection sock = conn->sock;
+ SilcClientKeyAgreement ke;
+ SilcBuffer buffer;
+
+ assert(client_entry);
+
+ if (client_entry->ke)
+ return;
+
+ /* Create the listener if hostname and port was provided */
+ if (hostname && port) {
+ ke = silc_calloc(1, sizeof(*ke));
+ ke->fd = silc_net_create_server(port, hostname);
+
+ if (ke->fd < 0) {
+ 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);
+ silc_free(ke);
+ return;
+ }
+
+ ke->client = client;
+ ke->conn = conn;
+ ke->client_entry = client_entry;
+ ke->completion = completion;
+ ke->context = context;
+
+ /* Add listener task to the queue. This task receives the key
+ negotiations. */
+ silc_task_register(client->io_queue, ke->fd,
+ silc_client_process_key_agreement,
+ (void *)ke, 0, 0,
+ SILC_TASK_FD,
+ SILC_TASK_PRI_NORMAL);
+
+ /* 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);
+ }
+
+ /* Encode the key agreement payload */
+ buffer = silc_key_agreement_payload_encode(hostname, port);
+
+ /* Send the key agreement packet to the client */
+ silc_client_packet_send(client, sock, SILC_PACKET_KEY_AGREEMENT,
+ client_entry->id, SILC_ID_CLIENT, NULL, NULL,
+ buffer->data, buffer->len, FALSE);
+ silc_free(buffer);
+}
+
+static int
+silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx)
+{
+ int sock;
+
+ /* Create connection to server asynchronously */
+ sock = silc_net_create_connection_async(ctx->port, ctx->host);
+ if (sock < 0)
+ return -1;
+
+ /* Register task that will receive the async connect and will
+ read the result. */
+ ctx->task = silc_task_register(ctx->client->io_queue, sock,
+ silc_client_perform_key_agreement_start,
+ (void *)ctx, 0, 0,
+ SILC_TASK_FD,
+ SILC_TASK_PRI_NORMAL);
+ silc_task_reset_iotype(ctx->task, SILC_TASK_WRITE);
+ silc_schedule_set_listen_fd(sock, ctx->task->iomask);
+
+ ctx->sock = sock;
+
+ return sock;
+}
+
+/* Routine used by silc_client_perform_key_agreement to create connection
+ to the remote client on specified port. */
+
+static int
+silc_client_connect_to_client(SilcClient client,
+ SilcClientConnection conn, int port,
+ char *host, void *context)
+{
+ SilcClientInternalConnectContext *ctx;
+
+ /* Allocate internal context for connection process. This is
+ needed as we are doing async connecting. */
+ ctx = silc_calloc(1, sizeof(*ctx));
+ ctx->client = client;
+ ctx->conn = conn;
+ ctx->host = strdup(host);
+ ctx->port = port;
+ ctx->tries = 0;
+ ctx->context = context;
+
+ /* Do the actual connecting process */
+ return silc_client_connect_to_client_internal(ctx);
+}
+
+/* Callback that is called after connection has been created. This actually
+ starts the key agreement protocol. This is initiator function. */
+
+SILC_TASK_CALLBACK(silc_client_perform_key_agreement_start)
+{
+ SilcClientInternalConnectContext *ctx =
+ (SilcClientInternalConnectContext *)context;
+ SilcClient client = ctx->client;
+ SilcClientConnection conn = ctx->conn;
+ SilcClientKeyAgreement ke = (SilcClientKeyAgreement)ctx->context;
+ int opt, opt_len = sizeof(opt);
+
+ SILC_LOG_DEBUG(("Start"));
+
+ /* Check the socket status as it might be in error */
+ getsockopt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
+ if (opt != 0) {
+ if (ctx->tries < 2) {
+ /* Connection failed but lets try again */
+ client->ops->say(client, conn, "Could not connect to client %s: %s",
+ ctx->host, strerror(opt));
+ client->ops->say(client, conn,
+ "Connecting to port %d of client %s resumed",
+ ctx->port, ctx->host);
+
+ /* Unregister old connection try */
+ silc_schedule_unset_listen_fd(fd);
+ silc_net_close_connection(fd);
+ silc_task_unregister(client->io_queue, ctx->task);
+
+ /* Try again */
+ silc_client_connect_to_client_internal(ctx);
+ ctx->tries++;
+ } else {
+ /* Connection failed and we won't try anymore */
+ client->ops->say(client, conn, "Could not connect to client %s: %s",
+ ctx->host, strerror(opt));
+ silc_schedule_unset_listen_fd(fd);
+ silc_net_close_connection(fd);
+ silc_task_unregister(client->io_queue, ctx->task);
+ silc_free(ctx);
+
+ /* Call the completion callback */
+ ke->completion(ke->client, ke->conn, ke->client_entry,
+ 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->completion, ke->context);
+ silc_free(ke);
+}
+
+/* Performs the actual key agreement protocol. Application may use this
+ to initiate the key agreement protocol. This can be called for example
+ after the application has received the `key_agreement' client operation,
+ and did not return TRUE from it.
+
+ The `hostname' is the remote hostname (or IP address) and the `port'
+ is the remote port. The `completion' callback with the `context' will
+ be called after the key agreement protocol.
+
+ NOTE: If the application returns TRUE in the `key_agreement' client
+ operation the library will automatically start the key agreement. In this
+ case the application must not call this function. However, application
+ may choose to just ignore the `key_agreement' client operation (and
+ merely just print information about it on the screen) and call this
+ function when the user whishes to do so (by, for example, giving some
+ specific command). Thus, the API provides both, automatic and manual
+ initiation of the key agreement. Calling this function is the manual
+ initiation and returning TRUE in the `key_agreement' client operation
+ is the automatic initiation. */
+
+void silc_client_perform_key_agreement(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry client_entry,
+ char *hostname,
+ int port,
+ SilcKeyAgreementCallback completion,
+ void *context)
+{
+ SilcClientKeyAgreement ke;
+
+ assert(client_entry && hostname && port);
+
+ ke = silc_calloc(1, sizeof(*ke));
+ ke->client = client;
+ ke->conn = conn;
+ ke->client_entry = client_entry;
+ ke->completion = completion;
+ ke->context = context;
+
+ /* 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);
+ silc_free(ke);
+ return;
+ }
+}
+
+/* Same as above but application has created already the connection to
+ the remote host. The `sock' is the socket to the remote connection.
+ Application can use this function if it does not want the client library
+ to create the connection. */
+
+void silc_client_perform_key_agreement_fd(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry client_entry,
+ int sock,
+ SilcKeyAgreementCallback completion,
+ void *context)
+{
+ SilcClientKeyAgreement ke;
+ SilcClientKEInternalContext *proto_ctx;
+ SilcProtocol protocol;
+
+ assert(client_entry);
+
+ ke = silc_calloc(1, sizeof(*ke));
+ ke->client = client;
+ ke->conn = conn;
+ ke->client_entry = client_entry;
+ ke->fd = sock;
+ ke->completion = completion;
+ ke->context = context;
+
+ /* Allocate new socket connection object */
+ silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, (void *)conn, &ke->sock);
+
+ /* Allocate internal context for key exchange protocol. This is
+ sent as context for the protocol. */
+ proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+ proto_ctx->client = client;
+ proto_ctx->sock = ke->sock;
+ proto_ctx->rng = client->rng;
+ proto_ctx->responder = FALSE;
+ proto_ctx->context = ke;
+ proto_ctx->send_packet = silc_client_key_agreement_send_packet;
+
+ /* Perform key exchange protocol. */
+ silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
+ &protocol, (void *)proto_ctx,
+ silc_client_key_agreement_final);
+ ke->sock->protocol = protocol;
+
+ /* Register the connection for network input and output. This sets
+ that scheduler will listen for incoming packets for this connection
+ and sets that outgoing packets may be sent to this connection as well.
+ 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);
+
+ /* Execute the protocol */
+ protocol->execute(client->timeout_queue, 0, protocol, sock, 0, 0);
+}
+
+/* This function can be called to unbind the hostname and the port for
+ the key agreement protocol. However, this function has effect only
+ before the key agreement protocol has been performed. After it has
+ been performed the library will automatically unbind the port. The
+ `client_entry' is the client to which we sent the key agreement
+ request. */
+
+void silc_client_abort_key_agreement(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry client_entry)
+{
+ assert(client_entry);
+
+ if (client_entry->ke) {
+ if (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)
+ silc_task_unregister(client->timeout_queue,
+ client_entry->ke->timeout);
+ silc_free(client_entry->ke);
+ }
+}
+
+/* Callback function that is called after we've resolved the client
+ information who sent us the key agreement packet from the server.
+ We actually call the key_agreement client operation now. */
+
+static void
+silc_client_key_agreement_resolve_cb(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry *clients,
+ unsigned int clients_count,
+ void *context)
+{
+ SilcPacketContext *packet = (SilcPacketContext *)context;
+ SilcKeyAgreementPayload payload;
+ int ret;
+ SilcKeyAgreementCallback completion;
+ void *completion_context;
+
+ if (!clients)
+ goto out;
+
+ /* Parse the key agreement payload */
+ payload = silc_key_agreement_payload_parse(packet->buffer);
+ if (!payload)
+ goto out;
+
+ /* Call the key_agreement client operation */
+ ret = client->ops->key_agreement(client, conn, clients[0],
+ silc_key_agreement_get_hostname(payload),
+ silc_key_agreement_get_port(payload),
+ &completion, &completion_context);
+
+ /* If the user returned TRUE then we'll start the key agreement right
+ here and right now. */
+ if (ret == TRUE)
+ silc_client_perform_key_agreement(client, conn, clients[0],
+ silc_key_agreement_get_hostname(payload),
+ silc_key_agreement_get_port(payload),
+ completion, completion_context);
+
+ silc_key_agreement_payload_free(payload);
+
+ out:
+ silc_packet_context_free(packet);
+}
+
+/* Received Key Agreement packet from remote client. Process the packet
+ and resolve the client information from the server before actually
+ letting the application know that we've received this packet. Then
+ call the key_agreement client operation and let the user decide
+ whether we perform the key agreement protocol now or not. */
+
+void silc_client_key_agreement(SilcClient client,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcClientID *remote_id;
+
+ if (packet->src_id_type != SILC_ID_CLIENT)
+ return;
+
+ remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+ SILC_ID_CLIENT);
+ if (!remote_id)
+ return;
+
+ silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
+ silc_client_key_agreement_resolve_cb,
+ silc_packet_context_dup(packet));
+ silc_free(remote_id);
+}
--- /dev/null
+/*
+
+ client_notify.c
+
+ Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+ Copyright (C) 1997 - 2001 Pekka Riikonen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+*/
+/* $Id$ */
+/* This file includes the Notify packet handling. Notify packets are
+ important packets sent by the server. They tell different things to the
+ client such as nick changes, mode changes etc. */
+
+#include "clientlibincludes.h"
+#include "client_internal.h"
+
+/* Called when notify is received and some async operation (such as command)
+ is required before processing the notify message. This calls again the
+ silc_client_notify_by_server and reprocesses the original notify packet. */
+
+static void silc_client_notify_by_server_pending(void *context)
+{
+ SilcPacketContext *p = (SilcPacketContext *)context;
+ silc_client_notify_by_server(p->context, p->sock, p);
+}
+
+/* Destructor for the pending command callback */
+
+static void silc_client_notify_by_server_destructor(void *context)
+{
+ silc_packet_context_free((SilcPacketContext *)context);
+}
+
+/* Resolve client information from server by Client ID. */
+
+static void silc_client_notify_by_server_resolve(SilcClient client,
+ SilcClientConnection conn,
+ SilcPacketContext *packet,
+ SilcClientID *client_id)
+{
+ SilcPacketContext *p = silc_packet_context_dup(packet);
+ SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+
+ p->context = (void *)client;
+ p->sock = conn->sock;
+
+ silc_client_send_command(client, conn, SILC_COMMAND_WHOIS, ++conn->cmd_ident,
+ 1, 3, idp->data, idp->len);
+ silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
+ silc_client_notify_by_server_destructor,
+ silc_client_notify_by_server_pending, p);
+ silc_buffer_free(idp);
+}
+
+/* Received notify message from server */
+
+void silc_client_notify_by_server(SilcClient client,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcBuffer buffer = packet->buffer;
+ SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+ SilcNotifyPayload payload;
+ SilcNotifyType type;
+ SilcArgumentPayload args;
+
+ SilcClientID *client_id = NULL;
+ SilcChannelID *channel_id = NULL;
+ SilcClientEntry client_entry;
+ SilcClientEntry client_entry2;
+ SilcChannelEntry channel;
+ SilcChannelUser chu;
+ SilcIDCacheEntry id_cache = NULL;
+ unsigned char *tmp;
+ unsigned int tmp_len, mode;
+
+ payload = silc_notify_payload_parse(buffer);
+ if (!payload)
+ goto out;
+
+ type = silc_notify_get_type(payload);
+ args = silc_notify_get_args(payload);
+ if (!args)
+ goto out;
+
+ switch(type) {
+ case SILC_NOTIFY_TYPE_NONE:
+ /* Notify application */
+ client->ops->notify(client, conn, type,
+ silc_argument_get_arg_type(args, 1, NULL));
+ break;
+
+ case SILC_NOTIFY_TYPE_INVITE:
+ /*
+ * Someone invited me to a channel. Find Client and Channel entries
+ * for the application.
+ */
+
+ /* Get Client ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id)
+ goto out;
+
+ /* Find Client entry and if not found query it */
+ client_entry = silc_client_get_client_by_id(client, conn, client_id);
+ if (!client_entry) {
+ silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+ goto out;
+ }
+
+ /* Get Channel ID */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!channel_id)
+ goto out;
+
+ /* XXX Will ALWAYS fail because currently we don't have way to resolve
+ channel information for channel that we're not joined to. */
+ /* XXX ways to fix: use (extended) LIST command, or define the channel
+ name to the notfy type when name resolving is not mandatory. */
+ /* Find channel entry */
+ if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+ SILC_ID_CHANNEL, &id_cache))
+ goto out;
+
+ channel = (SilcChannelEntry)id_cache->context;
+
+ /* Notify application */
+ client->ops->notify(client, conn, type, client_entry, channel);
+ break;
+
+ case SILC_NOTIFY_TYPE_JOIN:
+ /*
+ * Someone has joined to a channel. Get their ID and nickname and
+ * cache them for later use.
+ */
+
+ /* Get Client ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id)
+ goto out;
+
+ /* Find Client entry and if not found query it */
+ client_entry = silc_client_get_client_by_id(client, conn, client_id);
+ if (!client_entry) {
+ silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+ goto out;
+ }
+
+ /* If nickname or username hasn't been resolved, do so */
+ if (!client_entry->nickname || !client_entry->username) {
+ silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+ goto out;
+ }
+
+ /* Get Channel ID */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!channel_id)
+ goto out;
+
+ /* Get channel entry */
+ if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+ SILC_ID_CHANNEL, &id_cache))
+ break;
+
+ channel = (SilcChannelEntry)id_cache->context;
+
+ /* Add client to channel */
+ chu = silc_calloc(1, sizeof(*chu));
+ chu->client = client_entry;
+ silc_list_add(channel->clients, chu);
+
+ /* XXX add support for multiple same nicks on same channel. Check
+ for them here */
+
+ /* Notify application. The channel entry is sent last as this notify
+ is for channel but application don't know it from the arguments
+ sent by server. */
+ client->ops->notify(client, conn, type, client_entry, channel);
+ break;
+
+ case SILC_NOTIFY_TYPE_LEAVE:
+ /*
+ * Someone has left a channel. We will remove it from the channel but
+ * we'll keep it in the cache in case we'll need it later.
+ */
+
+ /* Get Client ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id)
+ goto out;
+
+ /* Find Client entry */
+ client_entry =
+ silc_client_get_client_by_id(client, conn, client_id);
+ if (!client_entry)
+ goto out;
+
+ /* Get channel entry */
+ channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+ SILC_ID_CHANNEL);
+ if (!channel_id)
+ goto out;
+ if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+ SILC_ID_CHANNEL, &id_cache))
+ break;
+
+ channel = (SilcChannelEntry)id_cache->context;
+
+ /* Remove client from channel */
+ silc_list_start(channel->clients);
+ while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+ if (chu->client == client_entry) {
+ silc_list_del(channel->clients, chu);
+ silc_free(chu);
+ break;
+ }
+ }
+
+ /* Notify application. The channel entry is sent last as this notify
+ is for channel but application don't know it from the arguments
+ sent by server. */
+ client->ops->notify(client, conn, type, client_entry, channel);
+ break;
+
+ case SILC_NOTIFY_TYPE_SIGNOFF:
+ /*
+ * Someone left SILC. We'll remove it from all channels and from cache.
+ */
+
+ /* Get Client ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id)
+ goto out;
+
+ /* Find Client entry */
+ client_entry =
+ silc_client_get_client_by_id(client, conn, client_id);
+ if (!client_entry)
+ goto out;
+
+ /* Remove from all channels */
+ silc_client_remove_from_channels(client, conn, client_entry);
+
+ /* Remove from cache */
+ silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT,
+ client_entry->id);
+
+ /* Get signoff message */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (tmp_len > 128)
+ tmp = NULL;
+
+ /* Notify application */
+ client->ops->notify(client, conn, type, client_entry, tmp);
+
+ /* Free data */
+ if (client_entry->nickname)
+ silc_free(client_entry->nickname);
+ if (client_entry->server)
+ silc_free(client_entry->server);
+ if (client_entry->id)
+ silc_free(client_entry->id);
+ if (client_entry->send_key)
+ silc_cipher_free(client_entry->send_key);
+ if (client_entry->receive_key)
+ silc_cipher_free(client_entry->receive_key);
+ break;
+
+ case SILC_NOTIFY_TYPE_TOPIC_SET:
+ /*
+ * Someone set the topic on a channel.
+ */
+
+ /* Get Client ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id)
+ goto out;
+
+ /* Find Client entry */
+ client_entry =
+ silc_client_get_client_by_id(client, conn, client_id);
+ if (!client_entry)
+ goto out;
+
+ /* Get topic */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ /* Get channel entry */
+ channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+ SILC_ID_CHANNEL);
+ if (!channel_id)
+ goto out;
+ if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+ SILC_ID_CHANNEL, &id_cache))
+ break;
+
+ channel = (SilcChannelEntry)id_cache->context;
+
+ /* Notify application. The channel entry is sent last as this notify
+ is for channel but application don't know it from the arguments
+ sent by server. */
+ client->ops->notify(client, conn, type, client_entry, tmp, channel);
+ break;
+
+ case SILC_NOTIFY_TYPE_NICK_CHANGE:
+ /*
+ * Someone changed their nickname. If we don't have entry for the new
+ * ID we will query it and return here after it's done. After we've
+ * returned we fetch the old entry and free it and notify the
+ * application.
+ */
+
+ /* Get new Client ID */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id)
+ goto out;
+
+ /* Ignore my ID */
+ if (!SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
+ break;
+
+ /* Find Client entry and if not found query it */
+ client_entry2 =
+ silc_client_get_client_by_id(client, conn, client_id);
+ if (!client_entry2) {
+ silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+ goto out;
+ }
+ silc_free(client_id);
+
+ /* Get old Client ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id)
+ goto out;
+
+ /* Find old Client entry */
+ client_entry =
+ silc_client_get_client_by_id(client, conn, client_id);
+ if (!client_entry)
+ goto out;
+
+ /* Remove the old from cache */
+ silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT,
+ client_entry->id);
+
+ /* Replace old ID entry with new one on all channels. */
+ silc_client_replace_from_channels(client, conn, client_entry,
+ client_entry2);
+
+ /* Notify application */
+ client->ops->notify(client, conn, type, client_entry, client_entry2);
+
+ /* Free data */
+ if (client_entry->nickname)
+ silc_free(client_entry->nickname);
+ if (client_entry->server)
+ silc_free(client_entry->server);
+ if (client_entry->id)
+ silc_free(client_entry->id);
+ if (client_entry->send_key)
+ silc_cipher_free(client_entry->send_key);
+ if (client_entry->receive_key)
+ silc_cipher_free(client_entry->receive_key);
+ break;
+
+ case SILC_NOTIFY_TYPE_CMODE_CHANGE:
+ /*
+ * Someone changed a channel mode
+ */
+
+ /* Get Client ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id)
+ goto out;
+
+ /* Find Client entry */
+ client_entry =
+ silc_client_get_client_by_id(client, conn, client_id);
+ if (!client_entry)
+ goto out;
+
+ /* Get the mode */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ SILC_GET32_MSB(mode, tmp);
+
+ /* Get channel entry */
+ channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+ SILC_ID_CHANNEL);
+ if (!channel_id)
+ goto out;
+ if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+ SILC_ID_CHANNEL, &id_cache))
+ break;
+
+ channel = (SilcChannelEntry)id_cache->context;
+
+ /* Save the new mode */
+ channel->mode = mode;
+
+ /* Notify application. The channel entry is sent last as this notify
+ is for channel but application don't know it from the arguments
+ sent by server. */
+ client->ops->notify(client, conn, type, client_entry, mode, channel);
+ break;
+
+ case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
+ /*
+ * Someone changed user's mode on a channel
+ */
+
+ /* Get Client ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id)
+ goto out;
+
+ /* Find Client entry */
+ client_entry =
+ silc_client_get_client_by_id(client, conn, client_id);
+ if (!client_entry)
+ goto out;
+
+ /* Get the mode */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ SILC_GET32_MSB(mode, tmp);
+
+ /* Get target Client ID */
+ tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ silc_free(client_id);
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id)
+ goto out;
+
+ /* Find target Client entry */
+ client_entry2 =
+ silc_client_get_client_by_id(client, conn, client_id);
+ if (!client_entry2)
+ goto out;
+
+ /* Get channel entry */
+ channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+ SILC_ID_CHANNEL);
+ if (!channel_id)
+ goto out;
+ if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+ SILC_ID_CHANNEL, &id_cache))
+ break;
+
+ channel = (SilcChannelEntry)id_cache->context;
+
+ /* Save the mode */
+ silc_list_start(channel->clients);
+ while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+ if (chu->client == client_entry) {
+ chu->mode = mode;
+ break;
+ }
+ }
+
+ /* Notify application. The channel entry is sent last as this notify
+ is for channel but application don't know it from the arguments
+ sent by server. */
+ client->ops->notify(client, conn, type, client_entry, mode,
+ client_entry2, channel);
+ break;
+
+ case SILC_NOTIFY_TYPE_MOTD:
+ /*
+ * Received Message of the day
+ */
+
+ /* Get motd */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ /* Notify application */
+ client->ops->notify(client, conn, type, tmp);
+ break;
+
+ case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
+ /*
+ * Router has enforced a new ID to a channel. Let's change the old
+ * ID to the one provided here.
+ */
+
+ /* Get the old ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
+ goto out;
+ channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!channel_id)
+ goto out;
+
+ /* Get the channel entry */
+ if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+ SILC_ID_CHANNEL, &id_cache))
+ break;
+
+ channel = (SilcChannelEntry)id_cache->context;
+
+ /* Free the old ID */
+ silc_free(channel_id);
+ silc_free(channel->id);
+
+ /* Get the new ID */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (!tmp)
+ goto out;
+ channel->id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!channel->id)
+ goto out;
+
+ id_cache->id = (void *)channel->id;
+
+ /* Notify application */
+ client->ops->notify(client, conn, type, channel, channel);
+ break;
+
+ case SILC_NOTIFY_TYPE_KICKED:
+ /*
+ * A client (maybe me) was kicked from a channel
+ */
+
+ /* Get Client ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id)
+ goto out;
+
+ /* Find Client entry */
+ client_entry =
+ silc_client_get_client_by_id(client, conn, client_id);
+ if (!client_entry)
+ goto out;
+
+ /* Get channel entry */
+ channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+ SILC_ID_CHANNEL);
+ if (!channel_id)
+ goto out;
+ if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+ SILC_ID_CHANNEL, &id_cache))
+ break;
+
+ channel = (SilcChannelEntry)id_cache->context;
+
+ /* Get comment */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+
+ /* Notify application. The channel entry is sent last as this notify
+ is for channel but application don't know it from the arguments
+ sent by server. */
+ client->ops->notify(client, conn, type, client_entry, tmp, channel);
+
+ /* If I was kicked from channel, remove the channel */
+ if (client_entry == conn->local_entry) {
+ if (conn->current_channel == channel)
+ conn->current_channel = NULL;
+ silc_idcache_del_by_id(conn->channel_cache,
+ SILC_ID_CHANNEL, channel->id);
+ silc_free(channel->channel_name);
+ silc_free(channel->id);
+ silc_free(channel->key);
+ silc_cipher_free(channel->channel_key);
+ silc_free(channel);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ out:
+ silc_notify_payload_free(payload);
+ if (client_id)
+ silc_free(client_id);
+ if (channel_id)
+ silc_free(channel_id);
+}
--- /dev/null
+/*
+
+ client_prvmsg.c
+
+ Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+ Copyright (C) 1997 - 2001 Pekka Riikonen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+*/
+/* $Id$ */
+/* This file includes the private message sending and receiving routines
+ and private message key handling routines. */
+
+#include "clientlibincludes.h"
+#include "client_internal.h"
+
+/* Sends private message to remote client. If private message key has
+ not been set with this client then the message will be encrypted using
+ normal session keys. Private messages are special packets in SILC
+ network hence we need this own function for them. This is similiar
+ to silc_client_packet_send_to_channel except that we send private
+ message. The `data' is the private message. If the `force_send' is
+ TRUE the packet is sent immediately. */
+
+void silc_client_send_private_message(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry client_entry,
+ unsigned char *data,
+ unsigned int data_len,
+ int force_send)
+{
+ SilcSocketConnection sock = conn->sock;
+ SilcBuffer buffer;
+ SilcPacketContext packetdata;
+ unsigned int nick_len;
+ SilcCipher cipher;
+ SilcHmac hmac;
+
+ SILC_LOG_DEBUG(("Sending private message"));
+
+ /* Create private message payload */
+ nick_len = strlen(conn->nickname);
+ buffer = silc_buffer_alloc(2 + nick_len + data_len);
+ silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+ silc_buffer_format(buffer,
+ SILC_STR_UI_SHORT(nick_len),
+ SILC_STR_UI_XNSTRING(conn->nickname,
+ nick_len),
+ SILC_STR_UI_XNSTRING(data, data_len),
+ SILC_STR_END);
+
+ /* If we don't have private message specific key then private messages
+ are just as any normal packet thus call normal packet sending. If
+ the key exist then the encryption process is a bit different and
+ will be done in the rest of this function. */
+ if (!client_entry->send_key) {
+ silc_client_packet_send(client, sock, SILC_PACKET_PRIVATE_MESSAGE,
+ client_entry->id, SILC_ID_CLIENT, NULL, NULL,
+ buffer->data, buffer->len, force_send);
+ goto out;
+ }
+
+ /* We have private message specific key */
+
+ /* Get data used in the encryption */
+ cipher = client_entry->send_key;
+ hmac = conn->hmac;
+
+ /* Set the packet context pointers. */
+ packetdata.flags = SILC_PACKET_FLAG_PRIVMSG_KEY;
+ packetdata.type = SILC_PACKET_PRIVATE_MESSAGE;
+ packetdata.src_id = conn->local_id_data;
+ packetdata.src_id_len = SILC_ID_CLIENT_LEN;
+ packetdata.src_id_type = SILC_ID_CLIENT;
+ packetdata.dst_id = silc_id_id2str(client_entry->id, SILC_ID_CLIENT);
+ packetdata.dst_id_len = SILC_ID_CLIENT_LEN;
+ packetdata.dst_id_type = SILC_ID_CLIENT;
+ packetdata.truelen = buffer->len + SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len + packetdata.dst_id_len;
+ packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len +
+ packetdata.dst_id_len));
+
+ /* Prepare outgoing data buffer for packet sending */
+ silc_packet_send_prepare(sock,
+ SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len +
+ packetdata.dst_id_len,
+ packetdata.padlen,
+ buffer->len);
+
+ packetdata.buffer = sock->outbuf;
+
+ /* Encrypt payload of the packet. Encrypt with private message specific
+ key */
+ cipher->cipher->encrypt(cipher->context, buffer->data, buffer->data,
+ buffer->len, cipher->iv);
+
+ /* Put the actual encrypted payload data into the buffer. */
+ silc_buffer_put(sock->outbuf, buffer->data, buffer->len);
+
+ /* Create the outgoing packet */
+ silc_packet_assemble(&packetdata);
+
+ /* Encrypt the header and padding of the packet. */
+ cipher = conn->send_key;
+ silc_packet_encrypt(cipher, hmac, sock->outbuf, SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len + packetdata.dst_id_len +
+ packetdata.padlen);
+
+ SILC_LOG_HEXDUMP(("Private message packet, len %d", sock->outbuf->len),
+ sock->outbuf->data, sock->outbuf->len);
+
+ /* Now actually send the packet */
+ silc_client_packet_send_real(client, sock, force_send);
+ silc_free(packetdata.dst_id);
+
+ out:
+ silc_free(buffer);
+}
+
+/* Private message received. This processes the private message and
+ finally displays it on the screen. */
+
+void silc_client_private_message(SilcClient client,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+ SilcBuffer buffer = packet->buffer;
+ SilcIDCacheEntry id_cache;
+ SilcClientID *remote_id = NULL;
+ SilcClientEntry remote_client;
+ unsigned short nick_len;
+ unsigned char *nickname, *message = NULL;
+ int ret;
+
+ if (packet->src_id_type != SILC_ID_CLIENT)
+ goto out;
+
+ /* Get nickname */
+ ret = silc_buffer_unformat(buffer,
+ SILC_STR_UI16_NSTRING_ALLOC(&nickname, &nick_len),
+ SILC_STR_END);
+ if (ret == -1)
+ return;
+
+ silc_buffer_pull(buffer, 2 + nick_len);
+
+ message = silc_calloc(buffer->len + 1, sizeof(char));
+ memcpy(message, buffer->data, buffer->len);
+
+ remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+ SILC_ID_CLIENT);
+ if (!remote_id)
+ goto out;
+
+ /* Check whether we know this client already */
+ if (!silc_idcache_find_by_id_one(conn->client_cache, remote_id,
+ SILC_ID_CLIENT, &id_cache))
+ {
+ /* Allocate client entry */
+ remote_client = silc_calloc(1, sizeof(*remote_client));
+ remote_client->id = remote_id;
+ silc_parse_nickname(nickname, &remote_client->nickname,
+ &remote_client->server, &remote_client->num);
+
+ /* Save the client to cache */
+ silc_idcache_add(conn->client_cache, remote_client->nickname,
+ SILC_ID_CLIENT, remote_client->id, remote_client,
+ TRUE);
+ } else {
+ remote_client = (SilcClientEntry)id_cache->context;
+ }
+
+ /* Pass the private message to application */
+ client->ops->private_message(client, conn, remote_client, message);
+
+ /* See if we are away (gone). If we are away we will reply to the
+ sender with the set away message. */
+ if (conn->away && conn->away->away) {
+ /* If it's me, ignore */
+ if (!SILC_ID_CLIENT_COMPARE(remote_id, conn->local_id))
+ goto out;
+
+ /* Send the away message */
+ silc_client_send_private_message(client, conn, remote_client,
+ conn->away->away,
+ strlen(conn->away->away), TRUE);
+ }
+
+ out:
+ if (remote_id)
+ silc_free(remote_id);
+
+ if (message) {
+ memset(message, 0, buffer->len);
+ silc_free(message);
+ }
+ silc_free(nickname);
+}
+
+/* Function that actually employes the received private message key */
+
+static void silc_client_private_message_key_cb(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry *clients,
+ unsigned int clients_count,
+ void *context)
+{
+ SilcPacketContext *packet = (SilcPacketContext *)context;
+ unsigned char *key;
+ unsigned short key_len;
+ unsigned char *cipher;
+ int ret;
+
+ if (!clients)
+ goto out;
+
+ /* Parse the private message key payload */
+ ret = silc_buffer_unformat(packet->buffer,
+ SILC_STR_UI16_NSTRING(&key, &key_len),
+ SILC_STR_UI16_STRING(&cipher),
+ SILC_STR_END);
+ if (!ret)
+ goto out;
+
+ if (key_len > packet->buffer->len)
+ goto out;
+
+ /* Now take the key in use */
+ if (!silc_client_add_private_message_key(client, conn, clients[0],
+ cipher, key, key_len, FALSE))
+ goto out;
+
+ /* Print some info for application */
+ client->ops->say(client, conn,
+ "Received private message key from %s%s%s %s%s%s",
+ clients[0]->nickname,
+ clients[0]->server ? "@" : "",
+ clients[0]->server ? clients[0]->server : "",
+ clients[0]->username ? "(" : "",
+ clients[0]->username ? clients[0]->username : "",
+ clients[0]->username ? ")" : "");
+
+ out:
+ silc_packet_context_free(packet);
+}
+
+/* Processes incoming Private Message Key payload. The libary always
+ accepts the key and takes it into use. */
+
+void silc_client_private_message_key(SilcClient client,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcClientID *remote_id;
+
+ if (packet->src_id_type != SILC_ID_CLIENT)
+ return;
+
+ remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+ SILC_ID_CLIENT);
+ if (!remote_id)
+ return;
+
+ silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
+ silc_client_private_message_key_cb,
+ silc_packet_context_dup(packet));
+ silc_free(remote_id);
+}
+
+/* Adds private message key to the client library. The key will be used to
+ encrypt all private message between the client and the remote client
+ indicated by the `client_entry'. If the `key' is NULL and the boolean
+ value `generate_key' is TRUE the library will generate random key.
+ The `key' maybe for example pre-shared-key, passphrase or similar.
+ The `cipher' MAY be provided but SHOULD be NULL to assure that the
+ requirements of the SILC protocol are met. The API, however, allows
+ to allocate any cipher.
+
+ It is not necessary to set key for normal private message usage. If the
+ key is not set then the private messages are encrypted using normal
+ session keys. Setting the private key, however, increases the security.
+
+ Returns FALSE if the key is already set for the `client_entry', TRUE
+ otherwise. */
+
+int silc_client_add_private_message_key(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry client_entry,
+ char *cipher,
+ unsigned char *key,
+ unsigned int key_len,
+ int generate_key)
+{
+ unsigned char private_key[32];
+ unsigned int len;
+ int i;
+ SilcSKEKeyMaterial *keymat;
+
+ assert(client_entry);
+
+ /* Return FALSE if key already set */
+ if (client_entry->send_key && client_entry->receive_key)
+ return FALSE;
+
+ if (!cipher)
+ cipher = "aes-256-cbc";
+
+ /* Check the requested cipher */
+ if (!silc_cipher_is_supported(cipher))
+ return FALSE;
+
+ /* Generate key if not provided */
+ if (!key && generate_key == TRUE) {
+ len = 32;
+ for (i = 0; i < len; i++) private_key[i] = silc_rng_get_byte(client->rng);
+ key = private_key;
+ key_len = len;
+ client_entry->generated = TRUE;
+ }
+
+ /* Save the key */
+ client_entry->key = silc_calloc(key_len, sizeof(*client_entry->key));
+ memcpy(client_entry->key, key, key_len);
+ client_entry->key_len = key_len;
+
+ /* Produce the key material as the protocol defines */
+ keymat = silc_calloc(1, sizeof(*keymat));
+ if (silc_ske_process_key_material_data(key, key_len, 16, 256, 16,
+ client->md5hash, keymat)
+ != SILC_SKE_STATUS_OK)
+ return FALSE;
+
+ /* Allocate the ciphers */
+ silc_cipher_alloc(cipher, &client_entry->send_key);
+ silc_cipher_alloc(cipher, &client_entry->receive_key);
+
+ /* Set the keys */
+ silc_cipher_set_key(client_entry->send_key, keymat->send_enc_key,
+ keymat->enc_key_len);
+ silc_cipher_set_iv(client_entry->send_key, keymat->send_iv);
+ silc_cipher_set_key(client_entry->receive_key, keymat->receive_enc_key,
+ keymat->enc_key_len);
+ silc_cipher_set_iv(client_entry->receive_key, keymat->receive_iv);
+
+ /* Free the key material */
+ silc_ske_free_key_material(keymat);
+
+ return TRUE;
+}
+
+/* Same as above but takes the key material from the SKE key material
+ structure. This structure is received if the application uses the
+ silc_client_send_key_agreement to negotiate the key material. The
+ `cipher' SHOULD be provided as it is negotiated also in the SKE
+ protocol. */
+
+int silc_client_add_private_message_key_ske(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry client_entry,
+ char *cipher,
+ SilcSKEKeyMaterial *key)
+{
+ assert(client_entry);
+
+ /* Return FALSE if key already set */
+ if (client_entry->send_key && client_entry->receive_key)
+ return FALSE;
+
+ if (!cipher)
+ cipher = "aes-256-cbc";
+
+ /* Check the requested cipher */
+ if (!silc_cipher_is_supported(cipher))
+ return FALSE;
+
+ /* Allocate the ciphers */
+ silc_cipher_alloc(cipher, &client_entry->send_key);
+ silc_cipher_alloc(cipher, &client_entry->receive_key);
+
+ /* Set the keys */
+ silc_cipher_set_key(client_entry->send_key, key->send_enc_key,
+ key->enc_key_len);
+ silc_cipher_set_iv(client_entry->send_key, key->send_iv);
+ silc_cipher_set_key(client_entry->receive_key, key->receive_enc_key,
+ key->enc_key_len);
+ silc_cipher_set_iv(client_entry->receive_key, key->receive_iv);
+
+ return TRUE;
+}
+
+/* Sends private message key payload to the remote client indicated by
+ the `client_entry'. If the `force_send' is TRUE the packet is sent
+ immediately. Returns FALSE if error occurs, TRUE otherwise. The
+ application should call this function after setting the key to the
+ client.
+
+ Note that the key sent using this function is sent to the remote client
+ through the SILC network. The packet is protected using normal session
+ keys. */
+
+int silc_client_send_private_message_key(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry client_entry,
+ int force_send)
+{
+ SilcSocketConnection sock = conn->sock;
+ SilcBuffer buffer;
+ int cipher_len;
+
+ if (!client_entry->send_key || !client_entry->key)
+ return FALSE;
+
+ SILC_LOG_DEBUG(("Sending private message key"));
+
+ cipher_len = strlen(client_entry->send_key->cipher->name);
+
+ /* Create private message key payload */
+ buffer = silc_buffer_alloc(2 + client_entry->key_len);
+ silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+ silc_buffer_format(buffer,
+ SILC_STR_UI_SHORT(client_entry->key_len),
+ SILC_STR_UI_XNSTRING(client_entry->key,
+ client_entry->key_len),
+ SILC_STR_UI_SHORT(cipher_len),
+ SILC_STR_UI_XNSTRING(client_entry->send_key->cipher->name,
+ cipher_len),
+ SILC_STR_END);
+
+ /* Send the packet */
+ silc_client_packet_send(client, sock, SILC_PACKET_PRIVATE_MESSAGE_KEY,
+ client_entry->id, SILC_ID_CLIENT, NULL, NULL,
+ buffer->data, buffer->len, force_send);
+ silc_free(buffer);
+
+ return TRUE;
+}
+
+/* Removes the private message from the library. The key won't be used
+ after this to protect the private messages with the remote `client_entry'
+ client. Returns FALSE on error, TRUE otherwise. */
+
+int silc_client_del_private_message_key(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry client_entry)
+{
+ assert(client_entry);
+
+ if (!client_entry->send_key && !client_entry->receive_key)
+ return FALSE;
+
+ silc_cipher_free(client_entry->send_key);
+ silc_cipher_free(client_entry->receive_key);
+
+ if (client_entry->key) {
+ memset(client_entry->key, 0, client_entry->key_len);
+ silc_free(client_entry->key);
+ }
+
+ client_entry->send_key = NULL;
+ client_entry->receive_key = NULL;
+ client_entry->key = NULL;
+
+ return TRUE;
+}
+
+/* Returns array of set private message keys associated to the connection
+ `conn'. Returns allocated SilcPrivateMessageKeys array and the array
+ count to the `key_count' argument. The array must be freed by the caller
+ by calling the silc_client_free_private_message_keys function. Note:
+ the keys returned in the array is in raw format. It might not be desired
+ to show the keys as is. The application might choose not to show the keys
+ at all or to show the fingerprints of the keys. */
+
+SilcPrivateMessageKeys
+silc_client_list_private_message_keys(SilcClient client,
+ SilcClientConnection conn,
+ unsigned int *key_count)
+{
+ SilcPrivateMessageKeys keys;
+ unsigned int count = 0;
+ SilcIDCacheEntry id_cache;
+ SilcIDCacheList list;
+ SilcClientEntry entry;
+
+ if (!silc_idcache_find_by_id(conn->client_cache, SILC_ID_CACHE_ANY,
+ SILC_ID_CLIENT, &list))
+ return NULL;
+
+ if (!silc_idcache_list_count(list)) {
+ silc_idcache_list_free(list);
+ return NULL;
+ }
+
+ keys = silc_calloc(silc_idcache_list_count(list), sizeof(*keys));
+
+ silc_idcache_list_first(list, &id_cache);
+ while (id_cache) {
+ entry = (SilcClientEntry)id_cache->context;
+
+ if (entry->send_key) {
+ keys[count].client_entry = entry;
+ keys[count].cipher = entry->send_key->cipher->name;
+ keys[count].key = entry->generated == FALSE ? entry->key : NULL;
+ keys[count].key_len = entry->generated == FALSE ? entry->key_len : 0;
+ count++;
+ }
+
+ if (!silc_idcache_list_next(list, &id_cache))
+ break;
+ }
+
+ if (key_count)
+ *key_count = count;
+
+ return keys;
+}
+
+/* Frees the SilcPrivateMessageKeys array returned by the function
+ silc_client_list_private_message_keys. */
+
+void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
+ unsigned int key_count)
+{
+ silc_free(keys);
+}
/* $Id$ */
#include "clientlibincludes.h"
+#include "client_internal.h"
/* Client command list. */
SilcClientCommand silc_command_list[] =
SILC_CLIENT_CMD(ping, PING, "PING", SILC_CF_LAG | SILC_CF_REG, 2),
SILC_CLIENT_CMD(oper, OPER, "OPER",
SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
- SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 4),
+ SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 5),
SILC_CLIENT_CMD(motd, MOTD, "MOTD", SILC_CF_LAG | SILC_CF_REG, 2),
SILC_CLIENT_CMD(umode, UMODE, "UMODE", SILC_CF_LAG | SILC_CF_REG, 2),
SILC_CLIENT_CMD(cmode, CMODE, "CMODE", SILC_CF_LAG | SILC_CF_REG, 4),
/* $Id$ */
#include "clientlibincludes.h"
+#include "client_internal.h"
/* Client command reply list. */
SilcClientCommandReply silc_command_reply_list[] =
SilcClient client = cmd->client;
SilcCommandStatus status;
SilcIDPayload idp = NULL;
+ SilcChannelEntry channel;
unsigned int argc, mode, len;
- char *topic, *tmp, *channel_name = NULL;
+ char *topic, *tmp, *channel_name = NULL, *hmac;
SilcBuffer keyp;
SILC_LOG_DEBUG(("Start"));
silc_buffer_put(keyp, tmp, len);
/* Get topic */
- topic = silc_argument_get_arg_type(cmd->args, 8, NULL);
+ topic = silc_argument_get_arg_type(cmd->args, 9, NULL);
- /* Save received Channel ID */
- silc_client_new_channel_id(cmd->client, cmd->sock, channel_name,
- mode, idp);
+ /* Save received Channel ID. This actually creates the channel */
+ channel = silc_client_new_channel_id(cmd->client, cmd->sock, channel_name,
+ mode, idp);
silc_id_payload_free(idp);
+ /* Get hmac */
+ hmac = silc_argument_get_arg_type(cmd->args, 10, NULL);
+ if (hmac) {
+ if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
+ cmd->client->ops->say(cmd->client, conn,
+ "Cannot join channel: Unsupported HMAC `%s'",
+ hmac);
+ COMMAND_REPLY_ERROR;
+ silc_free(channel_name);
+ goto out;
+ }
+ }
+
/* Save channel key */
- silc_client_save_channel_key(conn, keyp, conn->current_channel);
+ silc_client_save_channel_key(conn, keyp, channel);
silc_buffer_free(keyp);
if (topic)
"Topic for %s: %s", channel_name, topic);
/* Notify application */
- COMMAND_REPLY((ARGS, channel_name, conn->current_channel, mode,
- NULL, NULL, topic));
+ COMMAND_REPLY((ARGS, channel_name, channel, mode, NULL, NULL, topic, hmac));
/* Execute any pending command callbacks */
SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
GetClientByIDInternal i = silc_calloc(1, sizeof(*i));
idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
- silc_client_send_command(client, conn, SILC_COMMAND_IDENTIFY,
+ silc_client_send_command(client, conn, SILC_COMMAND_WHOIS,
++conn->cmd_ident,
1, 3, idp->data, idp->len);
silc_buffer_free(idp);
i->context = context;
/* Add pending callback */
- silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
+ silc_client_command_pending(conn, SILC_COMMAND_WHOIS,
++conn->cmd_ident,
silc_client_get_client_by_id_destructor,
silc_client_command_get_client_by_id_callback,
generated the key. */
unsigned int key_len;
int generated; /* TRUE if library generated the key */
+ SilcClientKeyAgreement ke; /* Current key agreement context or NULL */
} *SilcClientEntry;
/* Client and its mode on a channel */
unsigned char *key;
unsigned int key_len;
unsigned char iv[SILC_CIPHER_MAX_IV_SIZE];
+ SilcHmac hmac;
} *SilcChannelEntry;
/* Prototypes (some functions are defined in the silcapi.h) */
/* $Id$ */
#include "clientlibincludes.h"
+#include "client_internal.h"
SILC_TASK_CALLBACK(silc_client_protocol_connection_auth);
SILC_TASK_CALLBACK(silc_client_protocol_key_exchange);
/* Function that is called when SKE protocol sends packets to network. */
-static void silc_client_protocol_ke_send_packet(SilcSKE ske,
- SilcBuffer packet,
- SilcPacketType type,
- void *context)
+void silc_client_protocol_ke_send_packet(SilcSKE ske,
+ SilcBuffer packet,
+ SilcPacketType type,
+ void *context)
{
SilcProtocol protocol = (SilcProtocol)context;
SilcClientKEInternalContext *ctx =
/* Sets the negotiated key material into use for particular connection. */
-static void silc_client_protocol_ke_set_keys(SilcSKE ske,
- SilcSocketConnection sock,
- SilcSKEKeyMaterial *keymat,
- SilcCipher cipher,
- SilcPKCS pkcs,
- SilcHash hash)
+void silc_client_protocol_ke_set_keys(SilcSKE ske,
+ SilcSocketConnection sock,
+ SilcSKEKeyMaterial *keymat,
+ SilcCipher cipher,
+ SilcPKCS pkcs,
+ SilcHash hash,
+ SilcHmac hmac)
{
SilcClientConnection conn = (SilcClientConnection)sock->user_data;
- SilcHash nhash;
SILC_LOG_DEBUG(("Setting new keys into use"));
#endif
/* Save HMAC key to be used in the communication. */
- silc_hash_alloc(hash->hash->name, &nhash);
- silc_hmac_alloc(nhash, &conn->hmac);
+ silc_hmac_alloc(hmac->hmac->name, NULL, &conn->hmac);
silc_hmac_set_key(conn->hmac, keymat->hmac_key, keymat->hmac_key_len);
}
ske->user_data = (void *)client;
if (ctx->responder == TRUE) {
-#if 0
- SilcBuffer start_payload;
-
-
/* Start the key exchange by processing the received security
properties packet from initiator. */
status = silc_ske_responder_start(ske, ctx->rng, ctx->sock,
- start_payload,
- silc_client_protocol_ke_send_packet,
- context);
-#endif
+ silc_version_string,
+ ctx->packet->buffer, NULL, NULL);
} else {
SilcSKEStartPayload *start_payload;
to the remote end. */
status = silc_ske_initiator_start(ske, ctx->rng, ctx->sock,
start_payload,
- silc_client_protocol_ke_send_packet,
+ ctx->send_packet,
context);
}
return;
}
- /* Advance the state of the protocol. */
+ /* Advance protocol state and call the next state if we are responder */
protocol->state++;
+ if (ctx->responder == TRUE)
+ protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 100000);
}
break;
case 2:
* Phase 1
*/
if (ctx->responder == TRUE) {
-#if 0
+ /* Sends the selected security properties to the initiator. */
status =
silc_ske_responder_phase_1(ctx->ske,
ctx->ske->start_payload,
- silc_server_protocol_ke_send_packet,
+ ctx->send_packet,
context);
-#endif
} else {
/* Call Phase-1 function. This processes the Key Exchange Start
paylaod reply we just got from the responder. The callback
return;
}
- /* Advance the state of the protocol and call the next state. */
+ /* Advance protocol state and call next state if we are initiator */
protocol->state++;
- protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 0);
+ if (ctx->responder == FALSE)
+ protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 100000);
}
break;
case 3:
* Phase 2
*/
if (ctx->responder == TRUE) {
-#if 0
- status =
- silc_ske_responder_phase_2(ctx->ske,
- ctx->ske->start_payload,
- silc_server_protocol_ke_send_packet,
- context);
-#endif
+ /* Process the received Key Exchange 1 Payload packet from
+ the initiator. This also creates our parts of the Diffie
+ Hellman algorithm. */
+ status = silc_ske_responder_phase_2(ctx->ske, ctx->packet->buffer,
+ NULL, NULL);
} else {
/* Call the Phase-2 function. This creates Diffie Hellman
key exchange parameters and sends our public part inside
status =
silc_ske_initiator_phase_2(ctx->ske,
client->public_key,
- silc_client_protocol_ke_send_packet,
+ ctx->send_packet,
context);
}
return;
}
- /* Advance the state of the protocol. */
+ /* Advance protocol state and call the next state if we are responder */
protocol->state++;
+ if (ctx->responder == TRUE)
+ protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 100000);
}
break;
case 4:
* Finish protocol
*/
if (ctx->responder == TRUE) {
- status = 0;
-#if 0
+ /* This creates the key exchange material and sends our
+ public parts to the initiator inside Key Exchange 2 Payload. */
status =
- silc_ske_responder_phase_2(ctx->ske,
- ctx->ske->start_payload,
- silc_server_protocol_ke_send_packet,
- context);
-#endif
+ silc_ske_responder_finish(ctx->ske,
+ client->public_key, client->private_key,
+ SILC_SKE_PK_TYPE_SILC,
+ ctx->send_packet,
+ context);
+ status = 0;
} else {
/* Finish the protocol. This verifies the Key Exchange 2 payload
sent by responder. */
/* Send Ok to the other end. We will end the protocol as server
sends Ok to us when we will take the new keys into use. */
- silc_ske_end(ctx->ske, silc_client_protocol_ke_send_packet, context);
+ if (ctx->responder == FALSE)
+ silc_ske_end(ctx->ske, ctx->send_packet, context);
/* End the protocol on the next round */
protocol->state = SILC_PROTOCOL_STATE_END;
* End protocol
*/
SilcSKEKeyMaterial *keymat;
- int key_len = silc_cipher_get_key_len(ctx->ske->prop->cipher, NULL);
+ int key_len = silc_cipher_get_key_len(ctx->ske->prop->cipher);
int hash_len = ctx->ske->prop->hash->hash->hash_len;
/* Process the key material */
keymat = silc_calloc(1, sizeof(*keymat));
- silc_ske_process_key_material(ctx->ske, 16, key_len, hash_len,
- keymat);
+ status = silc_ske_process_key_material(ctx->ske, 16, key_len, hash_len,
+ keymat);
+ if (status != SILC_SKE_STATUS_OK) {
+ protocol->state = SILC_PROTOCOL_STATE_ERROR;
+ protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 300000);
+ silc_ske_free_key_material(keymat);
+ return;
+ }
+ ctx->keymat = keymat;
- /* Take the negotiated keys into use. */
- silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, keymat,
- ctx->ske->prop->cipher,
- ctx->ske->prop->pkcs,
- ctx->ske->prop->hash);
+ /* Send Ok to the other end if we are responder. If we are initiator
+ we have sent this already. */
+ if (ctx->responder == TRUE)
+ silc_ske_end(ctx->ske, ctx->send_packet, context);
- silc_ske_free_key_material(keymat);
+ /* Unregister the timeout task since the protocol has ended.
+ This was the timeout task to be executed if the protocol is
+ not completed fast enough. */
+ if (ctx->timeout_task)
+ silc_task_unregister(client->timeout_queue, ctx->timeout_task);
/* Protocol has ended, call the final callback */
if (protocol->final_callback)
/* Send abort notification */
silc_ske_abort(ctx->ske, ctx->ske->status,
- silc_client_protocol_ke_send_packet,
- context);
+ ctx->send_packet, context);
/* On error the final callback is always called. */
if (protocol->final_callback)
* Received failure from remote.
*/
+ /* Unregister the timeout task since the protocol has ended.
+ This was the timeout task to be executed if the protocol is
+ not completed fast enough. */
+ if (ctx->timeout_task)
+ silc_task_unregister(client->timeout_queue, ctx->timeout_task);
+
/* On error the final callback is always called. */
if (protocol->final_callback)
protocol->execute_final(client->timeout_queue, 0, protocol, fd);
SilcRng rng;
int responder;
- /* Destinations ID taken from authenticataed packet so that we can
- get the destinations ID. */
- void *dest_id;
- SilcIdType dest_id_type;
+ void *dest_id; /* Destination ID from packet */
+ SilcIdType dest_id_type; /* Destination ID type */
+ SilcTask timeout_task;
SilcPacketContext *packet;
- SilcSKE ske;
+
+ SilcSKESendPacketCb send_packet; /* SKE's packet sending callback */
+ SilcSKE ske; /* The SKE object */
+ SilcSKEKeyMaterial *keymat; /* The negotiated key material */
+ void *context; /* Internal context */
} SilcClientKEInternalContext;
/* Internal context for connection authentication protocol */
/* Prototypes */
void silc_client_protocols_register(void);
void silc_client_protocols_unregister(void);
+void silc_client_protocol_ke_send_packet(SilcSKE ske,
+ SilcBuffer packet,
+ SilcPacketType type,
+ void *context);
+void silc_client_protocol_ke_set_keys(SilcSKE ske,
+ SilcSocketConnection sock,
+ SilcSKEKeyMaterial *keymat,
+ SilcCipher cipher,
+ SilcPKCS pkcs,
+ SilcHash hash,
+ SilcHmac hmac);
#endif
of how to use the SILC Client Library.
*/
+/* 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. */
+typedef void (*SilcKeyAgreementCallback)(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry client_entry,
+ SilcSKEKeyMaterial *key,
+ void *context);
+
/******************************************************************************
SILC Client Operations
reply to our key agreement packet. This returns TRUE if the user wants
the library to perform the key agreement protocol and FALSE if it is not
desired (application may start it later by calling the function
- silc_client_perform_key_agreement). */
+ silc_client_perform_key_agreement). If TRUE is returned also the
+ `completion' and `context' arguments must be set by the application. */
int (*key_agreement)(SilcClient client, SilcClientConnection conn,
SilcClientEntry client_entry, char *hostname,
- int port);
+ int port,
+ SilcKeyAgreementCallback *completion,
+ void **context);
} SilcClientOperations;
******************************************************************************/
-/* Initialization functions */
+/* Initialization functions (client.c) */
/* Allocates new client object. This has to be done before client may
work. After calling this one must call silc_client_init to initialize
void silc_client_stop(SilcClient client);
-/* Connecting functions */
+/* Connecting functions (client.c) */
/* Connects to remote server. This is the main routine used to connect
to SILC server. Returns -1 on error and the created socket otherwise.
SilcClientConnection conn);
-/* Message sending functions */
+/* Message sending functions (client_channel.c and client_prvmsg.c) */
/* Sends packet to the `channel'. Packet to channel is always encrypted
differently from "normal" packets. SILC header of the packet is
int force_send);
-/* Client and Channel entry retrieval */
+/* Client and Channel entry retrieval (idlist.c) */
/* Callback function given to the silc_client_get_client function. The
found entries are allocated into the `clients' array. The array must
char *channel);
-/* Command management */
+/* Command management (command.c) */
/* Allocate Command Context. The context is defined in `command.h' file.
The context is used by the library commands and applications should use
void *context);
-/* Private Message key management */
+/* Private Message key management (client_prvmsg.c) */
/* Adds private message key to the client library. The key will be used to
encrypt all private message between the client and the remote client
unsigned int key_count);
-/* Channel private key management */
+/* Channel private key management (client_channel.c) */
/* Adds private key for channel. This may be set only if the channel's mode
mask includes the SILC_CHANNEL_MODE_PRIVKEY. This returns FALSE if the
silc_client_list_channel_private_keys(SilcClient client,
SilcClientConnection conn,
SilcChannelEntry channel,
- unsigned int key_count);
+ unsigned int *key_count);
/* Frees the SilcChannelPrivateKey array. */
void silc_client_free_channel_private_keys(SilcChannelPrivateKey *keys,
unsigned int key_count);
-/* Key Agreement routines */
-
-/* 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. */
-typedef void (*SilcKeyAgreementCallback)(SilcClient client,
- SilcClientConnection conn,
- SilcClientEntry client_entry,
- SilcSKEKeyMaterial *key,
- void *context);
+/* Key Agreement routines (client_keyagr.c) */
/* Sends key agreement request to the remote client indicated by the
`client_entry'. If the caller provides the `hostname' and the `port'
or decides not to reply with the key agreement packet then we cannot
perform the key agreement at all. If the key agreement protocol is
performed the `completion' callback with the `context' will be called.
- If remote side decides to ignore the request the `completion' will never
- be called and the caller is responsible of freeing the `context' memory.
- The application can do this by setting, for example, timeout. */
+ If remote side decides to ignore the request the `completion' will be
+ called after the specified timeout, `timeout_secs'.
+
+ NOTE: There can be only one active key agreement for one client entry.
+ Before setting new one, the old one must be finished (it is finished
+ after calling the completion callback) or the function
+ silc_client_abort_key_agreement must be called. */
void silc_client_send_key_agreement(SilcClient client,
SilcClientConnection conn,
SilcClientEntry client_entry,
char *hostname,
int port,
+ unsigned long timeout_secs,
SilcKeyAgreementCallback completion,
void *context);
and did not return TRUE from it.
The `hostname' is the remote hostname (or IP address) and the `port'
- is the remote port. The `completion' callblack with the `context' will
+ is the remote port. The `completion' callback with the `context' will
be called after the key agreement protocol.
NOTE: If the application returns TRUE in the `key_agreement' client
SilcKeyAgreementCallback completion,
void *context);
+/* Same as above but application has created already the connection to
+ the remote host. The `sock' is the socket to the remote connection.
+ Application can use this function if it does not want the client library
+ to create the connection. */
+void silc_client_perform_key_agreement_fd(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry client_entry,
+ int sock,
+ SilcKeyAgreementCallback completion,
+ void *context);
+
/* This function can be called to unbind the hostname and the port for
the key agreement protocol. However, this function has effect only
before the key agreement protocol has been performed. After it has
unsigned int port)
{
SilcBuffer buffer;
- unsigned int len = strlen(hostname);
+ unsigned int len = hostname ? strlen(hostname) : 0;
SILC_LOG_DEBUG(("Encoding Key Agreement Payload"));
/******************************************************************************
- Channel Payload
+ Channel Message Payload
******************************************************************************/
-/* Channel Payload structure. Contents of this structure is parsed
+/* Channel Message Payload structure. Contents of this structure is parsed
from SILC packets. */
struct SilcChannelPayloadStruct {
unsigned short data_len;
unsigned char *data;
- unsigned short iv_len;
+ unsigned char *mac;
unsigned char *iv;
};
-/* Parses channel payload returning new channel payload structure */
+/* Parses channel payload returning new channel payload structure. This
+ also decrypts it and checks the MAC. */
-SilcChannelPayload silc_channel_payload_parse(SilcBuffer buffer)
+SilcChannelPayload silc_channel_payload_parse(SilcBuffer buffer,
+ SilcCipher cipher,
+ SilcHmac hmac)
{
SilcChannelPayload new;
int ret;
+ unsigned int iv_len, mac_len;
+ unsigned char *mac, mac2[32];
SILC_LOG_DEBUG(("Parsing channel payload"));
+ /* Decrypt the channel message. First push the IV out of the packet.
+ The IV is used in the decryption process. Then decrypt the message.
+ After decyprtion, take the MAC from the decrypted packet, compute MAC
+ and compare the MACs. If they match, the decryption was successfull
+ and we have the channel message ready to be displayed. */
+
+ /* Push the IV out of the packet (it will be in buffer->tail) */
+ iv_len = silc_cipher_get_block_len(cipher);
+ silc_buffer_push_tail(buffer, iv_len);
+
+ /* Decrypt the channel message */
+ silc_cipher_decrypt(cipher, buffer->data, buffer->data,
+ buffer->len, buffer->tail);
+
+ /* Take the MAC */
+ mac_len = silc_hmac_len(hmac);
+ silc_buffer_push_tail(buffer, mac_len);
+ mac = buffer->tail;
+
+ /* Check the MAC of the message */
+ SILC_LOG_DEBUG(("Checking channel message MACs"));
+ silc_hmac_make(hmac, buffer->data, buffer->len, mac2, &mac_len);
+ if (memcmp(mac, mac2, mac_len)) {
+ SILC_LOG_DEBUG(("Channel message MACs does not match"));
+ return NULL;
+ }
+ SILC_LOG_DEBUG(("MAC is Ok"));
+ silc_buffer_pull_tail(buffer, iv_len + mac_len);
+
new = silc_calloc(1, sizeof(*new));
- /* Parse the Channel Payload. Ignore padding and IV, we don't need
- them. */
+ /* Parse the Channel Payload. Ignore the padding. */
ret = silc_buffer_unformat(buffer,
- SILC_STR_UI16_NSTRING_ALLOC(&new->data,
- &new->data_len),
- SILC_STR_UI16_NSTRING_ALLOC(NULL, NULL),
+ SILC_STR_UI16_NSTRING(&new->data,
+ &new->data_len),
+ SILC_STR_UI16_NSTRING(NULL, NULL),
+ SILC_STR_UI_XNSTRING(&new->mac, mac_len),
+ SILC_STR_UI_XNSTRING(&new->iv, iv_len),
SILC_STR_END);
if (ret == -1)
goto err;
return new;
err:
- if (new->data)
- silc_free(new->data);
- if (new->iv)
- silc_free(new->iv);
silc_free(new);
return NULL;
}
unsigned char *data,
unsigned short iv_len,
unsigned char *iv,
+ SilcCipher cipher,
+ SilcHmac hmac,
SilcRng rng)
{
int i;
SilcBuffer buffer;
- unsigned int len, pad_len;
+ unsigned int len, pad_len, mac_len;
unsigned char pad[SILC_PACKET_MAX_PADLEN];
+ unsigned char mac[32];
SILC_LOG_DEBUG(("Encoding channel payload"));
/* Calculate length of padding. IV is not included into the calculation
since it is not encrypted. */
- len = 2 + data_len + 2;
+ mac_len = silc_hmac_len(hmac);
+ len = 4 + data_len + mac_len;
pad_len = SILC_PACKET_PADLEN((len + 2));
/* Allocate channel payload buffer */
- len += pad_len;
- buffer = silc_buffer_alloc(len + iv_len);
- silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+ len += pad_len + iv_len;
+ buffer = silc_buffer_alloc(len);
/* Generate padding */
for (i = 0; i < pad_len; i++) pad[i] = silc_rng_get_byte(rng);
/* Encode the Channel Payload */
+ silc_buffer_pull_tail(buffer, 4 + data_len + pad_len);
silc_buffer_format(buffer,
SILC_STR_UI_SHORT(data_len),
SILC_STR_UI_XNSTRING(data, data_len),
SILC_STR_UI_SHORT(pad_len),
SILC_STR_UI_XNSTRING(pad, pad_len),
+ SILC_STR_END);
+
+ /* Compute the MAC of the channel message data */
+ silc_hmac_make(hmac, buffer->data, buffer->len, mac, &mac_len);
+
+ /* Put rest of the data to the payload */
+ silc_buffer_pull_tail(buffer, mac_len + iv_len);
+ silc_buffer_pull(buffer, 4 + data_len + pad_len);
+ silc_buffer_format(buffer,
+ SILC_STR_UI_XNSTRING(mac, mac_len),
SILC_STR_UI_XNSTRING(iv, iv_len),
SILC_STR_END);
+ silc_buffer_push(buffer, 4 + data_len + pad_len);
+
+ /* Encrypt payload of the packet. This is encrypted with the channel key. */
+ silc_cipher_encrypt(cipher, buffer->data, buffer->data,
+ buffer->len - iv_len, iv);
+
+ memset(pad, 0, sizeof(pad));
+ memset(mac, 0, sizeof(mac));
- memset(pad, 0, pad_len);
return buffer;
}
void silc_channel_payload_free(SilcChannelPayload payload)
{
- if (payload) {
- if (payload->data)
- silc_free(payload->data);
- if (payload->iv)
- silc_free(payload->iv);
+ if (payload)
silc_free(payload);
- }
}
/* Return data */
return payload->data;
}
-/* Return initial vector */
+/* Return MAC. The caller knows the length of the MAC */
-unsigned char *silc_channel_get_iv(SilcChannelPayload payload,
- unsigned int *iv_len)
+unsigned char *silc_channel_get_mac(SilcChannelPayload payload)
{
- if (iv_len)
- *iv_len = payload->iv_len;
+ return payload->mac;
+}
+
+/* Return IV. The caller knows the length of the IV */
+unsigned char *silc_channel_get_iv(SilcChannelPayload payload)
+{
return payload->iv;
}
#ifndef SILCCHANNEL_H
#define SILCCHANNEL_H
-/* Forward declaration for Channel Payload parsed from packet. The
+/* Forward declaration for Channel Message Payload parsed from packet. The
actual structure is defined in source file and is private data. */
typedef struct SilcChannelPayloadStruct *SilcChannelPayload;
typedef struct SilcChannelKeyPayloadStruct *SilcChannelKeyPayload;
/* Prototypes */
-SilcChannelPayload silc_channel_payload_parse(SilcBuffer buffer);
+SilcChannelPayload silc_channel_payload_parse(SilcBuffer buffer,
+ SilcCipher cipher,
+ SilcHmac hmac);
SilcBuffer silc_channel_payload_encode(unsigned short data_len,
unsigned char *data,
unsigned short iv_len,
unsigned char *iv,
+ SilcCipher cipher,
+ SilcHmac hmac,
SilcRng rng);
void silc_channel_payload_free(SilcChannelPayload payload);
unsigned char *silc_channel_get_data(SilcChannelPayload payload,
unsigned int *data_len);
-unsigned char *silc_channel_get_iv(SilcChannelPayload payload,
- unsigned int *iv_len);
+unsigned char *silc_channel_get_mac(SilcChannelPayload payload);
+unsigned char *silc_channel_get_iv(SilcChannelPayload payload);
SilcChannelKeyPayload silc_channel_key_payload_parse(SilcBuffer buffer);
SilcBuffer silc_channel_key_payload_encode(unsigned short id_len,
unsigned char *id,
#define SILC_CHANNEL_MODE_PASSPHRASE 0x0040 /* passphrase set */
#define SILC_CHANNEL_MODE_BAN 0x0080 /* ban list set */
#define SILC_CHANNEL_MODE_INVITE_LIST 0x0100 /* invite list set */
-#define SILC_CHANNEL_MODE_CIPHER 0x0200 /* sets cipher of channel */
+#define SILC_CHANNEL_MODE_CIPHER 0x0200 /* sets cipher of the channel */
+#define SILC_CHANNEL_MODE_HMAC 0x0400 /* sets hmac of the channel */
/* User modes on channel */
#define SILC_CHANNEL_UMODE_NONE 0x0000 /* Normal user */
SilcBuffer buffer, unsigned int len)
{
unsigned char mac[32];
+ unsigned int mac_len;
/* Compute HMAC. This assumes that HMAC is created from the entire
data area thus this uses the length found in buffer, not the length
sent as argument. */
if (hmac) {
- silc_hmac_make(hmac, buffer->data, buffer->len, mac);
- silc_buffer_put_tail(buffer, mac, hmac->hash->hash->hash_len);
+ silc_hmac_make(hmac, buffer->data, buffer->len, mac, &mac_len);
+ silc_buffer_put_tail(buffer, mac, mac_len);
memset(mac, 0, sizeof(mac));
}
/* Pull the HMAC into the visible data area in the buffer */
if (hmac)
- silc_buffer_pull_tail(buffer, hmac->hash->hash->hash_len);
+ silc_buffer_pull_tail(buffer, mac_len);
}
/* Assembles a new packet to be ready for send out. The buffer sent as
return;
if (hmac)
- mac_len = hmac->hash->hash->hash_len;
+ mac_len = hmac->hmac->len;
/* Parse the packets from the data */
count = 0;
/* Check MAC */
if (hmac) {
unsigned char mac[32];
+ unsigned int mac_len;
SILC_LOG_DEBUG(("Verifying MAC"));
/* Compute HMAC of packet */
memset(mac, 0, sizeof(mac));
- silc_hmac_make(hmac, buffer->data, buffer->len, mac);
+ silc_hmac_make(hmac, buffer->data, buffer->len, mac, &mac_len);
/* Compare the HMAC's (buffer->tail has the packet's HMAC) */
- if (memcmp(mac, buffer->tail, hmac->hash->hash->hash_len)) {
+ if (memcmp(mac, buffer->tail, mac_len)) {
SILC_LOG_DEBUG(("MAC failed"));
return FALSE;
}
/* Pull MAC from packet before decryption */
if (hmac) {
- if ((buffer->len - hmac->hash->hash->hash_len) > SILC_PACKET_MIN_LEN) {
- silc_buffer_push_tail(buffer, hmac->hash->hash->hash_len);
+ if ((buffer->len - hmac->hmac->len) > SILC_PACKET_MIN_LEN) {
+ silc_buffer_push_tail(buffer, hmac->hmac->len);
} else {
SILC_LOG_DEBUG(("Bad MAC length in packet, packet dropped"));
return FALSE;
/* Pull MAC from packet before decryption */
if (hmac) {
- if ((buffer->len - hmac->hash->hash->hash_len) > SILC_PACKET_MIN_LEN) {
- silc_buffer_push_tail(buffer, hmac->hash->hash->hash_len);
+ if ((buffer->len - hmac->hmac->len) > SILC_PACKET_MIN_LEN) {
+ silc_buffer_push_tail(buffer, hmac->hmac->len);
} else {
SILC_LOG_DEBUG(("Bad MAC length in packet, packet dropped"));
return FALSE;
if (!protocol) {
SILC_LOG_ERROR(("Requested protocol does not exists"));
+ *new_protocol = NULL;
return;
}
{
struct SilcCipherListStruct *new, *c;
- SILC_LOG_DEBUG(("Registering new cipher"));
+ SILC_LOG_DEBUG(("Registering new cipher `%s'", cipher->name));
new = silc_calloc(1, sizeof(*new));
new->cipher = silc_calloc(1, sizeof(*new->cipher));
return list;
}
+/* Encrypts */
+
+int silc_cipher_encrypt(SilcCipher cipher, const unsigned char *src,
+ unsigned char *dst, unsigned int len,
+ unsigned char *iv)
+{
+ return cipher->cipher->encrypt(cipher->context, src, dst, len, iv);
+}
+
+/* Decrypts */
+
+int silc_cipher_decrypt(SilcCipher cipher, const unsigned char *src,
+ unsigned char *dst, unsigned int len,
+ unsigned char *iv)
+{
+ return cipher->cipher->decrypt(cipher->context, src, dst, len, iv);
+}
+
/* Sets the key for the cipher */
int silc_cipher_set_key(SilcCipher cipher, const unsigned char *key,
/* Returns the key length of the cipher. */
-unsigned int silc_cipher_get_key_len(SilcCipher cipher,
- const unsigned char *name)
+unsigned int silc_cipher_get_key_len(SilcCipher cipher)
{
return cipher->cipher->key_len;
}
void silc_cipher_free(SilcCipher cipher);
int silc_cipher_is_supported(const unsigned char *name);
char *silc_cipher_get_supported();
+int silc_cipher_encrypt(SilcCipher cipher, const unsigned char *src,
+ unsigned char *dst, unsigned int len,
+ unsigned char *iv);
+int silc_cipher_decrypt(SilcCipher cipher, const unsigned char *src,
+ unsigned char *dst, unsigned int len,
+ unsigned char *iv);
int silc_cipher_set_key(SilcCipher cipher, const unsigned char *key,
unsigned int keylen);
void silc_cipher_set_iv(SilcCipher cipher, const unsigned char *iv);
void silc_cipher_get_iv(SilcCipher cipher, unsigned char *iv);
-unsigned int silc_cipher_get_key_len(SilcCipher cipher,
- const unsigned char *name);
+unsigned int silc_cipher_get_key_len(SilcCipher cipher);
unsigned int silc_cipher_get_block_len(SilcCipher cipher);
#endif
/* Statically declared list of hash functions. */
SilcHashObject silc_hash_builtin_list[] =
{
- { "md5", 16, 64, silc_md5_init, silc_md5_update, silc_md5_final,
- silc_md5_transform, silc_md5_context_len },
{ "sha1", 20, 64, silc_sha1_init, silc_sha1_update, silc_sha1_final,
silc_sha1_transform, silc_sha1_context_len },
+ { "md5", 16, 64, silc_md5_init, silc_md5_update, silc_md5_final,
+ silc_md5_transform, silc_md5_context_len },
{ NULL, 0, 0, NULL, NULL, NULL, NULL, NULL }
};
-/* Registers a ned hash function into the SILC. This function is used at
+/* Registers a new hash function into the SILC. This function is used at
the initialization of the SILC. */
int silc_hash_register(SilcHashObject *hash)
{
struct SilcHashListStruct *new, *h;
- SILC_LOG_DEBUG(("Registering new hash function"));
+ SILC_LOG_DEBUG(("Registering new hash function `%s'", hash->name));
new = silc_calloc(1, sizeof(*new));
new->hash = silc_calloc(1, sizeof(*new->hash));
/* Set the pointers */
- new->hash->name = silc_calloc(1, strlen(hash->name));
- memcpy(new->hash->name, hash->name, strlen(hash->name));
+ new->hash->name = strdup(hash->name);
new->hash->hash_len = hash->hash_len;
new->hash->block_len = hash->block_len;
new->hash->init = hash->init;
}
}
+/* Returns the length of the hash digest. */
+
+unsigned int silc_hash_len(SilcHash hash)
+{
+ return hash->hash->hash_len;
+}
+
/* Returns TRUE if hash algorithm `name' is supported. */
int silc_hash_is_supported(const unsigned char *name)
{
struct SilcHashListStruct *h;
int i;
+
+ if (!name)
+ return FALSE;
if (silc_hash_list) {
h = silc_hash_list;
int silc_hash_unregister(SilcHashObject *hash);
int silc_hash_alloc(const unsigned char *name, SilcHash *new_hash);
void silc_hash_free(SilcHash hash);
+unsigned int silc_hash_len(SilcHash hash);
int silc_hash_is_supported(const unsigned char *name);
char *silc_hash_get_supported();
void silc_hash_make(SilcHash hash, const unsigned char *data,
#include "silcincludes.h"
-/* Allocates a new SilcHmac object. First argument is the hash function
- object to tell the hmac which hash function should be used when creating
- HMAC's. The new SilcHmac object is returned to new_hmac argument. */
+/* List of dynamically registered HMACs. */
+SilcDList silc_hmac_list = NULL;
-int silc_hmac_alloc(SilcHash hash, SilcHmac *new_hmac)
+/* Registers a new HMAC into the SILC. This function is used at the
+ initialization of the SILC. */
+
+int silc_hmac_register(SilcHmacObject *hmac)
+{
+ SilcHmacObject *new;
+
+ SILC_LOG_DEBUG(("Registering new HMAC `%s'", hmac->name));
+
+ new = silc_calloc(1, sizeof(*new));
+ new->name = strdup(hmac->name);
+ new->len = hmac->len;
+
+ /* Add to list */
+ if (silc_hmac_list == NULL)
+ silc_hmac_list = silc_dlist_init();
+ silc_dlist_add(silc_hmac_list, new);
+
+ return TRUE;
+}
+
+/* Unregister a HMAC from the SILC. */
+
+int silc_hmac_unregister(SilcHmacObject *hmac)
{
- SILC_LOG_DEBUG(("Allocating new hmac object"));
+ SilcHmacObject *entry;
+
+ SILC_LOG_DEBUG(("Unregistering HMAC"));
+
+ if (!silc_hmac_list)
+ return FALSE;
+ silc_dlist_start(silc_hmac_list);
+ while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
+ if (entry == hmac) {
+ silc_dlist_del(silc_hmac_list, entry);
+
+ if (silc_dlist_count(silc_hmac_list) == 0) {
+ silc_dlist_uninit(silc_hmac_list);
+ silc_hmac_list = NULL;
+ }
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/* Allocates a new SilcHmac object of name of `name'. The `hash' may
+ be provided as argument. If provided it is used as the hash function
+ of the HMAC. If it is NULL then the hash function is allocated and
+ the name of the hash algorithm is derived from the `name'. */
+
+int silc_hmac_alloc(char *name, SilcHash hash, SilcHmac *new_hmac)
+{
+ SilcHmacObject *entry;
+
+ SILC_LOG_DEBUG(("Allocating new HMAC"));
+
+ /* Allocate the new object */
*new_hmac = silc_calloc(1, sizeof(**new_hmac));
+
+ if (!hash) {
+ char *tmp = strdup(name), *hname;
+
+ hname = tmp;
+ if (strchr(hname, '-'))
+ hname = strchr(hname, '-') + 1;
+ if (strchr(hname, '-'))
+ *strchr(hname, '-') = '\0';
+
+ if (!silc_hash_alloc(hname, &hash)) {
+ silc_free(tmp);
+ return FALSE;
+ }
+
+ (*new_hmac)->allocated_hash = TRUE;
+ silc_free(tmp);
+ }
+
(*new_hmac)->hash = hash;
- (*new_hmac)->set_key = silc_hmac_set_key;
- (*new_hmac)->make_hmac = silc_hmac_make;
- (*new_hmac)->make_hmac_with_key = silc_hmac_make_with_key;
- (*new_hmac)->make_hmac_truncated = silc_hmac_make_truncated;
- return TRUE;
+ if (silc_hmac_list) {
+ silc_dlist_start(silc_hmac_list);
+ while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
+ if (!strcmp(entry->name, name)) {
+ (*new_hmac)->hmac = entry;
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
}
/* Free's the SilcHmac object. */
void silc_hmac_free(SilcHmac hmac)
{
- if (hmac)
+ if (hmac) {
+ if (hmac->allocated_hash)
+ silc_hash_free(hmac->hash);
silc_free(hmac);
+ }
+}
+
+/* Returns the length of the MAC that the HMAC will produce. */
+
+unsigned int silc_hmac_len(SilcHmac hmac)
+{
+ return hmac->hmac->len;
+}
+
+/* Returns TRUE if HMAC `name' is supported. */
+
+int silc_hmac_is_supported(const char *name)
+{
+ SilcHmacObject *entry;
+
+ if (!name)
+ return FALSE;
+
+ if (silc_hmac_list) {
+ silc_dlist_start(silc_hmac_list);
+ while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
+ if (!strcmp(entry->name, name))
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/* Returns comma separated list of supported HMACs. */
+
+char *silc_hmac_get_supported()
+{
+ SilcHmacObject *entry;
+ char *list = NULL;
+ int len;
+
+ len = 0;
+ if (silc_hmac_list) {
+ silc_dlist_start(silc_hmac_list);
+ while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
+ len += strlen(entry->name);
+ list = silc_realloc(list, len + 1);
+
+ memcpy(list + (len - strlen(entry->name)),
+ entry->name, strlen(entry->name));
+ memcpy(list + len, ",", 1);
+ len++;
+ }
+ }
+
+ list[len - 1] = 0;
+
+ return list;
+}
+
+/* Sets the HMAC key used in the HMAC creation */
+
+void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
+ unsigned int key_len)
+{
+ hmac->key = silc_calloc(key_len, sizeof(unsigned char));
+ hmac->key_len = key_len;
+ memcpy(hmac->key, key, key_len);
}
/* Creates the HMAC. The created keyed hash value is returned to
unsigned char inner_pad[hash->hash->block_len + 1];
unsigned char outer_pad[hash->hash->block_len + 1];
unsigned char hvalue[hash->hash->hash_len];
+ unsigned char mac[128];
void *hash_context;
int i;
/* If the key length is more than block size of the hash function, the
key is hashed. */
if (key_len > hash->hash->block_len) {
- hash->make_hash(hash, key, key_len, hvalue);
+ silc_hash_make(hash, key, key_len, hvalue);
key = hvalue;
key_len = hash->hash->hash_len;
}
hash->hash->init(hash_context);
hash->hash->update(hash_context, inner_pad, hash->hash->block_len);
hash->hash->update(hash_context, data, data_len);
- hash->hash->final(hash_context, return_hash);
+ hash->hash->final(hash_context, mac);
hash->hash->init(hash_context);
hash->hash->update(hash_context, outer_pad, hash->hash->block_len);
- hash->hash->update(hash_context, return_hash, hash->hash->hash_len);
- hash->hash->final(hash_context, return_hash);
+ hash->hash->update(hash_context, mac, hash->hash->hash_len);
+ hash->hash->final(hash_context, mac);
+ memcpy(return_hash, mac, hmac->hmac->len);
+ memset(mac, 0, sizeof(mac));
}
/* Create the HMAC. This is thee make_hmac function pointer. This
uses the internal key set with silc_hmac_set_key. */
void silc_hmac_make(SilcHmac hmac, unsigned char *data,
- unsigned int data_len, unsigned char *return_hash)
+ unsigned int data_len, unsigned char *return_hash,
+ unsigned int *return_len)
{
silc_hmac_make_internal(hmac, data, data_len, hmac->key,
hmac->key_len, return_hash);
+ if (return_len)
+ *return_len = hmac->hmac->len;
+}
+
+/* Creates HMAC just as above except that this doesn't use the internal
+ key. The key is sent as argument to the function. */
+
+void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
+ unsigned int data_len,
+ unsigned char *key, unsigned int key_len,
+ unsigned char *return_hash,
+ unsigned int *return_len)
+{
+ silc_hmac_make_internal(hmac, data, data_len, key, key_len, return_hash);
+ if (return_len)
+ *return_len = hmac->hmac->len;
}
/* Creates the HMAC just as above except that the hash value is truncated
memcpy(return_hash, hvalue, truncated_len);
memset(hvalue, 0, sizeof(hvalue));
}
-
-/* Creates HMAC just as above except that this doesn't use the internal
- key. The key is sent as argument to the function. */
-
-void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
- unsigned int data_len,
- unsigned char *key, unsigned int key_len,
- unsigned char *return_hash)
-{
- silc_hmac_make_internal(hmac, data, data_len, key, key_len, return_hash);
-}
-
-/* Sets the HMAC key used in the HMAC creation */
-
-void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
- unsigned int key_len)
-{
- hmac->key = silc_calloc(key_len, sizeof(unsigned char));
- hmac->key_len = key_len;
- memcpy(hmac->key, key, key_len);
-}
routines were created according to RFC2104. Following short description
of the fields:
- SilcHash hash
+ SilcHmacObject:
- The hash object to tell what hash function to use with this HMAC.
+ char *name
+
+ Name of the HMAC.
- unsigned char *key
unsigned int len
- The key and its length used to make the HMAC. This is set
- with silc_hmac_set_key function.
+ Length of the MAC the HMAC is to produce (bytes).
- void (*set_key)(SilcHmac, const unsigned char *, unsigned int)
- Function used to set the key for the HMAC. Second argument is
- the key to be set and last argument is the length of the key.
+ SilcHmac:
- void (*make_hmac)(SilcHmac, unsigned char *, unsigned int,
- unsigned char *)
+ SilcHash hash
- Function what is used to create HMAC's. User can also use directly
- silc_hmac_make fuction. Although, one needs to allocate a SilcHmac
- object before doing it, naturally. This uses the key set with
- silc_hmac_set_key function.
+ The hash object to tell what hash function to use with this HMAC.
- void (*make_hmac_with_key)(SilcHmac, unsigned char *, unsigned int,
- unsigned char *, unsigned int, unsigned char *)
+ char allocated_hash
- Same function as above except that the key used in the HMAC
- creation is sent as argument. The key set with silc_hmac_set_key
- is ignored in this case.
+ TRUE if the `hash' was allocated and FALSE if it is static and
+ must not be freed.
- void (*make_hmac_truncated)(SilcHmac, unsigned char *, unsigned int,
- unsigned int, unsigned char *)
+ unsigned char *key
+ unsigned int len
- Same function as above except that the output hash value is truncated
- to the length sent as argument (second last argument). This makes
- variable truncations possible, however, one should not truncate
- hash values to less than half of the length of the hash value.
+ The key and its length used to make the HMAC. This is set
+ with silc_hmac_set_key function.
*/
typedef struct SilcHmacStruct *SilcHmac;
+typedef struct {
+ char *name;
+ unsigned int len;
+} SilcHmacObject;
+
struct SilcHmacStruct {
+ SilcHmacObject *hmac;
SilcHash hash;
+ char allocated_hash;
unsigned char *key;
unsigned int key_len;
- void (*set_key)(SilcHmac, const unsigned char *, unsigned int);
- void (*make_hmac)(SilcHmac, unsigned char *, unsigned int,
- unsigned char *);
- void (*make_hmac_with_key)(SilcHmac, unsigned char *, unsigned int,
- unsigned char *, unsigned int, unsigned char *);
- void (*make_hmac_truncated)(SilcHmac, unsigned char *,
- unsigned int, unsigned int, unsigned char *);
};
/* Prototypes */
-int silc_hmac_alloc(SilcHash hash, SilcHmac *new_hmac);
+int silc_hmac_register(SilcHmacObject *hmac);
+int silc_hmac_unregister(SilcHmacObject *hmac);
+int silc_hmac_alloc(char *name, SilcHash hash, SilcHmac *new_hmac);
void silc_hmac_free(SilcHmac hmac);
+int silc_hmac_is_supported(const char *name);
+char *silc_hmac_get_supported();
+unsigned int silc_hmac_len(SilcHmac hmac);
void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
unsigned int key_len);
-void silc_hmac_make(SilcHmac hmac,
- unsigned char *data,
- unsigned int data_len,
- unsigned char *return_hash);
-void silc_hmac_make_with_key(SilcHmac hmac,
- unsigned char *data,
- unsigned int data_len,
- unsigned char *key,
- unsigned int key_len,
- unsigned char *return_hash);
+void silc_hmac_make(SilcHmac hmac, unsigned char *data,
+ unsigned int data_len, unsigned char *return_hash,
+ unsigned int *return_len);
+void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
+ unsigned int data_len,
+ unsigned char *key, unsigned int key_len,
+ unsigned char *return_hash,
+ unsigned int *return_len);
void silc_hmac_make_truncated(SilcHmac hmac,
unsigned char *data,
unsigned int data_len,
{
int i;
+ if (!name)
+ return FALSE;
+
for (i = 0; silc_pkcs_list[i].name; i++) {
if (!strcmp(silc_pkcs_list[i].name, name))
return TRUE;
SILC_STR_UI_SHORT(payload->hash_alg_len),
SILC_STR_UI_XNSTRING(payload->hash_alg_list,
payload->hash_alg_len),
+ SILC_STR_UI_SHORT(payload->hmac_alg_len),
+ SILC_STR_UI_XNSTRING(payload->hmac_alg_list,
+ payload->hmac_alg_len),
SILC_STR_UI_SHORT(payload->comp_alg_len),
SILC_STR_UI_XNSTRING(payload->comp_alg_list,
payload->comp_alg_len),
silc_buffer_unformat(buffer,
SILC_STR_UI_XNSTRING_ALLOC(&payload->hash_alg_list,
payload->hash_alg_len),
- SILC_STR_UI_SHORT(&payload->comp_alg_len),
+ SILC_STR_UI_SHORT(&payload->hmac_alg_len),
SILC_STR_END);
if (ret == -1) {
status = SILC_SKE_STATUS_ERROR;
len2 += len = payload->hash_alg_len + 2;
silc_buffer_pull(buffer, len);
+ /* Parse HMAC list */
+ ret =
+ silc_buffer_unformat(buffer,
+ SILC_STR_UI_XNSTRING_ALLOC(&payload->hmac_alg_list,
+ payload->hmac_alg_len),
+ SILC_STR_UI_SHORT(&payload->comp_alg_len),
+ SILC_STR_END);
+ if (ret == -1) {
+ status = SILC_SKE_STATUS_ERROR;
+ goto err;
+ }
+
+ len2 += len = payload->hmac_alg_len + 2;
+ silc_buffer_pull(buffer, len);
+
/* Parse compression alg list */
if (payload->comp_alg_len) {
ret =
silc_free(payload->enc_alg_list);
if (payload->hash_alg_list)
silc_free(payload->hash_alg_list);
+ if (payload->hmac_alg_list)
+ silc_free(payload->hmac_alg_list);
if (payload->comp_alg_list)
silc_free(payload->comp_alg_list);
silc_free(payload);
unsigned short hash_alg_len;
unsigned char *hash_alg_list;
+ unsigned short hmac_alg_len;
+ unsigned char *hmac_alg_list;
+
unsigned short comp_alg_len;
unsigned char *comp_alg_list;
} SilcSKEStartPayload;
silc_cipher_free(ske->prop->cipher);
if (ske->prop->hash)
silc_hash_free(ske->prop->hash);
+ if (ske->prop->hmac)
+ silc_hmac_free(ske->prop->hmac);
silc_free(ske->prop);
}
if (ske->start_payload_copy)
goto err;
}
+ if (silc_hmac_alloc(payload->hmac_alg_list, NULL, &prop->hmac) == FALSE) {
+ status = SILC_SKE_STATUS_UNKNOWN_HMAC;
+ goto err;
+ }
+
ske->start_payload = payload;
/* Return the received payload by calling the callback function. */
silc_cipher_free(prop->cipher);
if (prop->hash)
silc_hash_free(prop->hash);
+ if (prop->hmac)
+ silc_hmac_free(prop->hmac);
silc_free(prop);
ske->prop = NULL;
goto err;
}
+ if (silc_hmac_alloc(start_payload->hmac_alg_list, NULL,
+ &prop->hmac) == FALSE) {
+ status = SILC_SKE_STATUS_UNKNOWN_HMAC;
+ goto err;
+ }
+
/* Encode the payload */
status = silc_ske_payload_start_encode(ske, start_payload, &payload_buf);
if (status != SILC_SKE_STATUS_OK)
silc_cipher_free(prop->cipher);
if (prop->hash)
silc_hash_free(prop->hash);
+ if (prop->hmac)
+ silc_hmac_free(prop->hmac);
silc_free(prop);
ske->prop = NULL;
rp->hash_alg_list = silc_hash_get_supported();
rp->hash_alg_len = strlen(rp->hash_alg_list);
+ /* Get supported HMACs */
+ rp->hmac_alg_list = silc_hmac_get_supported();
+ rp->hmac_alg_len = strlen(rp->hmac_alg_list);
+
/* XXX */
/* Get supported compression algorithms */
rp->comp_alg_list = "";
2 + rp->version_len +
2 + rp->ke_grp_len + 2 + rp->pkcs_alg_len +
2 + rp->enc_alg_len + 2 + rp->hash_alg_len +
- 2 + rp->comp_alg_len;
+ 2 + rp->hmac_alg_len + 2 + rp->comp_alg_len;
*return_payload = rp;
payload->hash_alg_list = strdup(rp->hash_alg_list);
}
+ /* Get supported HMACs */
+ cp = rp->hmac_alg_list;
+ if (cp && strchr(cp, ',')) {
+ while(cp) {
+ char *item;
+
+ len = strcspn(cp, ",");
+ item = silc_calloc(len + 1, sizeof(char));
+ memcpy(item, cp, len);
+
+ SILC_LOG_DEBUG(("Proposed HMAC `%s'", item));
+
+ if (silc_hmac_is_supported(item) == TRUE) {
+ SILC_LOG_DEBUG(("Found HMAC `%s'", item));
+
+ payload->hmac_alg_len = len;
+ payload->hmac_alg_list = item;
+ break;
+ }
+
+ cp += len;
+ if (strlen(cp) == 0)
+ cp = NULL;
+ else
+ cp++;
+
+ if (item)
+ silc_free(item);
+ }
+
+ if (!payload->hmac_alg_len && !payload->hmac_alg_list) {
+ SILC_LOG_DEBUG(("Could not find supported HMAC"));
+ silc_free(payload->ke_grp_list);
+ silc_free(payload->pkcs_alg_list);
+ silc_free(payload->enc_alg_list);
+ silc_free(payload->hash_alg_list);
+ silc_free(payload);
+ return SILC_SKE_STATUS_UNKNOWN_HMAC;
+ }
+ } else {
+
+ if (!rp->hmac_alg_len) {
+ SILC_LOG_DEBUG(("HMAC not defined in payload"));
+ silc_free(payload->ke_grp_list);
+ silc_free(payload->pkcs_alg_list);
+ silc_free(payload->enc_alg_list);
+ silc_free(payload->hash_alg_list);
+ silc_free(payload);
+ return SILC_SKE_STATUS_BAD_PAYLOAD;
+ }
+
+ SILC_LOG_DEBUG(("Proposed HMAC `%s' and selected it",
+ rp->hmac_alg_list));
+
+ payload->hmac_alg_len = rp->hmac_alg_len;
+ payload->hmac_alg_list = strdup(rp->hmac_alg_list);
+ }
+
#if 0
/* Get supported compression algorithms */
cp = rp->hash_alg_list;
2 + payload->version_len +
2 + payload->ke_grp_len + 2 + payload->pkcs_alg_len +
2 + payload->enc_alg_len + 2 + payload->hash_alg_len +
- 2 + payload->comp_alg_len;
+ 2 + payload->hmac_alg_len + 2 + payload->comp_alg_len;
return SILC_SKE_STATUS_OK;
}
/* Take third round */
dist = silc_buffer_realloc(dist, data_len + hash_len + hash_len);
+ silc_buffer_pull_tail(dist, hash_len);
silc_buffer_pull(dist, data_len + hash_len);
silc_buffer_format(dist,
SILC_STR_UI_XNSTRING(k2, hash_len),
/* Take third round */
dist = silc_buffer_realloc(dist, data_len + hash_len + hash_len);
+ silc_buffer_pull_tail(dist, hash_len);
silc_buffer_pull(dist, data_len + hash_len);
silc_buffer_format(dist,
SILC_STR_UI_XNSTRING(k2, hash_len),
void silc_ske_free_key_material(SilcSKEKeyMaterial *key)
{
+ if (!key)
+ return;
+
if (key->send_iv)
silc_free(key->send_iv);
if (key->receive_iv)
SilcPKCS pkcs;
SilcCipher cipher;
SilcHash hash;
- /* XXX SilcCompression comp; */
+ SilcHmac hmac;
+ /* XXX SilcZip comp; */
};
struct SilcSKEStruct {
SILC_SKE_STATUS_UNKNOWN_CIPHER = 4,
SILC_SKE_STATUS_UNKNOWN_PKCS = 5,
SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION = 6,
- SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY = 7,
- SILC_SKE_STATUS_INCORRECT_SIGNATURE = 8,
- SILC_SKE_STATUS_BAD_VERSION = 9,
+ SILC_SKE_STATUS_UNKNOWN_HMAC = 7,
+ SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY = 8,
+ SILC_SKE_STATUS_INCORRECT_SIGNATURE = 9,
+ SILC_SKE_STATUS_BAD_VERSION = 10,
SILC_SKE_STATUS_KEY_EXCHANGE_NOT_ACTIVE,
SILC_SKE_STATUS_BAD_RESERVED_FIELD,
fail:
SILC_LOG_DEBUG(("Error occured while formatting data"));
+#ifdef SILC_DEBUG
+ assert(FALSE);
+#endif
return -1;
ok:
fail:
SILC_LOG_DEBUG(("Error occured while unformatting buffer"));
+#ifdef SILC_DEBUG
+ assert(FALSE);
+#endif
return -1;
ok:
}
}
+/* Unregister a task by callback function. This invalidates the task. */
+
+void silc_task_unregister_by_callback(SilcTaskQueue queue,
+ SilcTaskCallback callback)
+{
+ SilcTask next;
+
+ SILC_LOG_DEBUG(("Unregister task by callback"));
+
+ if (queue->task == NULL)
+ return;
+
+ next = queue->task;
+
+ while(1) {
+ if (next->callback == callback)
+ next->valid = FALSE;
+ if (queue->task == next->next)
+ break;
+ next = next->next;
+ }
+}
+
/* Sets the I/O mask for the task. Only one I/O type can be set at a
time. */
with name 'func' as a task callback function. */
#define SILC_TASK_CALLBACK(func) \
static void func(void *qptr, int type, void *context, int fd)
+#define SILC_TASK_CALLBACK_GLOBAL(func) \
+void func(void *qptr, int type, void *context, int fd)
/* Prototypes */
void silc_task_queue_alloc(SilcTaskQueue *new, int valid);
int silc_task_remove(SilcTaskQueue queue, SilcTask task);
void silc_task_unregister(SilcTaskQueue queue, SilcTask task);
void silc_task_unregister_by_fd(SilcTaskQueue queue, int fd);
+void silc_task_unregister_by_callback(SilcTaskQueue queue,
+ SilcTaskCallback callback);
void silc_task_set_iotype(SilcTask task, int type);
void silc_task_reset_iotype(SilcTask task, int type);
int silc_task_timeout_compare(struct timeval *smaller,