]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
gnutls_utf8_password_normalize: perform more strict check on input characters
authorNikos Mavrogiannopoulos <nmav@redhat.com>
Fri, 25 Nov 2016 13:20:11 +0000 (14:20 +0100)
committerNikos Mavrogiannopoulos <nmav@redhat.com>
Mon, 28 Nov 2016 11:47:53 +0000 (12:47 +0100)
That is, ensure that the input characters are in the valid class of characters
for the PRECIS FreeformClass.

lib/errors.c
lib/includes/gnutls/gnutls.h.in
lib/str-unicode.c

index 0f8c512ed1b40fd9f6f34cb4a685b3d79a1f6765..cb3c8893ed9038debf4093e02b0569d9e49d34b1 100644 (file)
@@ -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),
index ad18559515d7b6faeb09e7c61adeb68d6dfe8869..dbac6a30469a1ab79b9e2768d219ff0fcf437297 100644 (file)
@@ -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
 
index a3bdfd15939671774660712342dc7e521d57d9e1..35c87cf5ac8be7edcd76c67f814f3510fe6a8f92 100644 (file)
 # 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;
 }