diff options
author | Valentin Bartenev <vbart@nginx.com> | 2021-10-26 15:43:44 +0300 |
---|---|---|
committer | Valentin Bartenev <vbart@nginx.com> | 2021-10-26 15:43:44 +0300 |
commit | 7bf6253941d3b61e5eb3339fb5f68c84e9e68795 (patch) | |
tree | ff59b7a2c74d939c7eb3e08ef65246238bcb90d1 | |
parent | 7503cc96df4f8c5637ac90dcf6095d93fd03296f (diff) | |
download | unit-7bf6253941d3b61e5eb3339fb5f68c84e9e68795.tar.gz unit-7bf6253941d3b61e5eb3339fb5f68c84e9e68795.tar.bz2 |
Custom implementation of Base64 decoding function.
Compared to the previous implementation based on OpenSSL, the new implementation
has these advantages:
1. Strict and reliable detection of invalid strings, including strings with
less than 4 bytes of garbage at the end;
2. Allows to use Base64 strings without '=' padding.
Diffstat (limited to '')
-rw-r--r-- | auto/sources | 1 | ||||
-rw-r--r-- | src/nxt_conf_validation.c | 10 | ||||
-rw-r--r-- | src/nxt_openssl.c | 76 | ||||
-rw-r--r-- | src/nxt_string.c | 97 | ||||
-rw-r--r-- | src/nxt_string.h | 2 | ||||
-rw-r--r-- | src/nxt_tls.h | 2 | ||||
-rw-r--r-- | src/test/nxt_base64_test.c | 98 | ||||
-rw-r--r-- | src/test/nxt_tests.c | 4 | ||||
-rw-r--r-- | src/test/nxt_tests.h | 1 |
9 files changed, 209 insertions, 82 deletions
diff --git a/auto/sources b/auto/sources index 01fec6c1..990f5d11 100644 --- a/auto/sources +++ b/auto/sources @@ -170,6 +170,7 @@ NXT_TEST_SRCS=" \ src/test/nxt_rbtree1_test.c \ src/test/nxt_http_parse_test.c \ src/test/nxt_strverscmp_test.c \ + src/test/nxt_base64_test.c \ " diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 5701ae17..a090cd5f 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -517,8 +517,8 @@ static nxt_int_t nxt_conf_vldt_ticket_key_element(nxt_conf_validation_t *vldt, nxt_conf_value_t *value) { + ssize_t ret; nxt_str_t key; - nxt_int_t ret; if (nxt_conf_type(value) != NXT_CONF_STRING) { return nxt_conf_vldt_error(vldt, "The \"key\" array must " @@ -527,12 +527,8 @@ nxt_conf_vldt_ticket_key_element(nxt_conf_validation_t *vldt, nxt_conf_get_string(value, &key); - ret = nxt_openssl_base64_decode(NULL, 0, key.start, key.length); - if (nxt_slow_path(ret == NXT_ERROR)) { - return NXT_ERROR; - } - - if (ret == NXT_DECLINED) { + ret = nxt_base64_decode(NULL, key.start, key.length); + if (ret == NXT_ERROR) { return nxt_conf_vldt_error(vldt, "Invalid Base64 format for the ticket " "key \"%V\".", &key); } diff --git a/src/nxt_openssl.c b/src/nxt_openssl.c index d57928a4..1e08015e 100644 --- a/src/nxt_openssl.c +++ b/src/nxt_openssl.c @@ -621,8 +621,8 @@ static nxt_int_t nxt_tls_ticket_keys(nxt_task_t *task, SSL_CTX *ctx, nxt_tls_init_t *tls_init, nxt_mp_t *mp) { + size_t len; uint32_t i; - nxt_int_t ret; nxt_str_t value; nxt_uint_t count; nxt_conf_value_t *member, *tickets_conf; @@ -686,14 +686,11 @@ nxt_tls_ticket_keys(nxt_task_t *task, SSL_CTX *ctx, nxt_tls_init_t *tls_init, nxt_conf_get_string(member, &value); - ret = nxt_openssl_base64_decode(buf, 80, value.start, value.length); - if (nxt_slow_path(ret == NXT_ERROR)) { - return NXT_ERROR; - } + len = nxt_base64_decode(buf, value.start, value.length); nxt_memcpy(ticket->name, buf, 16); - if (ret == 48) { + if (len == 48) { nxt_memcpy(ticket->aes_key, buf + 16, 16); nxt_memcpy(ticket->hmac_key, buf + 32, 16); ticket->size = 16; @@ -1818,70 +1815,3 @@ nxt_openssl_copy_error(u_char *p, u_char *end) return p; } - - -nxt_int_t -nxt_openssl_base64_decode(u_char *d, size_t dlen, const u_char *s, size_t slen) -{ - BIO *bio, *b64; - nxt_int_t count, ret; - u_char buf[128]; - - b64 = BIO_new(BIO_f_base64()); - if (nxt_slow_path(b64 == NULL)) { - goto error; - } - - bio = BIO_new_mem_buf(s, slen); - if (nxt_slow_path(bio == NULL)) { - goto error; - } - - bio = BIO_push(b64, bio); - - BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); - - count = 0; - - if (d == NULL) { - - for ( ;; ) { - ret = BIO_read(bio, buf, 128); - - if (ret < 0) { - goto invalid; - } - - count += ret; - - if (ret != 128) { - break; - } - } - - } else { - count = BIO_read(bio, d, dlen); - - if (count < 0) { - goto invalid; - } - } - - BIO_free_all(bio); - - return count; - -error: - - BIO_vfree(b64); - ERR_clear_error(); - - return NXT_ERROR; - -invalid: - - BIO_free_all(bio); - ERR_clear_error(); - - return NXT_DECLINED; -} diff --git a/src/nxt_string.c b/src/nxt_string.c index ab568990..b7aef79e 100644 --- a/src/nxt_string.c +++ b/src/nxt_string.c @@ -745,3 +745,100 @@ nxt_is_complex_uri_encoded(u_char *src, size_t length) return 1; } + + +ssize_t +nxt_base64_decode(u_char *dst, u_char *src, size_t length) +{ + u_char *end, *p; + size_t pad; + uint8_t v1, v2, v3, v4; + + static const uint8_t decode[] = { + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77, 77, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77, + 77, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 77, 77, 77, 77, 77, + 77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 77, 77, 77, 77, 77, + + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 + }; + + end = src + length; + pad = (4 - (length % 4)) % 4; + + if (dst == NULL) { + if (pad > 2) { + return NXT_ERROR; + } + + while (src < end) { + if (decode[*src] != 77) { + src++; + continue; + } + + if (pad == 0) { + pad = end - src; + + if ((pad == 1 || (pad == 2 && src[1] == '=')) && src[0] == '=') + { + break; + } + } + + return NXT_ERROR; + } + + return (length + 3) / 4 * 3 - pad; + } + + nxt_assert(length != 0); + + if (pad == 0) { + pad = (end[-1] == '=') + (end[-2] == '='); + end -= (pad + 3) & 4; + + } else { + end -= 4 - pad; + } + + p = dst; + + while (src < end) { + v1 = decode[src[0]]; + v2 = decode[src[1]]; + v3 = decode[src[2]]; + v4 = decode[src[3]]; + + *p++ = (v1 << 2 | v2 >> 4); + *p++ = (v2 << 4 | v3 >> 2); + *p++ = (v3 << 6 | v4); + + src += 4; + } + + if (pad > 0) { + v1 = decode[src[0]]; + v2 = decode[src[1]]; + + *p++ = (v1 << 2 | v2 >> 4); + + if (pad == 1) { + v3 = decode[src[2]]; + *p++ = (v2 << 4 | v3 >> 2); + } + } + + return (p - dst); +} diff --git a/src/nxt_string.h b/src/nxt_string.h index 7e02f59a..4d565e87 100644 --- a/src/nxt_string.h +++ b/src/nxt_string.h @@ -190,6 +190,8 @@ NXT_EXPORT uintptr_t nxt_encode_complex_uri(u_char *dst, u_char *src, size_t length); NXT_EXPORT nxt_bool_t nxt_is_complex_uri_encoded(u_char *s, size_t length); +NXT_EXPORT ssize_t nxt_base64_decode(u_char *dst, u_char *src, size_t length); + extern const uint8_t nxt_hex2int[256]; diff --git a/src/nxt_tls.h b/src/nxt_tls.h index e02a0aab..0667ade3 100644 --- a/src/nxt_tls.h +++ b/src/nxt_tls.h @@ -98,8 +98,6 @@ extern const nxt_tls_lib_t nxt_openssl_lib; void nxt_cdecl nxt_openssl_log_error(nxt_task_t *task, nxt_uint_t level, const char *fmt, ...); u_char *nxt_openssl_copy_error(u_char *p, u_char *end); -nxt_int_t nxt_openssl_base64_decode(u_char *d, size_t dlen, const u_char *s, - size_t slen); #endif #if (NXT_HAVE_GNUTLS) diff --git a/src/test/nxt_base64_test.c b/src/test/nxt_base64_test.c new file mode 100644 index 00000000..13a772b6 --- /dev/null +++ b/src/test/nxt_base64_test.c @@ -0,0 +1,98 @@ + +/* + * Copyright (C) NGINX, Inc. + */ + +#include <nxt_main.h> +#include "nxt_tests.h" + + +nxt_int_t +nxt_base64_test(nxt_thread_t *thr) +{ + ssize_t ret; + nxt_uint_t i; + + static struct { + nxt_str_t enc; + nxt_str_t dec; + + } tests[] = { + { nxt_string("ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+//+9876543210" + "zyxwvutsrqponmlkjihgfedcba" + "ZYXWVUTSRQPONMLKJIHGFEDCBA"), + nxt_string("\x00\x10\x83\x10\x51\x87\x20\x92\x8b\x30\xd3\x8f" + "\x41\x14\x93\x51\x55\x97\x61\x96\x9b\x71\xd7\x9f" + "\x82\x18\xa3\x92\x59\xa7\xa2\x9a\xab\xb2\xdb\xaf" + "\xc3\x1c\xb3\xd3\x5d\xb7\xe3\x9e\xbb\xf3\xdf\xbf" + "\xff\xef\x7c\xef\xae\x78\xdf\x6d\x74\xcf\x2c\x70" + "\xbe\xeb\x6c\xae\xaa\x68\x9e\x69\x64\x8e\x28\x60" + "\x7d\xe7\x5c\x6d\xa6\x58\x5d\x65\x54\x4d\x24\x50" + "\x3c\xe3\x4c\x2c\xa2\x48\x1c\x61\x44\x0c\x20\x40") }, + + { nxt_string("Aa=="), + nxt_string("\x01") }, + { nxt_string("0Z"), + nxt_string("\xd1") }, + { nxt_string("0aA="), + nxt_string("\xd1\xa0") }, + { nxt_string("z/+"), + nxt_string("\xcf\xff") }, + { nxt_string("z9+Npe=="), + nxt_string("\xcf\xdf\x8d\xa5") }, + { nxt_string("/+98765"), + nxt_string("\xff\xef\x7c\xef\xae") }, + + { nxt_string("aBc_"), + nxt_null_string }, + { nxt_string("5"), + nxt_null_string }, + { nxt_string("M==="), + nxt_null_string }, + { nxt_string("===="), + nxt_null_string }, + { nxt_string("Ab="), + nxt_null_string }, + { nxt_string("00=0"), + nxt_null_string }, + { nxt_string("\0"), + nxt_null_string }, + { nxt_string("\r\naaaa"), + nxt_null_string }, + { nxt_string("=0000"), + nxt_null_string }, + }; + + u_char buf[96]; + + nxt_thread_time_update(thr); + + for (i = 0; i < nxt_nitems(tests); i++) { + ret = nxt_base64_decode(NULL, tests[i].enc.start, tests[i].enc.length); + + if (ret == NXT_ERROR && tests[i].dec.start == NULL) { + continue; + } + + if ((size_t) ret != tests[i].dec.length) { + nxt_log_alert(thr->log, + "nxt_base64_decode() test \"%V\" failed: incorrect " + "length of decoded string %z, expected %uz", + &tests[i].enc, ret, tests[i].dec.length); + return NXT_ERROR; + } + + ret = nxt_base64_decode(buf, tests[i].enc.start, tests[i].enc.length); + + if (!nxt_str_eq(&tests[i].dec, buf, (size_t) ret)) { + nxt_log_alert(thr->log, "nxt_base64_decode() test \"%V\" failed"); + return NXT_ERROR; + } + } + + nxt_log_error(NXT_LOG_NOTICE, thr->log, "nxt_base64_decode() test passed"); + + return NXT_OK; +} diff --git a/src/test/nxt_tests.c b/src/test/nxt_tests.c index 901d76c3..f5a1cbd4 100644 --- a/src/test/nxt_tests.c +++ b/src/test/nxt_tests.c @@ -162,6 +162,10 @@ main(int argc, char **argv) return 1; } + if (nxt_base64_test(thr) != NXT_OK) { + return 1; + } + #if (NXT_HAVE_CLONE_NEWUSER) if (nxt_clone_creds_test(thr) != NXT_OK) { return 1; diff --git a/src/test/nxt_tests.h b/src/test/nxt_tests.h index d531cc7d..463dc851 100644 --- a/src/test/nxt_tests.h +++ b/src/test/nxt_tests.h @@ -64,6 +64,7 @@ nxt_int_t nxt_malloc_test(nxt_thread_t *thr); nxt_int_t nxt_utf8_test(nxt_thread_t *thr); nxt_int_t nxt_http_parse_test(nxt_thread_t *thr); nxt_int_t nxt_strverscmp_test(nxt_thread_t *thr); +nxt_int_t nxt_base64_test(nxt_thread_t *thr); nxt_int_t nxt_clone_creds_test(nxt_thread_t *thr); |