From 789adec4bf1addc3ef7fb47d6370f5b3fe308376 Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Fri, 9 Mar 2001 19:21:04 +0000 Subject: [PATCH] updates. --- CHANGES | 41 ++++++-- apps/silcd/command.c | 59 +++++++---- apps/silcd/command_reply.c | 9 +- apps/silcd/idlist.c | 10 +- apps/silcd/idlist.h | 9 +- apps/silcd/packet_receive.c | 28 ++++++ apps/silcd/packet_send.c | 151 ++++++++++++++++++++++++++-- apps/silcd/packet_send.h | 11 ++ apps/silcd/server.c | 78 ++++++++++++-- doc/draft-riikonen-silc-pp-01.nroff | 6 ++ lib/silcclient/client.c | 28 +++++- lib/silccore/silcchannel.c | 76 +++++++++----- lib/silccore/silcchannel.h | 4 + lib/silccore/silcpacket.c | 27 +++-- lib/silccore/silcpacket.h | 9 +- lib/silccrypt/silchmac.c | 4 + 16 files changed, 461 insertions(+), 89 deletions(-) diff --git a/CHANGES b/CHANGES index 7d100fb5..b6fb793c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,12 +1,42 @@ Fri Mar 9 12:40:42 EET 2001 Pekka Riikonen - * Minor fix t the channel payload; allocate the data area, as it + * Minor fix to the channel payload; allocate the data area, as it needs to be of specific length. * If the key agreement port is zero then the operating system will define the bound port. Affected files are lib/silcclient/silcapi.h and lib/silcclient/client_keyagr.c. + * Added new function silc_channel_payload_decrypt into the file + lib/silccore/silcchannel.[ch]. + + * Moved the channel message etc, check from silc_packet_decrypt + to applications. The library calls now a generic + SilcPacketCheckDecrypt callback which is to return TRUE or FALSE + when the packet is either normal or special. This was done to + allow more wide range of checking that was not allowed when + the code was in library. Now applications can do virtually any + checks to the packet and return to the library the decision how + the packet should be processed. Affected files are + lib/silccore/silcpacket.[ch]. + + Added silc_server_packet_decrypt_check to the server and + silc_client_packet_decrypt_check to the client library. + + * Added silc_server_packet_send_srcdest into silcd/packet_send.[ch] + to send with specified source and destination information. + + * 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. + + * Fixed various commands in server to check also the global list + for the channel entry and not just the local list. The affected + file silcd/command.c. + Thu Mar 8 21:39:03 EET 2001 Pekka Riikonen * Added assert()s to buffer formatting and unformatting routines @@ -33,7 +63,7 @@ Thu Mar 8 21:39:03 EET 2001 Pekka Riikonen * 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 + MAC is 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 @@ -50,13 +80,6 @@ Thu Mar 8 21:39:03 EET 2001 Pekka Riikonen 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 * Fixed a minor formatting bug in the SKE's key material processing. diff --git a/apps/silcd/command.c b/apps/silcd/command.c index 779c0385..de42e834 100644 --- a/apps/silcd/command.c +++ b/apps/silcd/command.c @@ -1370,9 +1370,13 @@ SILC_SERVER_CMD_FUNC(topic) channel = silc_idlist_find_channel_by_id(server->local_list, channel_id, NULL); if (!channel) { - silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC, - SILC_STATUS_ERR_NO_SUCH_CHANNEL); - goto out; + channel = silc_idlist_find_channel_by_id(server->global_list, + channel_id, NULL); + if (!channel) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC, + SILC_STATUS_ERR_NO_SUCH_CHANNEL); + goto out; + } } if (argc > 1) { @@ -1498,9 +1502,13 @@ SILC_SERVER_CMD_FUNC(invite) channel = silc_idlist_find_channel_by_id(server->local_list, channel_id, NULL); if (!channel) { - silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE, - SILC_STATUS_ERR_NO_SUCH_CHANNEL); - goto out; + channel = silc_idlist_find_channel_by_id(server->global_list, + channel_id, NULL); + if (!channel) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE, + SILC_STATUS_ERR_NO_SUCH_CHANNEL); + goto out; + } } /* Check whether the sender of this command is on the channel. */ @@ -2288,9 +2296,13 @@ SILC_SERVER_CMD_FUNC(cmode) channel = silc_idlist_find_channel_by_id(server->local_list, channel_id, NULL); if (!channel) { - silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE, - SILC_STATUS_ERR_NO_SUCH_CHANNEL); - goto out; + channel = silc_idlist_find_channel_by_id(server->global_list, + channel_id, NULL); + if (!channel) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE, + SILC_STATUS_ERR_NO_SUCH_CHANNEL); + goto out; + } } /* Check whether this client is on the channel */ @@ -2644,9 +2656,13 @@ SILC_SERVER_CMD_FUNC(cumode) channel = silc_idlist_find_channel_by_id(server->local_list, channel_id, NULL); if (!channel) { - silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE, - SILC_STATUS_ERR_NO_SUCH_CHANNEL); - goto out; + channel = silc_idlist_find_channel_by_id(server->global_list, + channel_id, NULL); + if (!channel) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE, + SILC_STATUS_ERR_NO_SUCH_CHANNEL); + goto out; + } } /* Check whether sender is on the channel */ @@ -2835,9 +2851,13 @@ SILC_SERVER_CMD_FUNC(kick) channel = silc_idlist_find_channel_by_id(server->local_list, channel_id, NULL); if (!channel) { - silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK, - SILC_STATUS_ERR_NO_SUCH_CHANNEL); - goto out; + channel = silc_idlist_find_channel_by_id(server->local_list, + channel_id, NULL); + if (!channel) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK, + SILC_STATUS_ERR_NO_SUCH_CHANNEL); + goto out; + } } /* Check whether sender is on the channel */ @@ -3142,9 +3162,12 @@ SILC_SERVER_CMD_FUNC(leave) /* Get channel entry */ channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL); if (!channel) { - silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE, - SILC_STATUS_ERR_NO_SUCH_CHANNEL); - goto out; + channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL); + if (!channel) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE, + SILC_STATUS_ERR_NO_SUCH_CHANNEL); + goto out; + } } /* Check whether this client is on the channel */ diff --git a/apps/silcd/command_reply.c b/apps/silcd/command_reply.c index 0034de11..60e9f9f7 100644 --- a/apps/silcd/command_reply.c +++ b/apps/silcd/command_reply.c @@ -376,9 +376,10 @@ SILC_SERVER_CMD_REPLY_FUNC(join) SilcCommandStatus status; SilcChannelID *id; SilcChannelEntry entry; + SilcHmac hmac = NULL; unsigned int id_len, len; unsigned char *id_string; - char *channel_name, *tmp, *hmac; + char *channel_name, *tmp; unsigned int mode, created; SilcBuffer keyp; @@ -421,7 +422,11 @@ SILC_SERVER_CMD_REPLY_FUNC(join) goto out; /* Get hmac */ - hmac = silc_argument_get_arg_type(cmd->args, 10, NULL); + tmp = silc_argument_get_arg_type(cmd->args, 10, NULL); + if (tmp) { + if (!silc_hmac_alloc(tmp, NULL, &hmac)) + goto out; + } /* See whether we already have the channel. */ entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL); diff --git a/apps/silcd/idlist.c b/apps/silcd/idlist.c index b40ff6a6..5bb09466 100644 --- a/apps/silcd/idlist.c +++ b/apps/silcd/idlist.c @@ -561,7 +561,7 @@ silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id, SilcChannelEntry silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode, SilcChannelID *id, SilcServerEntry router, - SilcCipher channel_key, char *hmac) + SilcCipher channel_key, SilcHmac hmac) { SilcChannelEntry channel; @@ -571,7 +571,13 @@ silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode, channel->id = id; channel->router = router; channel->channel_key = channel_key; - channel->hmac = hmac ? strdup(hmac) : strdup("hmac-sha1-96"); + channel->hmac = hmac; + if (!channel->hmac) + if (!silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac)) { + silc_free(channel); + return NULL; + } + silc_list_init(channel->user_list, struct SilcChannelClientEntryStruct, channel_list); diff --git a/apps/silcd/idlist.h b/apps/silcd/idlist.h index bbce9ea5..d018bd10 100644 --- a/apps/silcd/idlist.h +++ b/apps/silcd/idlist.h @@ -361,10 +361,9 @@ struct SilcClientEntryStruct { Current initial vector. Initial vector is received always along with the channel packet. By default this is filled with NULL. - char *hmac; + SilcHmac hmac; - HMAC of the channel. Server only saves the name of the HMAC as - it never actually needs to compute the MAC. + HMAC of the channel. */ struct SilcChannelEntryStruct { @@ -396,7 +395,7 @@ struct SilcChannelEntryStruct { unsigned char *key; unsigned int key_len; unsigned char iv[SILC_CIPHER_MAX_IV_SIZE]; - char *hmac; + SilcHmac hmac; }; /* @@ -506,7 +505,7 @@ silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id, SilcChannelEntry silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode, SilcChannelID *id, SilcServerEntry router, - SilcCipher channel_key, char *hmac); + SilcCipher channel_key, SilcHmac hmac); int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry); SilcChannelEntry silc_idlist_find_channel_by_name(SilcIDList id_list, char *name, diff --git a/apps/silcd/packet_receive.c b/apps/silcd/packet_receive.c index 403c5928..eb3f9455 100644 --- a/apps/silcd/packet_receive.c +++ b/apps/silcd/packet_receive.c @@ -920,6 +920,34 @@ void silc_server_channel_message(SilcServer server, } } + /* If we are router and the packet came from router and private key + has not been set for the channel then we must encrypt the packet + as it was decrypted with the session key shared between us and the + router which sent it. This is so, because cells does not share the + same channel key */ + if (server->server_type == SILC_ROUTER && + sock->type == SILC_SOCKET_TYPE_ROUTER && + !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) { + SilcBuffer chp; + unsigned int iv_len, i, data_len; + + 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(server->rng); + else + silc_hash_make(server->md5hash, channel->iv, iv_len, channel->iv); + + /* Encode new payload. This encrypts it also. */ + SILC_GET16_MSB(data_len, packet->buffer->data); + chp = silc_channel_payload_encode(data_len, packet->buffer->data + 2, + iv_len, channel->iv, + channel->channel_key, + channel->hmac, server->rng); + silc_buffer_put(packet->buffer, chp->data, chp->len); + silc_buffer_free(chp); + } + /* Distribute the packet to our local clients. This will send the packet for further routing as well, if needed. */ silc_server_packet_relay_to_channel(server, sock, channel, sender, diff --git a/apps/silcd/packet_send.c b/apps/silcd/packet_send.c index 28228987..69bd04cd 100644 --- a/apps/silcd/packet_send.c +++ b/apps/silcd/packet_send.c @@ -179,6 +179,102 @@ void silc_server_packet_send_dest(SilcServer server, silc_free(packetdata.dst_id); } +/* Assembles a new packet to be sent out to network. This doesn't actually + send the packet but creates the packet and fills the outgoing data + buffer and marks the packet ready to be sent to network. However, If + argument force_send is TRUE the packet is sent immediately and not put + to queue. Normal case is that the packet is not sent immediately. + The source and destination information is sent as argument for this + function. */ + +void silc_server_packet_send_srcdest(SilcServer server, + SilcSocketConnection sock, + SilcPacketType type, + SilcPacketFlags flags, + void *src_id, + SilcIdType src_id_type, + void *dst_id, + SilcIdType dst_id_type, + unsigned char *data, + unsigned int data_len, + int force_send) +{ + SilcPacketContext packetdata; + SilcIDListData idata; + SilcCipher cipher = NULL; + SilcHmac hmac = NULL; + unsigned char *dst_id_data = NULL; + unsigned int dst_id_len = 0; + unsigned char *src_id_data = NULL; + unsigned int src_id_len = 0; + + SILC_LOG_DEBUG(("Sending packet, type %d", type)); + + /* Get data used in the packet sending, keys and stuff */ + idata = (SilcIDListData)sock->user_data; + + if (dst_id) { + dst_id_data = silc_id_id2str(dst_id, dst_id_type); + dst_id_len = silc_id_get_len(dst_id_type); + } + + if (src_id) { + src_id_data = silc_id_id2str(src_id, src_id_type); + src_id_len = silc_id_get_len(src_id_type); + } + + /* Set the packet context pointers */ + packetdata.type = type; + packetdata.flags = flags; + packetdata.src_id = src_id_data; + packetdata.src_id_len = src_id_len; + packetdata.src_id_type = src_id_type; + packetdata.dst_id = dst_id_data; + packetdata.dst_id_len = dst_id_len; + packetdata.dst_id_type = dst_id_type; + packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + dst_id_len; + packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen); + + /* 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, + data_len); + + SILC_LOG_DEBUG(("Putting data to outgoing buffer, len %d", data_len)); + + packetdata.buffer = sock->outbuf; + + /* Put the data to the buffer */ + if (data && data_len) + silc_buffer_put(sock->outbuf, data, data_len); + + /* Create the outgoing packet */ + silc_packet_assemble(&packetdata); + + if (idata) { + cipher = idata->send_key; + hmac = idata->hmac; + } + + /* Encrypt the packet */ + silc_packet_encrypt(cipher, hmac, sock->outbuf, sock->outbuf->len); + + SILC_LOG_HEXDUMP(("Outgoing packet, len %d", sock->outbuf->len), + sock->outbuf->data, sock->outbuf->len); + + /* Now actually send the packet */ + silc_server_packet_send_real(server, sock, force_send); + + if (packetdata.src_id) + silc_free(packetdata.src_id); + if (packetdata.dst_id) + silc_free(packetdata.dst_id); +} + /* Broadcast received packet to our primary route. This function is used by router to further route received broadcast packet. It is expected that the broadcast flag from the packet is checked before calling this @@ -538,12 +634,6 @@ void silc_server_packet_relay_to_channel(SilcServer server, silc_id_render(client->id, SILC_ID_CLIENT), sock->hostname, sock->ip)); - /* Send the packet */ - silc_server_packet_send_to_channel_real(server, sock, &packetdata, - idata->send_key, idata->hmac, - data, data_len, TRUE, - force_send); - /* We want to make sure that the packet is routed to same router only once. Mark this route as sent route. */ k = routed_count; @@ -551,6 +641,55 @@ void silc_server_packet_relay_to_channel(SilcServer server, routed[k] = client->router; routed_count++; + /* If the remote connection is router then we'll decrypt the + channel message and re-encrypt it with the session key shared + between us and the remote router. This is done because the + channel keys are cell specific and we have different channel + key than the remote router has. */ + if (sock->type == SILC_SOCKET_TYPE_ROUTER) { + + /* If private key mode is not set then decrypt the packet + and re-encrypt it */ + if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) { + unsigned char *tmp = silc_calloc(data_len, sizeof(*data)); + memcpy(tmp, data, data_len); + + /* Decrypt the channel message (we don't check the MAC) */ + if (!silc_channel_payload_decrypt(tmp, data_len, + channel->channel_key, + NULL)) { + memset(tmp, 0, data_len); + silc_free(tmp); + continue; + } + + /* Now re-encrypt and send it to the router */ + silc_server_packet_send_srcdest(server, sock, + SILC_PACKET_CHANNEL_MESSAGE, 0, + sender, sender_type, + channel->id, SILC_ID_CHANNEL, + tmp, data_len, force_send); + + /* Free the copy of the channel message */ + memset(tmp, 0, data_len); + silc_free(tmp); + } else { + /* Private key mode is set, we don't have the channel key, so + just re-encrypt the entire packet and send it to the router. */ + silc_server_packet_send_dest(server, sock, + SILC_PACKET_CHANNEL_MESSAGE, 0, + channel->id, SILC_ID_CHANNEL, + data, data_len, force_send); + } + continue; + } + + /* Send the packet (to normal server) */ + silc_server_packet_send_to_channel_real(server, sock, &packetdata, + idata->send_key, idata->hmac, + data, data_len, TRUE, + force_send); + continue; } diff --git a/apps/silcd/packet_send.h b/apps/silcd/packet_send.h index 89daefe2..b402c6aa 100644 --- a/apps/silcd/packet_send.h +++ b/apps/silcd/packet_send.h @@ -42,6 +42,17 @@ void silc_server_packet_send_dest(SilcServer server, unsigned char *data, unsigned int data_len, int force_send); +void silc_server_packet_send_srcdest(SilcServer server, + SilcSocketConnection sock, + SilcPacketType type, + SilcPacketFlags flags, + void *src_id, + SilcIdType src_id_type, + void *dst_id, + SilcIdType dst_id_type, + unsigned char *data, + unsigned int data_len, + int force_send); void silc_server_packet_broadcast(SilcServer server, SilcSocketConnection sock, SilcPacketContext *packet); diff --git a/apps/silcd/server.c b/apps/silcd/server.c index 765f6be5..a7385384 100644 --- a/apps/silcd/server.c +++ b/apps/silcd/server.c @@ -1374,6 +1374,37 @@ SILC_TASK_CALLBACK(silc_server_packet_process) server); } +/* Callback function that the silc_packet_decrypt will call to make the + decision whether the packet is normal or special packet. We will + return TRUE if it is normal and FALSE if it is special */ + +static int silc_server_packet_decrypt_check(SilcPacketType packet_type, + SilcBuffer buffer, + SilcPacketContext *packet, + void *context) +{ + SilcPacketParserContext *parse_ctx = (SilcPacketParserContext *)context; + SilcServer server = (SilcServer)parse_ctx->context; + + /* Packet is normal packet, if: + + 1) packet is private message packet and does not have private key set + 2) is other packet than channel message packet + 3) is channel message packet and remote is router and we are router + + all other packets are special packets + */ + if ((packet_type == SILC_PACKET_PRIVATE_MESSAGE && + !(buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY)) || + packet_type != SILC_PACKET_CHANNEL_MESSAGE || + (packet_type == SILC_PACKET_CHANNEL_MESSAGE && + parse_ctx->sock->type == SILC_SOCKET_TYPE_ROUTER && + server->server_type == SILC_ROUTER)) + return TRUE; + + return FALSE; +} + /* Parses whole packet, received earlier. */ SILC_TASK_CALLBACK(silc_server_packet_parse_real) @@ -1388,7 +1419,8 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real) /* Decrypt the received packet */ ret = silc_packet_decrypt(parse_ctx->cipher, parse_ctx->hmac, - packet->buffer, packet); + packet->buffer, packet, + silc_server_packet_decrypt_check, parse_ctx); if (ret < 0) goto out; @@ -2313,6 +2345,7 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server, SilcChannelID *channel_id; SilcChannelEntry entry; SilcCipher key; + SilcHmac newhmac; SILC_LOG_DEBUG(("Creating new channel")); @@ -2325,13 +2358,19 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server, if (!silc_cipher_alloc(cipher, &key)) return NULL; + /* Allocate hmac */ + if (!silc_hmac_alloc(hmac, NULL, &newhmac)) { + silc_cipher_free(key); + return NULL; + } + channel_name = strdup(channel_name); /* Create the channel */ 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, hmac); + NULL, key, newhmac); if (!entry) { silc_free(channel_name); return NULL; @@ -2365,6 +2404,7 @@ silc_server_create_new_channel_with_id(SilcServer server, { SilcChannelEntry entry; SilcCipher key; + SilcHmac newhmac; SILC_LOG_DEBUG(("Creating new channel")); @@ -2377,12 +2417,18 @@ silc_server_create_new_channel_with_id(SilcServer server, if (!silc_cipher_alloc(cipher, &key)) return NULL; + /* Allocate hmac */ + if (!silc_hmac_alloc(hmac, NULL, &newhmac)) { + silc_cipher_free(key); + return NULL; + } + channel_name = strdup(channel_name); /* Create the channel */ entry = silc_idlist_add_channel(server->local_list, channel_name, SILC_CHANNEL_MODE_NONE, channel_id, - NULL, key, hmac); + NULL, key, newhmac); if (!entry) { silc_free(channel_name); return NULL; @@ -2413,7 +2459,7 @@ void silc_server_create_channel_key(SilcServer server, unsigned int key_len) { int i; - unsigned char channel_key[32]; + unsigned char channel_key[32], hash[32]; unsigned int len; if (!channel->channel_key) @@ -2431,8 +2477,7 @@ void silc_server_create_channel_key(SilcServer server, for (i = 0; i < len; i++) channel_key[i] = silc_rng_get_byte(server->rng); /* Set the key */ - channel->channel_key->cipher->set_key(channel->channel_key->context, - channel_key, len); + silc_cipher_set_key(channel->channel_key, channel_key, len * 8); /* Remove old key if exists */ if (channel->key) { @@ -2445,6 +2490,13 @@ void silc_server_create_channel_key(SilcServer server, channel->key = silc_calloc(len, sizeof(*channel->key)); memcpy(channel->key, channel_key, len); memset(channel_key, 0, sizeof(channel_key)); + + /* 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, channel->key, len, hash); + silc_hmac_set_key(channel->hmac, hash, silc_hash_len(channel->hmac->hash)); + memset(hash, 0, sizeof(hash)); } /* Saves the channel key found in the encoded `key_payload' buffer. This @@ -2457,7 +2509,7 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server, { SilcChannelKeyPayload payload = NULL; SilcChannelID *id = NULL; - unsigned char *tmp; + unsigned char *tmp, hash[32]; unsigned int tmp_len; char *cipher; @@ -2519,8 +2571,16 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server, channel->key_len = tmp_len * 8; channel->key = silc_calloc(tmp_len, sizeof(unsigned char)); memcpy(channel->key, tmp, tmp_len); - channel->channel_key->cipher->set_key(channel->channel_key->context, - tmp, tmp_len); + silc_cipher_set_key(channel->channel_key, tmp, 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, tmp, tmp_len, hash); + silc_hmac_set_key(channel->hmac, hash, silc_hash_len(channel->hmac->hash)); + + memset(hash, 0, sizeof(hash)); + memset(tmp, 0, tmp_len); out: if (id) diff --git a/doc/draft-riikonen-silc-pp-01.nroff b/doc/draft-riikonen-silc-pp-01.nroff index a8d91947..0b1dcf77 100644 --- a/doc/draft-riikonen-silc-pp-01.nroff +++ b/doc/draft-riikonen-silc-pp-01.nroff @@ -2121,6 +2121,12 @@ their own channel keys thus the channel message traveling from one cell to another must be protected as it would be any normal SILC packet. +If the SILC_CMODE_PRIVKEY channel mode has been set for the channel +then the router cannot decrypt the packet as it does not know the +private key. In this case the entire packet is encrypted with the +session key and sent to the router. The router receiving the packet +must check the channel mode and decrypt the packet accordingly. + .ti 0 2.5.3 Private Message Encryption And Decryption diff --git a/lib/silcclient/client.c b/lib/silcclient/client.c index 5b68a771..33d51e10 100644 --- a/lib/silcclient/client.c +++ b/lib/silcclient/client.c @@ -625,6 +625,31 @@ SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process) } } +/* Callback function that the silc_packet_decrypt will call to make the + decision whether the packet is normal or special packet. We will + return TRUE if it is normal and FALSE if it is special */ + +static int silc_client_packet_decrypt_check(SilcPacketType packet_type, + SilcBuffer buffer, + SilcPacketContext *packet, + void *context) +{ + + /* Packet is normal packet, if: + + 1) packet is private message packet and does not have private key set + 2) is other packet than channel message packet + + all other packets are special packets + */ + if ((packet_type == SILC_PACKET_PRIVATE_MESSAGE && + !(buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY)) || + packet_type != SILC_PACKET_CHANNEL_MESSAGE) + return TRUE; + + return FALSE; +} + /* Parses whole packet, received earlier. */ SILC_TASK_CALLBACK(silc_client_packet_parse_real) @@ -640,7 +665,8 @@ SILC_TASK_CALLBACK(silc_client_packet_parse_real) SILC_LOG_DEBUG(("Start")); /* Decrypt the received packet */ - ret = silc_packet_decrypt(conn->receive_key, conn->hmac, buffer, packet); + ret = silc_packet_decrypt(conn->receive_key, conn->hmac, buffer, packet, + silc_client_packet_decrypt_check, parse_ctx); if (ret < 0) goto out; diff --git a/lib/silccore/silcchannel.c b/lib/silccore/silcchannel.c index b3956cd2..433236eb 100644 --- a/lib/silccore/silcchannel.c +++ b/lib/silccore/silcchannel.c @@ -38,48 +38,68 @@ struct SilcChannelPayloadStruct { unsigned char *iv; }; -/* Parses channel payload returning new channel payload structure. This - also decrypts it and checks the MAC. */ +/* Decrypts the channel message payload. */ -SilcChannelPayload silc_channel_payload_parse(SilcBuffer buffer, - SilcCipher cipher, - SilcHmac hmac) +int silc_channel_payload_decrypt(unsigned char *data, + size_t data_len, + 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")); + unsigned char *end, *mac, mac2[32]; /* 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. */ + end = data + data_len; - /* Push the IV out of the packet (it will be in buffer->tail) */ + /* Push the IV out of the packet */ 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); + silc_cipher_decrypt(cipher, data, data, data_len - iv_len, (end - iv_len)); /* 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; + if (hmac) { + mac_len = silc_hmac_len(hmac); + mac = (end - iv_len - mac_len); + + /* Check the MAC of the message */ + SILC_LOG_DEBUG(("Checking channel message MACs")); + silc_hmac_make(hmac, data, (data_len - iv_len - mac_len), mac2, &mac_len); + if (memcmp(mac, mac2, mac_len)) { + SILC_LOG_DEBUG(("Channel message MACs does not match")); + return FALSE; + } + SILC_LOG_DEBUG(("MAC is Ok")); } - SILC_LOG_DEBUG(("MAC is Ok")); - silc_buffer_pull_tail(buffer, iv_len + mac_len); + + return TRUE; +} + +/* Parses channel payload returning new channel payload structure. This + also decrypts it and checks the MAC. */ + +SilcChannelPayload silc_channel_payload_parse(SilcBuffer buffer, + SilcCipher cipher, + SilcHmac hmac) +{ + SilcChannelPayload new; + int ret; + unsigned int iv_len, mac_len; + + SILC_LOG_DEBUG(("Parsing channel payload")); + + /* Decrypt the payload */ + ret = silc_channel_payload_decrypt(buffer->data, buffer->len, + cipher, hmac); + if (ret == FALSE) + return NULL; + + iv_len = silc_cipher_get_block_len(cipher); + mac_len = silc_hmac_len(hmac); new = silc_calloc(1, sizeof(*new)); @@ -176,8 +196,10 @@ SilcBuffer silc_channel_payload_encode(unsigned short data_len, void silc_channel_payload_free(SilcChannelPayload payload) { if (payload) { - if (payload->data) + if (payload->data) { + memset(payload->data, 0, payload->data_len); silc_free(payload->data); + } silc_free(payload); } } diff --git a/lib/silccore/silcchannel.h b/lib/silccore/silcchannel.h index 2ac4d7f6..0c6a41e5 100644 --- a/lib/silccore/silcchannel.h +++ b/lib/silccore/silcchannel.h @@ -30,6 +30,10 @@ typedef struct SilcChannelPayloadStruct *SilcChannelPayload; typedef struct SilcChannelKeyPayloadStruct *SilcChannelKeyPayload; /* Prototypes */ +int silc_channel_payload_decrypt(unsigned char *data, + size_t data_len, + SilcCipher cipher, + SilcHmac hmac); SilcChannelPayload silc_channel_payload_parse(SilcBuffer buffer, SilcCipher cipher, SilcHmac hmac); diff --git a/lib/silccore/silcpacket.c b/lib/silccore/silcpacket.c index f331cb09..3112f158 100644 --- a/lib/silccore/silcpacket.c +++ b/lib/silccore/silcpacket.c @@ -572,24 +572,33 @@ static int silc_packet_decrypt_rest_special(SilcCipher cipher, the HMAC of the packet. If any other special or customized decryption processing is required this function cannot be used. This returns -1 on error, 0 when packet is normal packet and 1 when the packet - is special and requires special processing. */ + is special and requires special processing. + + The `check_packet' is a callback funtion that this function will + call. The callback relates to the checking whether the packet is + normal packet or special packet and how it should be processed. If + the callback return TRUE the packet is normal and FALSE if the packet + is special and requires special procesing. */ int silc_packet_decrypt(SilcCipher cipher, SilcHmac hmac, - SilcBuffer buffer, SilcPacketContext *packet) + SilcBuffer buffer, SilcPacketContext *packet, + SilcPacketCheckDecrypt check_packet, + void *context) { + int check; /* Decrypt start of the packet header */ if (cipher) - cipher->cipher->decrypt(cipher->context, buffer->data + 2, - buffer->data + 2, SILC_PACKET_MIN_HEADER_LEN - 2, - cipher->iv); + silc_cipher_decrypt(cipher, buffer->data + 2, buffer->data + 2, + SILC_PACKET_MIN_HEADER_LEN - 2, cipher->iv); + + /* Do packet checking, whether the packet is normal or special */ + check = check_packet((SilcPacketType)buffer->data[3], buffer, + packet, context); /* If the packet type is not any special type lets decrypt rest of the packet here. */ - if ((buffer->data[3] == SILC_PACKET_PRIVATE_MESSAGE && - !(buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY)) || - buffer->data[3] != SILC_PACKET_CHANNEL_MESSAGE) { - + if (check == TRUE) { /* Normal packet, decrypt rest of the packet */ if (!silc_packet_decrypt_rest(cipher, hmac, buffer)) return -1; diff --git a/lib/silccore/silcpacket.h b/lib/silccore/silcpacket.h index fd430230..e3f98a52 100644 --- a/lib/silccore/silcpacket.h +++ b/lib/silccore/silcpacket.h @@ -186,6 +186,11 @@ typedef struct { typedef void (*SilcPacketParserCallback)(SilcPacketParserContext *parse_context); +/* The packet check callback in decryption phase */ +typedef int (*SilcPacketCheckDecrypt)(SilcPacketType packet_type, + SilcBuffer buffer, + SilcPacketContext *packet, + void *context); /* SILC Packet types. */ #define SILC_PACKET_NONE 0 /* NULL, never sent */ @@ -245,7 +250,9 @@ void silc_packet_send_prepare(SilcSocketConnection sock, int silc_packet_read(int sock, SilcBuffer dest); int silc_packet_receive(SilcSocketConnection sock); int silc_packet_decrypt(SilcCipher cipher, SilcHmac hmac, - SilcBuffer buffer, SilcPacketContext *packet); + SilcBuffer buffer, SilcPacketContext *packet, + SilcPacketCheckDecrypt check_packet, + void *context); void silc_packet_receive_process(SilcSocketConnection sock, SilcCipher cipher, SilcHmac hmac, SilcPacketParserCallback parser, diff --git a/lib/silccrypt/silchmac.c b/lib/silccrypt/silchmac.c index 1edfb381..d3484983 100644 --- a/lib/silccrypt/silchmac.c +++ b/lib/silccrypt/silchmac.c @@ -190,6 +190,10 @@ char *silc_hmac_get_supported() void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key, unsigned int key_len) { + if (hmac->key) { + memset(hmac->key, 0, hmac->key_len); + silc_free(hmac->key); + } hmac->key = silc_calloc(key_len, sizeof(unsigned char)); hmac->key_len = key_len; memcpy(hmac->key, key, key_len); -- 2.24.0