From 24b46776314faeea82e9ec2904c8148b4b289e76 Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Fri, 25 Nov 2016 14:20:11 +0100 Subject: gnutls_utf8_password_normalize: perform more strict check on input characters That is, ensure that the input characters are in the valid class of characters for the PRECIS FreeformClass. --- lib/errors.c | 2 + lib/includes/gnutls/gnutls.h.in | 1 + lib/str-unicode.c | 174 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 164 insertions(+), 13 deletions(-) diff --git a/lib/errors.c b/lib/errors.c index 0f8c512ed1..cb3c8893ed 100644 --- a/lib/errors.c +++ b/lib/errors.c @@ -278,6 +278,8 @@ static const gnutls_error_entry error_entries[] = { GNUTLS_E_INVALID_UTF8_STRING), ERROR_ENTRY(N_("The given email string contains non-ASCII characters before '@'."), GNUTLS_E_INVALID_UTF8_EMAIL), + ERROR_ENTRY(N_("The given password contains invalid characters."), + GNUTLS_E_INVALID_PASSWORD_STRING), ERROR_ENTRY(N_ ("The Message Authentication Code verification failed."), GNUTLS_E_MAC_VERIFY_FAILED), diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index ad18559515..dbac6a3046 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -2828,6 +2828,7 @@ unsigned gnutls_fips140_mode_enabled(void); #define GNUTLS_E_INVALID_UTF8_STRING -412 #define GNUTLS_E_NO_EMBEDDED_DATA -413 #define GNUTLS_E_INVALID_UTF8_EMAIL -414 +#define GNUTLS_E_INVALID_PASSWORD_STRING -415 #define GNUTLS_E_UNIMPLEMENTED_FEATURE -1250 diff --git a/lib/str-unicode.c b/lib/str-unicode.c index a3bdfd1593..35c87cf5ac 100644 --- a/lib/str-unicode.c +++ b/lib/str-unicode.c @@ -31,6 +31,149 @@ # include #endif +/* rfc5892#section-2.6 exceptions + */ +inline static int is_allowed_exception(uint32_t ch) +{ + switch (ch) { + case 0xB7: + case 0x0375: + case 0x05F3: + case 0x05F4: + case 0x30FB: + case 0x0660: + case 0x0661: + case 0x0662: + case 0x0663: + case 0x0664: + case 0x0665: + case 0x0666: + case 0x0667: + case 0x0668: + case 0x0669: + case 0x06F0: + case 0x06F1: + case 0x06F2: + case 0x06F3: + case 0x06F4: + case 0x06F5: + case 0x06F6: + case 0x06F7: + case 0x06F8: + case 0x06F9: + case 0x0640: + case 0x07FA: + case 0x302E: + case 0x302F: + case 0x3031: + case 0x3032: + case 0x3033: + case 0x3034: + case 0x3035: + case 0x303B: + return 0; /* disallowed */ + case 0xDF: + case 0x03C2: + case 0x06FD: + case 0x06FE: + case 0x0F0B: + case 0x3007: + return 1; /* allowed */ + default: + return -1; /* not exception */ + } +} + +/* Checks whether the provided string is in the valid set of FreeFormClass (RFC7564 + * as an RFC7613 requirement), and converts all spaces to the ASCII-space. */ +static int check_for_valid_freeformclass(uint32_t *ucs4, unsigned ucs4_size) +{ + unsigned i; + int rc; + uint32_t tmp[4]; + size_t tmp_size; + uint32_t *nrm; + uc_general_category_t cat; + unsigned is_invalid; + + /* make the union of Valid categories, excluding any invalid (i.e., control) */ + cat = uc_general_category_or(UC_CATEGORY_Ll, UC_CATEGORY_Lu); /* LetterDigits */ + cat = uc_general_category_or(cat, UC_CATEGORY_Lo); + cat = uc_general_category_or(cat, UC_CATEGORY_Nd); + cat = uc_general_category_or(cat, UC_CATEGORY_Lm); + cat = uc_general_category_or(cat, UC_CATEGORY_Mn); + cat = uc_general_category_or(cat, UC_CATEGORY_Mc); + cat = uc_general_category_or(cat, UC_CATEGORY_Lt); /* OtherLetterDigits */ + cat = uc_general_category_or(cat, UC_CATEGORY_Nl); + cat = uc_general_category_or(cat, UC_CATEGORY_No); + cat = uc_general_category_or(cat, UC_CATEGORY_Me); + cat = uc_general_category_or(cat, UC_CATEGORY_Sm); /* Symbols */ + cat = uc_general_category_or(cat, UC_CATEGORY_Sc); + cat = uc_general_category_or(cat, UC_CATEGORY_So); + cat = uc_general_category_or(cat, UC_CATEGORY_Sk); + cat = uc_general_category_or(cat, UC_CATEGORY_Pc); /* Punctuation */ + cat = uc_general_category_or(cat, UC_CATEGORY_Pd); + cat = uc_general_category_or(cat, UC_CATEGORY_Ps); + cat = uc_general_category_or(cat, UC_CATEGORY_Pe); + cat = uc_general_category_or(cat, UC_CATEGORY_Pi); + cat = uc_general_category_or(cat, UC_CATEGORY_Pf); + cat = uc_general_category_or(cat, UC_CATEGORY_Po); + cat = uc_general_category_or(cat, UC_CATEGORY_Zs); /* Spaces */ + cat = uc_general_category_and_not(cat, UC_CATEGORY_Cc); /* Not in Control */ + + /* check for being in the allowed sets in rfc7564#section-4.3 */ + for (i=0;i 0x7E) && !uc_is_general_category(ucs4[i], cat)) + is_invalid = 1; + + /* HasCompat */ + if (is_invalid) { + tmp_size = sizeof(tmp)/sizeof(tmp[0]); + nrm = u32_normalize(UNINORM_NFKC, &ucs4[i], 1, tmp, &tmp_size); + if (nrm == NULL || (tmp_size == 1 && nrm[0] == ucs4[i])) + return gnutls_assert_val(GNUTLS_E_INVALID_UTF8_STRING); + } + } + + return 0; +} + + /** * gnutls_utf8_password_normalize: * @password: contain the UTF-8 formatted password @@ -57,7 +200,6 @@ int gnutls_utf8_password_normalize(const unsigned char *password, unsigned plen, uint32_t *ucs4 = NULL; uint32_t *nrm = NULL; uint8_t *nrmu8 = NULL; - unsigned i; int ret; if (plen == 0) { @@ -72,6 +214,7 @@ int gnutls_utf8_password_normalize(const unsigned char *password, unsigned plen, if (u8_check((uint8_t*)password, plen) != NULL) { gnutls_assert(); if (flags & GNUTLS_UTF8_IGNORE_ERRS) { + raw_copy: out->data = gnutls_malloc(plen+1); if (out->data == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); @@ -92,18 +235,23 @@ int gnutls_utf8_password_normalize(const unsigned char *password, unsigned plen, goto fail; } - /* convert all spaces to the ASCII-space */ - for (i=0;idata = final; out->size = final_size; @@ -138,9 +286,9 @@ int gnutls_utf8_password_normalize(const unsigned char *password, unsigned plen, fail: gnutls_free(final); - gnutls_free(ucs4); - gnutls_free(nrm); - gnutls_free(nrmu8); + free(ucs4); + free(nrm); + free(nrmu8); return ret; } -- cgit v1.2.1