diff options
author | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2016-11-25 14:20:11 +0100 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2016-11-28 12:47:53 +0100 |
commit | 24b46776314faeea82e9ec2904c8148b4b289e76 (patch) | |
tree | 9a4a2f0535e453b102f4c62fe9871c655e679015 | |
parent | 323ffbfb692369d549e7ca2ef9e1440418fa1d27 (diff) | |
download | gnutls-24b46776314faeea82e9ec2904c8148b4b289e76.tar.gz |
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.
-rw-r--r-- | lib/errors.c | 2 | ||||
-rw-r--r-- | lib/includes/gnutls/gnutls.h.in | 1 | ||||
-rw-r--r-- | 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 <idn-free.h> #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<ucs4_size;i++) { + is_invalid = 0; + + /* Disallowed + o Old Hangul Jamo characters, i.e., the OldHangulJamo ("I") category + [FIXME: not handled in this code] + + o Control characters, i.e., the Controls ("L") category + + o Ignorable characters, i.e., the PrecisIgnorableProperties ("M") + */ + if (uc_is_property_default_ignorable_code_point(ucs4[i]) || + uc_is_property_not_a_character(ucs4[i])) { + return gnutls_assert_val(GNUTLS_E_INVALID_UTF8_STRING); + } + + + /* Contextual rules - we do not implement them / we reject chars from these sets + o A number of characters from the Exceptions ("F") category defined + + o Joining characters, i.e., the JoinControl ("H") category defined + */ + rc = is_allowed_exception(ucs4[i]); + if (rc == 0 || uc_is_property_join_control(ucs4[i])) + return gnutls_assert_val(GNUTLS_E_INVALID_UTF8_STRING); + + if (rc == 1) /* exceptionally allowed, continue */ + continue; + + + /* Replace all spaces; an RFC7613 requirement + */ + if (uc_is_general_category(ucs4[i], UC_CATEGORY_Zs)) /* replace */ + ucs4[i] = 0x20; + + /* Valid */ + if ((ucs4[i] < 0x21 || ucs4[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;i<ucs4_size;i++) { - if (uc_is_general_category(ucs4[i], UC_CATEGORY_Zs)) { - ucs4[i] = 0x20; + ret = check_for_valid_freeformclass(ucs4, ucs4_size); + if (ret < 0) { + gnutls_assert(); + if (flags & GNUTLS_UTF8_IGNORE_ERRS) { + free(ucs4); + goto raw_copy; } + if (ret == GNUTLS_E_INVALID_UTF8_STRING) + ret = GNUTLS_E_INVALID_PASSWORD_STRING; + goto fail; } /* normalize to NFC */ nrm = u32_normalize(UNINORM_NFC, ucs4, ucs4_size, NULL, &nrm_size); if (nrm == NULL) { gnutls_assert(); - ret = GNUTLS_E_PARSING_ERROR; + ret = GNUTLS_E_INVALID_PASSWORD_STRING; goto fail; } @@ -112,7 +260,7 @@ int gnutls_utf8_password_normalize(const unsigned char *password, unsigned plen, nrmu8 = u32_to_u8(nrm, nrm_size, NULL, &final_size); if (nrmu8 == NULL) { gnutls_assert(); - ret = GNUTLS_E_PARSING_ERROR; + ret = GNUTLS_E_INVALID_PASSWORD_STRING; goto fail; } @@ -127,9 +275,9 @@ int gnutls_utf8_password_normalize(const unsigned char *password, unsigned plen, memcpy(final, nrmu8, final_size); final[final_size] = 0; - gnutls_free(ucs4); - gnutls_free(nrm); - gnutls_free(nrmu8); + free(ucs4); + free(nrm); + free(nrmu8); out->data = 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; } |