|
Packit Service |
4684c1 |
/*
|
|
Packit Service |
4684c1 |
* Copyright (C) 2016, 2017 Red Hat, Inc.
|
|
Packit Service |
4684c1 |
*
|
|
Packit Service |
4684c1 |
* Author: Nikos Mavrogiannopoulos
|
|
Packit Service |
4684c1 |
*
|
|
Packit Service |
4684c1 |
* This file is part of GnuTLS.
|
|
Packit Service |
4684c1 |
*
|
|
Packit Service |
4684c1 |
* The GnuTLS is free software; you can redistribute it and/or
|
|
Packit Service |
4684c1 |
* modify it under the terms of the GNU Lesser General Public License
|
|
Packit Service |
4684c1 |
* as published by the Free Software Foundation; either version 2.1 of
|
|
Packit Service |
4684c1 |
* the License, or (at your option) any later version.
|
|
Packit Service |
4684c1 |
*
|
|
Packit Service |
4684c1 |
* This library is distributed in the hope that it will be useful, but
|
|
Packit Service |
4684c1 |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
4684c1 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit Service |
4684c1 |
* Lesser General Public License for more details.
|
|
Packit Service |
4684c1 |
*
|
|
Packit Service |
4684c1 |
* You should have received a copy of the GNU Lesser General Public License
|
|
Packit Service |
4684c1 |
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
|
Packit Service |
4684c1 |
*
|
|
Packit Service |
4684c1 |
*/
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
#include "gnutls_int.h"
|
|
Packit Service |
4684c1 |
#include "errors.h"
|
|
Packit Service |
4684c1 |
#include "str.h"
|
|
Packit Service |
4684c1 |
#include <uninorm.h>
|
|
Packit Service |
4684c1 |
#include <unistr.h>
|
|
Packit Service |
4684c1 |
#include <unictype.h>
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* rfc5892#section-2.6 exceptions
|
|
Packit Service |
4684c1 |
*/
|
|
Packit Service |
4684c1 |
inline static int is_allowed_exception(uint32_t ch)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
switch (ch) {
|
|
Packit Service |
4684c1 |
case 0xB7:
|
|
Packit Service |
4684c1 |
case 0x0375:
|
|
Packit Service |
4684c1 |
case 0x05F3:
|
|
Packit Service |
4684c1 |
case 0x05F4:
|
|
Packit Service |
4684c1 |
case 0x30FB:
|
|
Packit Service |
4684c1 |
case 0x0660:
|
|
Packit Service |
4684c1 |
case 0x0661:
|
|
Packit Service |
4684c1 |
case 0x0662:
|
|
Packit Service |
4684c1 |
case 0x0663:
|
|
Packit Service |
4684c1 |
case 0x0664:
|
|
Packit Service |
4684c1 |
case 0x0665:
|
|
Packit Service |
4684c1 |
case 0x0666:
|
|
Packit Service |
4684c1 |
case 0x0667:
|
|
Packit Service |
4684c1 |
case 0x0668:
|
|
Packit Service |
4684c1 |
case 0x0669:
|
|
Packit Service |
4684c1 |
case 0x06F0:
|
|
Packit Service |
4684c1 |
case 0x06F1:
|
|
Packit Service |
4684c1 |
case 0x06F2:
|
|
Packit Service |
4684c1 |
case 0x06F3:
|
|
Packit Service |
4684c1 |
case 0x06F4:
|
|
Packit Service |
4684c1 |
case 0x06F5:
|
|
Packit Service |
4684c1 |
case 0x06F6:
|
|
Packit Service |
4684c1 |
case 0x06F7:
|
|
Packit Service |
4684c1 |
case 0x06F8:
|
|
Packit Service |
4684c1 |
case 0x06F9:
|
|
Packit Service |
4684c1 |
case 0x0640:
|
|
Packit Service |
4684c1 |
case 0x07FA:
|
|
Packit Service |
4684c1 |
case 0x302E:
|
|
Packit Service |
4684c1 |
case 0x302F:
|
|
Packit Service |
4684c1 |
case 0x3031:
|
|
Packit Service |
4684c1 |
case 0x3032:
|
|
Packit Service |
4684c1 |
case 0x3033:
|
|
Packit Service |
4684c1 |
case 0x3034:
|
|
Packit Service |
4684c1 |
case 0x3035:
|
|
Packit Service |
4684c1 |
case 0x303B:
|
|
Packit Service |
4684c1 |
return 0; /* disallowed */
|
|
Packit Service |
4684c1 |
case 0xDF:
|
|
Packit Service |
4684c1 |
case 0x03C2:
|
|
Packit Service |
4684c1 |
case 0x06FD:
|
|
Packit Service |
4684c1 |
case 0x06FE:
|
|
Packit Service |
4684c1 |
case 0x0F0B:
|
|
Packit Service |
4684c1 |
case 0x3007:
|
|
Packit Service |
4684c1 |
return 1; /* allowed */
|
|
Packit Service |
4684c1 |
default:
|
|
Packit Service |
4684c1 |
return -1; /* not exception */
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* Checks whether the provided string is in the valid set of FreeFormClass (RFC7564
|
|
Packit Service |
4684c1 |
* as an RFC7613 requirement), and converts all spaces to the ASCII-space. */
|
|
Packit Service |
4684c1 |
static int check_for_valid_freeformclass(uint32_t *ucs4, unsigned ucs4_size)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
unsigned i;
|
|
Packit Service |
4684c1 |
int rc;
|
|
Packit Service |
4684c1 |
uint32_t tmp[4];
|
|
Packit Service |
4684c1 |
size_t tmp_size;
|
|
Packit Service |
4684c1 |
uint32_t *nrm;
|
|
Packit Service |
4684c1 |
uc_general_category_t cat;
|
|
Packit Service |
4684c1 |
unsigned is_invalid;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* make the union of Valid categories, excluding any invalid (i.e., control) */
|
|
Packit Service |
4684c1 |
cat = uc_general_category_or(UC_CATEGORY_Ll, UC_CATEGORY_Lu); /* LetterDigits */
|
|
Packit Service |
4684c1 |
cat = uc_general_category_or(cat, UC_CATEGORY_Lo);
|
|
Packit Service |
4684c1 |
cat = uc_general_category_or(cat, UC_CATEGORY_Nd);
|
|
Packit Service |
4684c1 |
cat = uc_general_category_or(cat, UC_CATEGORY_Lm);
|
|
Packit Service |
4684c1 |
cat = uc_general_category_or(cat, UC_CATEGORY_Mn);
|
|
Packit Service |
4684c1 |
cat = uc_general_category_or(cat, UC_CATEGORY_Mc);
|
|
Packit Service |
4684c1 |
cat = uc_general_category_or(cat, UC_CATEGORY_Lt); /* OtherLetterDigits */
|
|
Packit Service |
4684c1 |
cat = uc_general_category_or(cat, UC_CATEGORY_Nl);
|
|
Packit Service |
4684c1 |
cat = uc_general_category_or(cat, UC_CATEGORY_No);
|
|
Packit Service |
4684c1 |
cat = uc_general_category_or(cat, UC_CATEGORY_Me);
|
|
Packit Service |
4684c1 |
cat = uc_general_category_or(cat, UC_CATEGORY_Sm); /* Symbols */
|
|
Packit Service |
4684c1 |
cat = uc_general_category_or(cat, UC_CATEGORY_Sc);
|
|
Packit Service |
4684c1 |
cat = uc_general_category_or(cat, UC_CATEGORY_So);
|
|
Packit Service |
4684c1 |
cat = uc_general_category_or(cat, UC_CATEGORY_Sk);
|
|
Packit Service |
4684c1 |
cat = uc_general_category_or(cat, UC_CATEGORY_Pc); /* Punctuation */
|
|
Packit Service |
4684c1 |
cat = uc_general_category_or(cat, UC_CATEGORY_Pd);
|
|
Packit Service |
4684c1 |
cat = uc_general_category_or(cat, UC_CATEGORY_Ps);
|
|
Packit Service |
4684c1 |
cat = uc_general_category_or(cat, UC_CATEGORY_Pe);
|
|
Packit Service |
4684c1 |
cat = uc_general_category_or(cat, UC_CATEGORY_Pi);
|
|
Packit Service |
4684c1 |
cat = uc_general_category_or(cat, UC_CATEGORY_Pf);
|
|
Packit Service |
4684c1 |
cat = uc_general_category_or(cat, UC_CATEGORY_Po);
|
|
Packit Service |
4684c1 |
cat = uc_general_category_or(cat, UC_CATEGORY_Zs); /* Spaces */
|
|
Packit Service |
4684c1 |
cat = uc_general_category_and_not(cat, UC_CATEGORY_Cc); /* Not in Control */
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* check for being in the allowed sets in rfc7564#section-4.3 */
|
|
Packit Service |
4684c1 |
for (i=0;i
|
|
Packit Service |
4684c1 |
is_invalid = 0;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* Disallowed
|
|
Packit Service |
4684c1 |
o Old Hangul Jamo characters, i.e., the OldHangulJamo ("I") category
|
|
Packit Service |
4684c1 |
(not handled in this code)
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
o Control characters, i.e., the Controls ("L") category
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
o Ignorable characters, i.e., the PrecisIgnorableProperties ("M")
|
|
Packit Service |
4684c1 |
*/
|
|
Packit Service |
4684c1 |
if (uc_is_property_default_ignorable_code_point(ucs4[i]) ||
|
|
Packit Service |
4684c1 |
uc_is_property_not_a_character(ucs4[i])) {
|
|
Packit Service |
4684c1 |
return gnutls_assert_val(GNUTLS_E_INVALID_UTF8_STRING);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* Contextual rules - we do not implement them / we reject chars from these sets
|
|
Packit Service |
4684c1 |
o A number of characters from the Exceptions ("F") category defined
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
o Joining characters, i.e., the JoinControl ("H") category defined
|
|
Packit Service |
4684c1 |
*/
|
|
Packit Service |
4684c1 |
rc = is_allowed_exception(ucs4[i]);
|
|
Packit Service |
4684c1 |
if (rc == 0 || uc_is_property_join_control(ucs4[i]))
|
|
Packit Service |
4684c1 |
return gnutls_assert_val(GNUTLS_E_INVALID_UTF8_STRING);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (rc == 1) /* exceptionally allowed, continue */
|
|
Packit Service |
4684c1 |
continue;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* Replace all spaces; an RFC7613 requirement
|
|
Packit Service |
4684c1 |
*/
|
|
Packit Service |
4684c1 |
if (uc_is_general_category(ucs4[i], UC_CATEGORY_Zs)) /* replace */
|
|
Packit Service |
4684c1 |
ucs4[i] = 0x20;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* Valid */
|
|
Packit Service |
4684c1 |
if ((ucs4[i] < 0x21 || ucs4[i] > 0x7E) && !uc_is_general_category(ucs4[i], cat))
|
|
Packit Service |
4684c1 |
is_invalid = 1;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* HasCompat */
|
|
Packit Service |
4684c1 |
if (is_invalid) {
|
|
Packit Service |
4684c1 |
tmp_size = sizeof(tmp)/sizeof(tmp[0]);
|
|
Packit Service |
4684c1 |
nrm = u32_normalize(UNINORM_NFKC, &ucs4[i], 1, tmp, &tmp_size);
|
|
Packit Service |
4684c1 |
if (nrm == NULL || (tmp_size == 1 && nrm[0] == ucs4[i]))
|
|
Packit Service |
4684c1 |
return gnutls_assert_val(GNUTLS_E_INVALID_UTF8_STRING);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
return 0;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/**
|
|
Packit Service |
4684c1 |
* gnutls_utf8_password_normalize:
|
|
Packit Service |
4684c1 |
* @password: contain the UTF-8 formatted password
|
|
Packit Service |
4684c1 |
* @plen: the length of the provided password
|
|
Packit Service |
4684c1 |
* @out: the result in an null-terminated allocated string
|
|
Packit Service |
4684c1 |
* @flags: should be zero
|
|
Packit Service |
4684c1 |
*
|
|
Packit Service |
4684c1 |
* This function will convert the provided UTF-8 password according
|
|
Packit Service |
4684c1 |
* to the normalization rules in RFC7613.
|
|
Packit Service |
4684c1 |
*
|
|
Packit Service |
4684c1 |
* If the flag %GNUTLS_UTF8_IGNORE_ERRS is specified, any UTF-8 encoding
|
|
Packit Service |
4684c1 |
* errors will be ignored, and in that case the output will be a copy of the input.
|
|
Packit Service |
4684c1 |
*
|
|
Packit Service |
4684c1 |
* Returns: %GNUTLS_E_INVALID_UTF8_STRING on invalid UTF-8 data, or 0 on success.
|
|
Packit Service |
4684c1 |
*
|
|
Packit Service |
4684c1 |
* Since: 3.5.7
|
|
Packit Service |
4684c1 |
**/
|
|
Packit Service |
4684c1 |
int gnutls_utf8_password_normalize(const unsigned char *password, unsigned plen,
|
|
Packit Service |
4684c1 |
gnutls_datum_t *out, unsigned flags)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
size_t ucs4_size = 0, nrm_size = 0;
|
|
Packit Service |
4684c1 |
size_t final_size = 0;
|
|
Packit Service |
4684c1 |
uint8_t *final = NULL;
|
|
Packit Service |
4684c1 |
uint32_t *ucs4 = NULL;
|
|
Packit Service |
4684c1 |
uint32_t *nrm = NULL;
|
|
Packit Service |
4684c1 |
uint8_t *nrmu8 = NULL;
|
|
Packit Service |
4684c1 |
int ret;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (plen == 0) {
|
|
Packit Service |
4684c1 |
out->data = (uint8_t*)gnutls_strdup("");
|
|
Packit Service |
4684c1 |
out->size = 0;
|
|
Packit Service |
4684c1 |
if (out->data == NULL)
|
|
Packit Service |
4684c1 |
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
|
|
Packit Service |
4684c1 |
return 0;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* check for invalid UTF-8 */
|
|
Packit Service |
4684c1 |
if (u8_check((uint8_t*)password, plen) != NULL) {
|
|
Packit Service |
4684c1 |
gnutls_assert();
|
|
Packit Service |
4684c1 |
if (flags & GNUTLS_UTF8_IGNORE_ERRS) {
|
|
Packit Service |
4684c1 |
raw_copy:
|
|
Packit Service |
4684c1 |
out->data = gnutls_malloc(plen+1);
|
|
Packit Service |
4684c1 |
if (out->data == NULL)
|
|
Packit Service |
4684c1 |
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
|
|
Packit Service |
4684c1 |
out->size = plen;
|
|
Packit Service |
4684c1 |
memcpy(out->data, password, plen);
|
|
Packit Service |
4684c1 |
out->data[plen] = 0;
|
|
Packit Service |
4684c1 |
return 0;
|
|
Packit Service |
4684c1 |
} else {
|
|
Packit Service |
4684c1 |
return GNUTLS_E_INVALID_UTF8_STRING;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* convert to UTF-32 */
|
|
Packit Service |
4684c1 |
ucs4 = u8_to_u32((uint8_t*)password, plen, NULL, &ucs4_size);
|
|
Packit Service |
4684c1 |
if (ucs4 == NULL) {
|
|
Packit Service |
4684c1 |
gnutls_assert();
|
|
Packit Service |
4684c1 |
ret = GNUTLS_E_PARSING_ERROR;
|
|
Packit Service |
4684c1 |
goto fail;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
ret = check_for_valid_freeformclass(ucs4, ucs4_size);
|
|
Packit Service |
4684c1 |
if (ret < 0) {
|
|
Packit Service |
4684c1 |
gnutls_assert();
|
|
Packit Service |
4684c1 |
if (flags & GNUTLS_UTF8_IGNORE_ERRS) {
|
|
Packit Service |
4684c1 |
free(ucs4);
|
|
Packit Service |
4684c1 |
goto raw_copy;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
if (ret == GNUTLS_E_INVALID_UTF8_STRING)
|
|
Packit Service |
4684c1 |
ret = GNUTLS_E_INVALID_PASSWORD_STRING;
|
|
Packit Service |
4684c1 |
goto fail;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* normalize to NFC */
|
|
Packit Service |
4684c1 |
nrm = u32_normalize(UNINORM_NFC, ucs4, ucs4_size, NULL, &nrm_size);
|
|
Packit Service |
4684c1 |
if (nrm == NULL) {
|
|
Packit Service |
4684c1 |
gnutls_assert();
|
|
Packit Service |
4684c1 |
ret = GNUTLS_E_INVALID_PASSWORD_STRING;
|
|
Packit Service |
4684c1 |
goto fail;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* convert back to UTF-8 */
|
|
Packit Service |
4684c1 |
final_size = 0;
|
|
Packit Service |
4684c1 |
nrmu8 = u32_to_u8(nrm, nrm_size, NULL, &final_size);
|
|
Packit Service |
4684c1 |
if (nrmu8 == NULL) {
|
|
Packit Service |
4684c1 |
gnutls_assert();
|
|
Packit Service |
4684c1 |
ret = GNUTLS_E_INVALID_PASSWORD_STRING;
|
|
Packit Service |
4684c1 |
goto fail;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* copy to output with null terminator */
|
|
Packit Service |
4684c1 |
final = gnutls_malloc(final_size+1);
|
|
Packit Service |
4684c1 |
if (final == NULL) {
|
|
Packit Service |
4684c1 |
gnutls_assert();
|
|
Packit Service |
4684c1 |
ret = GNUTLS_E_MEMORY_ERROR;
|
|
Packit Service |
4684c1 |
goto fail;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
memcpy(final, nrmu8, final_size);
|
|
Packit Service |
4684c1 |
final[final_size] = 0;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
free(ucs4);
|
|
Packit Service |
4684c1 |
free(nrm);
|
|
Packit Service |
4684c1 |
free(nrmu8);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
out->data = final;
|
|
Packit Service |
4684c1 |
out->size = final_size;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
return 0;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
fail:
|
|
Packit Service |
4684c1 |
gnutls_free(final);
|
|
Packit Service |
4684c1 |
free(ucs4);
|
|
Packit Service |
4684c1 |
free(nrm);
|
|
Packit Service |
4684c1 |
free(nrmu8);
|
|
Packit Service |
4684c1 |
return ret;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|