From 0085398d088f7c3651c09af12a0bbff997a16bcc Mon Sep 17 00:00:00 2001 From: Daniel-Constantin Mierla Date: Mon, 1 Feb 2016 18:50:01 +0100 Subject: [PATCH] crypto: option to register a callid generator callback - use libssl random and hashing for generating RFC 4122 version 4 UUID with high quality entropy that can be used as callid for requests sent by tm, through sip routing core api - new mod param: register_callid - set to 1 in order to register callid genrator callback to core (default is 0) --- modules/crypto/crypto_mod.c | 23 +++- modules/crypto/crypto_uuid.c | 197 +++++++++++++++++++++++++++++++++++ modules/crypto/crypto_uuid.h | 68 ++++++++++++ 3 files changed, 287 insertions(+), 1 deletion(-) create mode 100644 modules/crypto/crypto_uuid.c create mode 100644 modules/crypto/crypto_uuid.h diff --git a/modules/crypto/crypto_mod.c b/modules/crypto/crypto_mod.c index c32e0344404..2517b60a47e 100644 --- a/modules/crypto/crypto_mod.c +++ b/modules/crypto/crypto_mod.c @@ -32,6 +32,8 @@ #include "../../lvalue.h" #include "../../basex.h" +#include "crypto_uuid.h" + #include #define AES_BLOCK_SIZE 256 @@ -58,6 +60,8 @@ static int fixup_crypto_aes_decrypt(void** param, int param_no); static char _crypto_salt[CRYPTO_SALT_BSIZE]; static char *_crypto_salt_param = "k8hTm4aZ"; +static int _crypto_register_callid = 0; + static cmd_export_t cmds[]={ {"crypto_aes_encrypt", (cmd_function)w_crypto_aes_encrypt, 3, fixup_crypto_aes_encrypt, 0, ANY_ROUTE}, @@ -67,7 +71,8 @@ static cmd_export_t cmds[]={ }; static param_export_t params[]={ - { "salt", PARAM_STRING, &_crypto_salt_param }, + { "salt", PARAM_STRING, &_crypto_salt_param }, + { "register_callid", PARAM_INT, &_crypto_register_callid }, { 0, 0, 0 } }; @@ -109,6 +114,17 @@ static int mod_init(void) k = _crypto_salt[i]; } } + if(_crypto_register_callid!=0) { + if(crypto_init_callid()<0) { + LM_ERR("failed to init callid callback\n"); + return -1; + } + if(crypto_register_callid_func()<0) { + LM_ERR("unable to register callid callback\n"); + return -1; + } + LM_DBG("registered crypto callid callback\n"); + } return 0; } @@ -117,6 +133,11 @@ static int mod_init(void) */ static int child_init(int rank) { + if(_crypto_register_callid!=0 && crypto_child_init_callid(rank)<0) { + LM_ERR("failed to register callid callback\n"); + return -1; + } + return 0; } diff --git a/modules/crypto/crypto_uuid.c b/modules/crypto/crypto_uuid.c new file mode 100644 index 00000000000..26316e50fb3 --- /dev/null +++ b/modules/crypto/crypto_uuid.c @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2016 Daniel-Constantin Mierla (asipto.com) + * Copyright (C) 2016 Travis Cross + * + * This file is part of Kamailio, a free SIP server. + * + * Kamailio 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 + * + * Kamailio 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +/*! + * \file + * \brief Crypto :: Fast enough high entropy Call-ID generator + * + * Fast enough high entropy Call-ID generator. The Call-ID generated + * is an RFC 4122 version 4 UUID using high quality entropy from + * OpenSSL. This entropy is extracted only once at startup and is + * then combined in each child with the process ID and a counter. The + * result is whitened with SHA1 and formatted per RFC 4122. + */ + +#include +#include +#include +#include +#include "../../dprint.h" +#include "../../srapi.h" +#include "crypto_uuid.h" + +#define SEED_LEN 16 +#define CTR_LEN 16 +static unsigned char crypto_callid_seed[SEED_LEN] = {0}; +static unsigned char crypto_callid_counter[CTR_LEN] = {0}; + + +/** + * \brief Convert value to hex character + * \param x unsigned char byte + * \return lowercase hex charater + */ +static inline char crypto_byte2hex(unsigned char x) { + return x < 10 ? '0' + x : 'a' + (x-10); +} + +/** + * \brief Convert array of bytes to hexidecimal string + * \param sbuf output character array + * \param sbuf_len allocated size of sbuf, must be 2x buf_len + * \param buf input byte array + * \param buf_len number of bytes of buf + * \return 0 on success, -1 on error + */ +static inline int crypto_bytes2hex(char *sbuf, size_t sbuf_len, + unsigned char *buf, size_t buf_len) { + size_t i, j; + if (sbuf_len < 2*buf_len) return -1; + for (i=0, j=(2*buf_len)-1; i> (j%2 ? 0 : 4)) % 0x0f); + if (j == 0) break; + } + return 0; +} + + +/** + * \brief Initialize the Call-ID generator + * \return 0 on success, -1 on error + */ +int crypto_init_callid(void) +{ + static char crypto_callid_seed_str[2*SEED_LEN] = {0}; + if (!(RAND_bytes(crypto_callid_seed,sizeof(crypto_callid_seed)))) { + LOG(L_ERR, "ERROR: Unable to get random bytes for Call-ID seed\n"); + return -1; + } + crypto_bytes2hex(crypto_callid_seed_str, sizeof(crypto_callid_seed_str), + crypto_callid_seed, sizeof(crypto_callid_seed)); + DBG("Call-ID initialization: '0x%.*s'\n", 2*SEED_LEN, + crypto_callid_seed_str); + return 0; +} + + +/** + * \brief Child initialization, permute seed with pid + * \param rank not used + * \return 0 on success, -1 on error + */ +int crypto_child_init_callid(int rank) +{ + static char crypto_callid_seed_str[2*SEED_LEN] = {0}; + unsigned int pid = my_pid(); + if (SEED_LEN < 2) { + LOG(L_CRIT, "BUG: Call-ID seed is too short\n"); + return -1; + } + crypto_callid_seed[0] ^= (pid >> 0) % 0xff; + crypto_callid_seed[1] ^= (pid >> 8) % 0xff; + crypto_bytes2hex(crypto_callid_seed_str, sizeof(crypto_callid_seed_str), + crypto_callid_seed, sizeof(crypto_callid_seed)); + DBG("Call-ID initialization: '0x%.*s'\n", 2*SEED_LEN, + crypto_callid_seed_str); + return 0; +} + + +/** + * \brief Increment a counter + * \param ctr input array of bytes + * \param len length of byte array + * \return void + */ +static inline void crypto_inc_counter(unsigned char* ctr, size_t len) +{ + size_t i; + for (i=0; i < len; i++) { + ctr[i] += 1; + if (ctr[i]) break; + } +} + + +/** + * \brief Convert array of bytes to RFC 4122 UUID (version 4) + * \param sbuf output character array + * \param sbuf_len allocated size of sbuf, must be at least 36 + * \param buf input byte array + * \param buf_len number of bytes of buf, must be at least 16 + * \return 0 on success, -1 on error + */ +#define UUID_LEN 36 +static inline int crypto_format_rfc4122_uuid(char *sbuf, size_t sbuf_len, + unsigned char *buf, size_t buf_len) +{ + size_t i, j; + if (sbuf_len < UUID_LEN) return -1; + if (buf_len < UUID_LEN/2) return -1; + buf[6] &= 0x0f; + buf[6] |= 4 << 4; + buf[8] &= 0x3f; + buf[8] |= 2 << 6; + for (i=0, j=0; i> (j%2 ? 0 : 4)) % 0x0f); + if (!(++j/2 < buf_len)) break; + } + return 0; +} + + +/** + * \brief Get a unique Call-ID + * \param callid returned Call-ID + */ +void crypto_generate_callid(str* callid) +{ + static SHA_CTX crypto_ctx = {0}; + static unsigned char crypto_buf[SHA_DIGEST_LENGTH] = {0}; + static char crypto_sbuf[UUID_LEN] = {0}; + crypto_inc_counter(crypto_callid_counter, CTR_LEN); + SHA1_Init(&crypto_ctx); + SHA1_Update(&crypto_ctx, crypto_callid_seed, SEED_LEN); + SHA1_Update(&crypto_ctx, crypto_callid_counter, CTR_LEN); + SHA1_Final(crypto_buf, &crypto_ctx); + crypto_format_rfc4122_uuid(crypto_sbuf, sizeof(crypto_sbuf), + crypto_buf, sizeof(crypto_buf)); + callid->s = crypto_sbuf; + callid->len = sizeof(crypto_sbuf); +} + + +/** + * + */ +int crypto_register_callid_func(void) +{ + if(sr_register_callid_func(crypto_generate_callid)<0) { + LM_ERR("unable to register callid func\n"); + return -1; + } + return 0; +} diff --git a/modules/crypto/crypto_uuid.h b/modules/crypto/crypto_uuid.h new file mode 100644 index 00000000000..aaa4a6a1562 --- /dev/null +++ b/modules/crypto/crypto_uuid.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2016 Daniel-Constantin Mierla (asipto.com) + * Copyright (C) 2016 Travis Cross + * + * This file is part of Kamailio, a free SIP server. + * + * Kamailio 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 + * + * Kamailio 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +/*! + * \file + * \brief Crypto :: Fast enough high entropy Call-ID generator + * \ingroup tm + */ + + +#ifndef __CRYPTO_UUID_H__ +#define __CRYPTO_UUID_H__ + +#include "../../str.h" + + +/** + * \brief Initialize the Call-ID generator + * \return 0 on success, -1 on error + */ +int crypto_init_callid(void); + + +/** + * \brief Child initialization + * \param rank not used + * \return 0 on success, -1 on error + */ +int crypto_child_init_callid(int rank); + + +/** + * \brief TM API export + */ +typedef void (*generate_callid_f)(str*); + + +/** + * \brief Get a unique Call-ID + * \param callid returned Call-ID + */ +void crypto_generate_callid(str* callid); + +/** + * + */ +int crypto_register_callid_func(void); + +#endif /* CALLID_H */