summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@redhat.com>2016-11-25 14:20:11 +0100
committerNikos Mavrogiannopoulos <nmav@redhat.com>2016-11-28 12:47:53 +0100
commit24b46776314faeea82e9ec2904c8148b4b289e76 (patch)
tree9a4a2f0535e453b102f4c62fe9871c655e679015
parent323ffbfb692369d549e7ca2ef9e1440418fa1d27 (diff)
downloadgnutls-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.c2
-rw-r--r--lib/includes/gnutls/gnutls.h.in1
-rw-r--r--lib/str-unicode.c174
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;
}