Line data Source code
1 : /*
2 : * Copyright (C) 2016, 2017 Red Hat, Inc.
3 : *
4 : * Author: Nikos Mavrogiannopoulos
5 : *
6 : * This file is part of GnuTLS.
7 : *
8 : * The GnuTLS is free software; you can redistribute it and/or
9 : * modify it under the terms of the GNU Lesser General Public License
10 : * as published by the Free Software Foundation; either version 2.1 of
11 : * the License, or (at your option) any later version.
12 : *
13 : * This library is distributed in the hope that it will be useful, but
14 : * WITHOUT ANY WARRANTY; without even the implied warranty of
15 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 : * Lesser General Public License for more details.
17 : *
18 : * You should have received a copy of the GNU Lesser General Public License
19 : * along with this program. If not, see <https://www.gnu.org/licenses/>
20 : *
21 : */
22 :
23 : #include "gnutls_int.h"
24 : #include "errors.h"
25 : #include "str.h"
26 : #include <uninorm.h>
27 : #include <unistr.h>
28 : #include <unictype.h>
29 :
30 : /* rfc5892#section-2.6 exceptions
31 : */
32 4743 : inline static int is_allowed_exception(uint32_t ch)
33 : {
34 4743 : switch (ch) {
35 : case 0xB7:
36 : case 0x0375:
37 : case 0x05F3:
38 : case 0x05F4:
39 : case 0x30FB:
40 : case 0x0660:
41 : case 0x0661:
42 : case 0x0662:
43 : case 0x0663:
44 : case 0x0664:
45 : case 0x0665:
46 : case 0x0666:
47 : case 0x0667:
48 : case 0x0668:
49 : case 0x0669:
50 : case 0x06F0:
51 : case 0x06F1:
52 : case 0x06F2:
53 : case 0x06F3:
54 : case 0x06F4:
55 : case 0x06F5:
56 : case 0x06F6:
57 : case 0x06F7:
58 : case 0x06F8:
59 : case 0x06F9:
60 : case 0x0640:
61 : case 0x07FA:
62 : case 0x302E:
63 : case 0x302F:
64 : case 0x3031:
65 : case 0x3032:
66 : case 0x3033:
67 : case 0x3034:
68 : case 0x3035:
69 : case 0x303B:
70 : return 0; /* disallowed */
71 2 : case 0xDF:
72 : case 0x03C2:
73 : case 0x06FD:
74 : case 0x06FE:
75 : case 0x0F0B:
76 : case 0x3007:
77 2 : return 1; /* allowed */
78 4735 : default:
79 4735 : return -1; /* not exception */
80 : }
81 : }
82 :
83 : /* Checks whether the provided string is in the valid set of FreeFormClass (RFC7564
84 : * as an RFC7613 requirement), and converts all spaces to the ASCII-space. */
85 946 : static int check_for_valid_freeformclass(uint32_t *ucs4, unsigned ucs4_size)
86 : {
87 946 : unsigned i;
88 946 : int rc;
89 946 : uint32_t tmp[4];
90 946 : size_t tmp_size;
91 946 : uint32_t *nrm;
92 946 : uc_general_category_t cat;
93 946 : unsigned is_invalid;
94 :
95 : /* make the union of Valid categories, excluding any invalid (i.e., control) */
96 946 : cat = uc_general_category_or(UC_CATEGORY_Ll, UC_CATEGORY_Lu); /* LetterDigits */
97 946 : cat = uc_general_category_or(cat, UC_CATEGORY_Lo);
98 946 : cat = uc_general_category_or(cat, UC_CATEGORY_Nd);
99 946 : cat = uc_general_category_or(cat, UC_CATEGORY_Lm);
100 946 : cat = uc_general_category_or(cat, UC_CATEGORY_Mn);
101 946 : cat = uc_general_category_or(cat, UC_CATEGORY_Mc);
102 946 : cat = uc_general_category_or(cat, UC_CATEGORY_Lt); /* OtherLetterDigits */
103 946 : cat = uc_general_category_or(cat, UC_CATEGORY_Nl);
104 946 : cat = uc_general_category_or(cat, UC_CATEGORY_No);
105 946 : cat = uc_general_category_or(cat, UC_CATEGORY_Me);
106 946 : cat = uc_general_category_or(cat, UC_CATEGORY_Sm); /* Symbols */
107 946 : cat = uc_general_category_or(cat, UC_CATEGORY_Sc);
108 946 : cat = uc_general_category_or(cat, UC_CATEGORY_So);
109 946 : cat = uc_general_category_or(cat, UC_CATEGORY_Sk);
110 946 : cat = uc_general_category_or(cat, UC_CATEGORY_Pc); /* Punctuation */
111 946 : cat = uc_general_category_or(cat, UC_CATEGORY_Pd);
112 946 : cat = uc_general_category_or(cat, UC_CATEGORY_Ps);
113 946 : cat = uc_general_category_or(cat, UC_CATEGORY_Pe);
114 946 : cat = uc_general_category_or(cat, UC_CATEGORY_Pi);
115 946 : cat = uc_general_category_or(cat, UC_CATEGORY_Pf);
116 946 : cat = uc_general_category_or(cat, UC_CATEGORY_Po);
117 946 : cat = uc_general_category_or(cat, UC_CATEGORY_Zs); /* Spaces */
118 946 : cat = uc_general_category_and_not(cat, UC_CATEGORY_Cc); /* Not in Control */
119 :
120 : /* check for being in the allowed sets in rfc7564#section-4.3 */
121 6627 : for (i=0;i<ucs4_size;i++) {
122 4748 : is_invalid = 0;
123 :
124 : /* Disallowed
125 : o Old Hangul Jamo characters, i.e., the OldHangulJamo ("I") category
126 : (not handled in this code)
127 :
128 : o Control characters, i.e., the Controls ("L") category
129 :
130 : o Ignorable characters, i.e., the PrecisIgnorableProperties ("M")
131 : */
132 4748 : if (uc_is_property_default_ignorable_code_point(ucs4[i]) ||
133 4744 : uc_is_property_not_a_character(ucs4[i])) {
134 5 : return gnutls_assert_val(GNUTLS_E_INVALID_UTF8_STRING);
135 : }
136 :
137 :
138 : /* Contextual rules - we do not implement them / we reject chars from these sets
139 : o A number of characters from the Exceptions ("F") category defined
140 :
141 : o Joining characters, i.e., the JoinControl ("H") category defined
142 : */
143 4743 : rc = is_allowed_exception(ucs4[i]);
144 4743 : if (rc == 0 || uc_is_property_join_control(ucs4[i]))
145 6 : return gnutls_assert_val(GNUTLS_E_INVALID_UTF8_STRING);
146 :
147 4737 : if (rc == 1) /* exceptionally allowed, continue */
148 2 : continue;
149 :
150 :
151 : /* Replace all spaces; an RFC7613 requirement
152 : */
153 4735 : if (uc_is_general_category(ucs4[i], UC_CATEGORY_Zs)) /* replace */
154 122 : ucs4[i] = 0x20;
155 :
156 : /* Valid */
157 4735 : if ((ucs4[i] < 0x21 || ucs4[i] > 0x7E) && !uc_is_general_category(ucs4[i], cat))
158 2 : is_invalid = 1;
159 :
160 : /* HasCompat */
161 2 : if (is_invalid) {
162 2 : tmp_size = sizeof(tmp)/sizeof(tmp[0]);
163 2 : nrm = u32_normalize(UNINORM_NFKC, &ucs4[i], 1, tmp, &tmp_size);
164 2 : if (nrm == NULL || (tmp_size == 1 && nrm[0] == ucs4[i]))
165 2 : return gnutls_assert_val(GNUTLS_E_INVALID_UTF8_STRING);
166 : }
167 : }
168 :
169 : return 0;
170 : }
171 :
172 :
173 : /**
174 : * gnutls_utf8_password_normalize:
175 : * @password: contain the UTF-8 formatted password
176 : * @plen: the length of the provided password
177 : * @out: the result in an null-terminated allocated string
178 : * @flags: should be zero
179 : *
180 : * This function will convert the provided UTF-8 password according
181 : * to the normalization rules in RFC7613.
182 : *
183 : * If the flag %GNUTLS_UTF8_IGNORE_ERRS is specified, any UTF-8 encoding
184 : * errors will be ignored, and in that case the output will be a copy of the input.
185 : *
186 : * Returns: %GNUTLS_E_INVALID_UTF8_STRING on invalid UTF-8 data, or 0 on success.
187 : *
188 : * Since: 3.5.7
189 : **/
190 953 : int gnutls_utf8_password_normalize(const unsigned char *password, unsigned plen,
191 : gnutls_datum_t *out, unsigned flags)
192 : {
193 953 : size_t ucs4_size = 0, nrm_size = 0;
194 953 : size_t final_size = 0;
195 953 : uint8_t *final = NULL;
196 953 : uint32_t *ucs4 = NULL;
197 953 : uint32_t *nrm = NULL;
198 953 : uint8_t *nrmu8 = NULL;
199 953 : int ret;
200 :
201 953 : if (plen == 0) {
202 7 : out->data = (uint8_t*)gnutls_strdup("");
203 7 : out->size = 0;
204 7 : if (out->data == NULL)
205 0 : return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
206 : return 0;
207 : }
208 :
209 : /* check for invalid UTF-8 */
210 946 : if (u8_check((uint8_t*)password, plen) != NULL) {
211 0 : gnutls_assert();
212 0 : if (flags & GNUTLS_UTF8_IGNORE_ERRS) {
213 0 : raw_copy:
214 5 : out->data = gnutls_malloc(plen+1);
215 5 : if (out->data == NULL)
216 0 : return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
217 5 : out->size = plen;
218 5 : memcpy(out->data, password, plen);
219 5 : out->data[plen] = 0;
220 5 : return 0;
221 : } else {
222 : return GNUTLS_E_INVALID_UTF8_STRING;
223 : }
224 : }
225 :
226 : /* convert to UTF-32 */
227 946 : ucs4 = u8_to_u32((uint8_t*)password, plen, NULL, &ucs4_size);
228 946 : if (ucs4 == NULL) {
229 0 : gnutls_assert();
230 0 : ret = GNUTLS_E_PARSING_ERROR;
231 0 : goto fail;
232 : }
233 :
234 946 : ret = check_for_valid_freeformclass(ucs4, ucs4_size);
235 946 : if (ret < 0) {
236 13 : gnutls_assert();
237 13 : if (flags & GNUTLS_UTF8_IGNORE_ERRS) {
238 5 : free(ucs4);
239 5 : goto raw_copy;
240 : }
241 8 : if (ret == GNUTLS_E_INVALID_UTF8_STRING)
242 8 : ret = GNUTLS_E_INVALID_PASSWORD_STRING;
243 8 : goto fail;
244 : }
245 :
246 : /* normalize to NFC */
247 933 : nrm = u32_normalize(UNINORM_NFC, ucs4, ucs4_size, NULL, &nrm_size);
248 933 : if (nrm == NULL) {
249 0 : gnutls_assert();
250 0 : ret = GNUTLS_E_INVALID_PASSWORD_STRING;
251 0 : goto fail;
252 : }
253 :
254 : /* convert back to UTF-8 */
255 933 : final_size = 0;
256 933 : nrmu8 = u32_to_u8(nrm, nrm_size, NULL, &final_size);
257 933 : if (nrmu8 == NULL) {
258 0 : gnutls_assert();
259 0 : ret = GNUTLS_E_INVALID_PASSWORD_STRING;
260 0 : goto fail;
261 : }
262 :
263 : /* copy to output with null terminator */
264 933 : final = gnutls_malloc(final_size+1);
265 933 : if (final == NULL) {
266 0 : gnutls_assert();
267 0 : ret = GNUTLS_E_MEMORY_ERROR;
268 0 : goto fail;
269 : }
270 :
271 933 : memcpy(final, nrmu8, final_size);
272 933 : final[final_size] = 0;
273 :
274 933 : free(ucs4);
275 933 : free(nrm);
276 933 : free(nrmu8);
277 :
278 933 : out->data = final;
279 933 : out->size = final_size;
280 :
281 933 : return 0;
282 :
283 8 : fail:
284 8 : gnutls_free(final);
285 8 : free(ucs4);
286 8 : free(nrm);
287 8 : free(nrmu8);
288 8 : return ret;
289 : }
290 :
|