diff --git a/modules/cookies/README.rst b/modules/cookies/README.rst index 13eed5286f4ff4f11ac22a2d52ba949b528eeb16..286629c2264c6fbcddcdbd01e04258b71755f543 100644 --- a/modules/cookies/README.rst +++ b/modules/cookies/README.rst @@ -17,12 +17,12 @@ Example Configuration -- Configure the client part of the resolver. Set 8 bytes of the client -- secret and choose the hashing algorithm to be used. - cookies.config( { ['client_secret'] = { 0, 1, 2, 3, 4, 5, 6, 7 }, + -- Use a string composed of hexadecimal digits to set the secret. + cookies.config( { ['client_secret'] = '0123456789ABCDEF', ['client_cookie_alg'] = 'FNV-64' } ) - -- Configure the server part of the resolver. Sets a string to be used - -- as server secret. Also chooses the hashing algorithm to be used. - cookies.config( { ['server_secret'] = 'secret key', + -- Configure the server part of the resolver. + cookies.config( { ['server_secret'] = 'FEDCBA9876543210', ['server_cookie_alg'] = 'FNV-64' } ) -- Enable client cookie functionality. (Add cookies into outbound @@ -43,7 +43,7 @@ Properties .. function:: cookies.config(configuration) :param table configuration: part of cookie configuration to be changed, may be called without parameter - :return: JSON dictionary containing corrent configuration + :return: JSON dictionary containing current configuration The function may be called without any parameter. In such case it only returns current configuration. The returned JSON alsao contains available algorithm choices. diff --git a/modules/cookies/cookiectl.c b/modules/cookies/cookiectl.c index fbb87e9bace989097d04f92b6dcfa39bcc049c85..8ffaf2d2eb3cdaae1d511933762d2bcacf06451f 100644 --- a/modules/cookies/cookiectl.c +++ b/modules/cookies/cookiectl.c @@ -16,6 +16,7 @@ #include <assert.h> #include <ccan/json/json.h> +#include <ctype.h> #include <libknot/rrtype/opt-cookie.h> #include <libknot/db/db_lmdb.h> #include <stdlib.h> @@ -86,48 +87,126 @@ static struct kr_cookie_secret *new_cookie_secret(size_t size, bool zero) return sq; } -static struct kr_cookie_secret *new_sq_str(const JsonNode *node) +static int hexchar2val(int d) { - assert(node && node->tag == JSON_STRING); + if (('0' <= d) && (d <= '9')) { + return d - '0'; + } else if (('a' <= d) && (d <= 'f')) { + return d - 'a' + 0x0a; + } else { + return -1; + } +} - size_t len = strlen(node->string_); +static int hexval2char(int i) +{ + if ((0 <= i) && (i <= 9)) { + return i + '0'; + } if ((0x0a <= i) && (i <= 0x0f)) { + return i - 0x0a + 'A'; + } else { + return -1; + } +} - struct kr_cookie_secret *sq = new_cookie_secret(len, false); - if (!sq) { - return NULL; +/** + * @brief Converts string containing two-digit hexadecimal number into int. + * @param hexstr hexadecimal string + * @return -1 on error, value from 0 to 255 else. + */ +static int hexbyte2int(const char *hexstr) +{ + if (!hexstr) { + return -1; } - memcpy(sq->data, node->string_, len); - return sq; + int dhi = tolower(hexstr[0]); + if (!isxdigit(dhi)) { + /* Exit also on empty string. */ + return -1; + } + int dlo = tolower(hexstr[1]); + if (!isxdigit(dlo)) { + return -1; + } + + dhi = hexchar2val(dhi); + assert(dhi != -1); + dlo = hexchar2val(dlo); + assert(dlo != -1); + + return (dhi << 4) | dlo; } -#define holds_char(x) ((x) >= 0 && (x) <= 255) +/** + * @brief Writes two hexadecimal digits (two byes) into given memory location. + * @param tgt target location + * @param i number from 0 to 255 + * @return 0 on success, -1 on failure + */ +static int int2hexbyte(char *tgt, int i) +{ + if (!tgt || i < 0x00 || i > 0xff) { + return -1; + } + + int ilo = hexval2char(i & 0x0f); + assert(ilo != -1); + int ihi = hexval2char((i >> 4) & 0x0f); + assert(ihi != -1); -static struct kr_cookie_secret *new_sq_array(const JsonNode *node) + tgt[0] = ihi; + tgt[1] = ilo; + + return 0; +} + +/** + * @brief Reads a string containing hexadecimal values. + * @note String must consist of hexadecimal digits only and must have even + * non-zero length. + */ +static struct kr_cookie_secret *new_sq_from_hexstr(const JsonNode *node) { - assert(node && node->tag == JSON_ARRAY); + assert(node && node->tag == JSON_STRING); - const JsonNode *element = NULL; - size_t cnt = 0; - json_foreach(element, node) { - if (element->tag != JSON_NUMBER || !holds_char(element->number_)) { - return NULL; - } - ++cnt; - } - if (cnt == 0) { + size_t len = strlen(node->string_); + if ((len % 2) != 0) { return NULL; } - struct kr_cookie_secret *sq = new_cookie_secret(cnt, false); + struct kr_cookie_secret *sq = new_cookie_secret(len / 2, false); if (!sq) { return NULL; } - cnt = 0; - json_foreach(element, node) { - sq->data[cnt++] = (uint8_t) element->number_; + const char *hexstr = node->string_; + uint8_t *data = sq->data; + for (int i = 0; i < len; i += 2) { + int num = hexbyte2int(hexstr + i); + if (num == -1) { + free(sq); + return NULL; + } + assert(0x00 <= num && num <= 0xff); + *data = num; + ++data; + } + + return sq; +} + +static struct kr_cookie_secret *new_sq_str(const JsonNode *node) +{ + assert(node && node->tag == JSON_STRING); + + size_t len = strlen(node->string_); + + struct kr_cookie_secret *sq = new_cookie_secret(len, false); + if (!sq) { + return NULL; } + memcpy(sq->data, node->string_, len); return sq; } @@ -147,10 +226,7 @@ static bool apply_secret_shallow(struct kr_cookie_secret **sec, switch (node->tag) { case JSON_STRING: - sq = new_sq_str(node); - break; - case JSON_ARRAY: - sq = new_sq_array(node); + sq = new_sq_from_hexstr(node); break; default: break; @@ -224,33 +300,55 @@ static bool apply_configuration_shallow(struct kr_cookie_ctx *cntrl, return false; } +/** + * @brief Creates a new string from secret quantity. + * @param sq secret quantity + * @return newly allocated string or NULL on error + */ +static char *new_hexstr_from_sq(const struct kr_cookie_secret *sq) +{ + if (!sq) { + return NULL; + } + + char *new_str = malloc((sq->size * 2) + 1); + if (!new_str) { + return NULL; + } + + char *tgt = new_str; + for (size_t i = 0; i < sq->size; ++i) { + if (0 != int2hexbyte(tgt, sq->data[i])) { + free(new_str); + return NULL; + } + tgt += 2; + } + + *tgt = '\0'; + return new_str; +} + static bool read_secret(JsonNode *root, const char *node_name, const struct kr_cookie_secret *secret) { assert(root && node_name && secret); - JsonNode *array = json_mkarray(); - if (!array) { + char *secret_str = new_hexstr_from_sq(secret); + if (!secret_str) { return false; } - for (size_t i = 0; i < secret->size; ++i) { - JsonNode *element = json_mknumber(secret->data[i]); - if (!element) { - goto fail; - } - json_append_element(array, element); + JsonNode *str_node = json_mkstring(secret_str); + if (!str_node) { + free(secret_str); + return false; } - json_append_member(root, node_name, array); + json_append_member(root, node_name, str_node); + free(secret_str); return true; - -fail: - if (array) { - json_delete(array); - } - return false; } static bool read_available_hashes(JsonNode *root, const char *root_name,