xref: /src/crypto/openssl/crypto/asn1/a_mbstr.c (revision f25b8c9fb4f58cf61adb47d7570abe7caa6d385d)
1 /*
2  * Copyright 1999-2021 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the Apache License 2.0 (the "License").  You may not use
5  * this file except in compliance with the License.  You can obtain a copy
6  * in the file LICENSE in the source distribution or at
7  * https://www.openssl.org/source/license.html
8  */
9 
10 #include <stdio.h>
11 #include "crypto/ctype.h"
12 #include "internal/cryptlib.h"
13 #include "internal/unicode.h"
14 #include <openssl/asn1.h>
15 
16 static int traverse_string(const unsigned char *p, int len, int inform,
17     int (*rfunc)(unsigned long value, void *in),
18     void *arg);
19 static int in_utf8(unsigned long value, void *arg);
20 static int out_utf8(unsigned long value, void *arg);
21 static int type_str(unsigned long value, void *arg);
22 static int cpy_asc(unsigned long value, void *arg);
23 static int cpy_bmp(unsigned long value, void *arg);
24 static int cpy_univ(unsigned long value, void *arg);
25 static int cpy_utf8(unsigned long value, void *arg);
26 
27 /*
28  * These functions take a string in UTF8, ASCII or multibyte form and a mask
29  * of permissible ASN1 string types. It then works out the minimal type
30  * (using the order Numeric < Printable < IA5 < T61 < BMP < Universal < UTF8)
31  * and creates a string of the correct type with the supplied data. Yes this is
32  * horrible: it has to be :-( The 'ncopy' form checks minimum and maximum
33  * size limits too.
34  */
35 
ASN1_mbstring_copy(ASN1_STRING ** out,const unsigned char * in,int len,int inform,unsigned long mask)36 int ASN1_mbstring_copy(ASN1_STRING **out, const unsigned char *in, int len,
37     int inform, unsigned long mask)
38 {
39     return ASN1_mbstring_ncopy(out, in, len, inform, mask, 0, 0);
40 }
41 
ASN1_mbstring_ncopy(ASN1_STRING ** out,const unsigned char * in,int len,int inform,unsigned long mask,long minsize,long maxsize)42 int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len,
43     int inform, unsigned long mask,
44     long minsize, long maxsize)
45 {
46     int str_type;
47     int ret;
48     char free_out;
49     int outform, outlen = 0;
50     ASN1_STRING *dest;
51     unsigned char *p;
52     int nchar;
53     int (*cpyfunc)(unsigned long, void *) = NULL;
54     if (len == -1)
55         len = strlen((const char *)in);
56     if (!mask)
57         mask = DIRSTRING_TYPE;
58     if (len < 0)
59         return -1;
60 
61     /* First do a string check and work out the number of characters */
62     switch (inform) {
63 
64     case MBSTRING_BMP:
65         if (len & 1) {
66             ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_BMPSTRING_LENGTH);
67             return -1;
68         }
69         nchar = len >> 1;
70         break;
71 
72     case MBSTRING_UNIV:
73         if (len & 3) {
74             ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_UNIVERSALSTRING_LENGTH);
75             return -1;
76         }
77         nchar = len >> 2;
78         break;
79 
80     case MBSTRING_UTF8:
81         nchar = 0;
82         /* This counts the characters and does utf8 syntax checking */
83         ret = traverse_string(in, len, MBSTRING_UTF8, in_utf8, &nchar);
84         if (ret < 0) {
85             ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_UTF8STRING);
86             return -1;
87         }
88         break;
89 
90     case MBSTRING_ASC:
91         nchar = len;
92         break;
93 
94     default:
95         ERR_raise(ERR_LIB_ASN1, ASN1_R_UNKNOWN_FORMAT);
96         return -1;
97     }
98 
99     if ((minsize > 0) && (nchar < minsize)) {
100         ERR_raise_data(ERR_LIB_ASN1, ASN1_R_STRING_TOO_SHORT,
101             "minsize=%ld", minsize);
102         return -1;
103     }
104 
105     if ((maxsize > 0) && (nchar > maxsize)) {
106         ERR_raise_data(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG,
107             "maxsize=%ld", maxsize);
108         return -1;
109     }
110 
111     /* Now work out minimal type (if any) */
112     if (traverse_string(in, len, inform, type_str, &mask) < 0) {
113         ERR_raise(ERR_LIB_ASN1, ASN1_R_ILLEGAL_CHARACTERS);
114         return -1;
115     }
116 
117     /*
118      * Now work out output format and string type.
119      * These checks should be in sync with the checks in type_str.
120      */
121     outform = MBSTRING_ASC;
122     if (mask & B_ASN1_NUMERICSTRING)
123         str_type = V_ASN1_NUMERICSTRING;
124     else if (mask & B_ASN1_PRINTABLESTRING)
125         str_type = V_ASN1_PRINTABLESTRING;
126     else if (mask & B_ASN1_IA5STRING)
127         str_type = V_ASN1_IA5STRING;
128     else if (mask & B_ASN1_T61STRING)
129         str_type = V_ASN1_T61STRING;
130     else if (mask & B_ASN1_BMPSTRING) {
131         str_type = V_ASN1_BMPSTRING;
132         outform = MBSTRING_BMP;
133     } else if (mask & B_ASN1_UNIVERSALSTRING) {
134         str_type = V_ASN1_UNIVERSALSTRING;
135         outform = MBSTRING_UNIV;
136     } else {
137         str_type = V_ASN1_UTF8STRING;
138         outform = MBSTRING_UTF8;
139     }
140     if (!out)
141         return str_type;
142     if (*out) {
143         free_out = 0;
144         dest = *out;
145         ASN1_STRING_set0(dest, NULL, 0);
146         dest->type = str_type;
147     } else {
148         free_out = 1;
149         dest = ASN1_STRING_type_new(str_type);
150         if (dest == NULL) {
151             ERR_raise(ERR_LIB_ASN1, ERR_R_ASN1_LIB);
152             return -1;
153         }
154         *out = dest;
155     }
156     /* If both the same type just copy across */
157     if (inform == outform) {
158         if (!ASN1_STRING_set(dest, in, len)) {
159             if (free_out) {
160                 ASN1_STRING_free(dest);
161                 *out = NULL;
162             }
163             ERR_raise(ERR_LIB_ASN1, ERR_R_ASN1_LIB);
164             return -1;
165         }
166         return str_type;
167     }
168 
169     /* Work out how much space the destination will need */
170     switch (outform) {
171     case MBSTRING_ASC:
172         outlen = nchar;
173         cpyfunc = cpy_asc;
174         break;
175 
176     case MBSTRING_BMP:
177         outlen = nchar << 1;
178         cpyfunc = cpy_bmp;
179         break;
180 
181     case MBSTRING_UNIV:
182         outlen = nchar << 2;
183         cpyfunc = cpy_univ;
184         break;
185 
186     case MBSTRING_UTF8:
187         outlen = 0;
188         ret = traverse_string(in, len, inform, out_utf8, &outlen);
189         if (ret < 0) {
190             ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_UTF8STRING);
191             return -1;
192         }
193         cpyfunc = cpy_utf8;
194         break;
195     }
196     if ((p = OPENSSL_malloc(outlen + 1)) == NULL) {
197         if (free_out) {
198             ASN1_STRING_free(dest);
199             *out = NULL;
200         }
201         return -1;
202     }
203     dest->length = outlen;
204     dest->data = p;
205     p[outlen] = 0;
206     traverse_string(in, len, inform, cpyfunc, &p);
207     return str_type;
208 }
209 
210 /*
211  * This function traverses a string and passes the value of each character to
212  * an optional function along with a void * argument.
213  */
214 
traverse_string(const unsigned char * p,int len,int inform,int (* rfunc)(unsigned long value,void * in),void * arg)215 static int traverse_string(const unsigned char *p, int len, int inform,
216     int (*rfunc)(unsigned long value, void *in),
217     void *arg)
218 {
219     unsigned long value;
220     int ret;
221     while (len) {
222         if (inform == MBSTRING_ASC) {
223             value = *p++;
224             len--;
225         } else if (inform == MBSTRING_BMP) {
226             value = *p++ << 8;
227             value |= *p++;
228             len -= 2;
229         } else if (inform == MBSTRING_UNIV) {
230             value = ((unsigned long)*p++) << 24;
231             value |= ((unsigned long)*p++) << 16;
232             value |= *p++ << 8;
233             value |= *p++;
234             len -= 4;
235         } else {
236             ret = UTF8_getc(p, len, &value);
237             if (ret < 0)
238                 return -1;
239             len -= ret;
240             p += ret;
241         }
242         if (rfunc) {
243             ret = rfunc(value, arg);
244             if (ret <= 0)
245                 return ret;
246         }
247     }
248     return 1;
249 }
250 
251 /* Various utility functions for traverse_string */
252 
253 /* Just count number of characters */
254 
in_utf8(unsigned long value,void * arg)255 static int in_utf8(unsigned long value, void *arg)
256 {
257     int *nchar;
258 
259     if (!is_unicode_valid(value))
260         return -2;
261     nchar = arg;
262     (*nchar)++;
263     return 1;
264 }
265 
266 /* Determine size of output as a UTF8 String */
267 
out_utf8(unsigned long value,void * arg)268 static int out_utf8(unsigned long value, void *arg)
269 {
270     int *outlen, len;
271 
272     len = UTF8_putc(NULL, -1, value);
273     if (len <= 0)
274         return len;
275     outlen = arg;
276     *outlen += len;
277     return 1;
278 }
279 
280 /*
281  * Determine the "type" of a string: check each character against a supplied
282  * "mask".
283  */
284 
type_str(unsigned long value,void * arg)285 static int type_str(unsigned long value, void *arg)
286 {
287     unsigned long usable_types = *((unsigned long *)arg);
288     unsigned long types = usable_types;
289     const int native = value > INT_MAX ? INT_MAX : ossl_fromascii(value);
290 
291     /*
292      * Clear out all the types which are not checked later. If any of those
293      * is present in the mask, then the UTF8 type will be added and checked
294      * below.
295      */
296     types &= B_ASN1_NUMERICSTRING | B_ASN1_PRINTABLESTRING
297         | B_ASN1_IA5STRING | B_ASN1_T61STRING | B_ASN1_BMPSTRING
298         | B_ASN1_UNIVERSALSTRING | B_ASN1_UTF8STRING;
299 
300     /*
301      * If any other types were in the input mask, they're effectively treated
302      * as UTF8
303      */
304     if (types != usable_types)
305         types |= B_ASN1_UTF8STRING;
306 
307     /*
308      * These checks should be in sync with ASN1_mbstring_ncopy.
309      */
310     if ((types & B_ASN1_NUMERICSTRING) && !(ossl_isdigit(native) || native == ' '))
311         types &= ~B_ASN1_NUMERICSTRING;
312     if ((types & B_ASN1_PRINTABLESTRING) && !ossl_isasn1print(native))
313         types &= ~B_ASN1_PRINTABLESTRING;
314     if ((types & B_ASN1_IA5STRING) && !ossl_isascii(native))
315         types &= ~B_ASN1_IA5STRING;
316     if ((types & B_ASN1_T61STRING) && (value > 0xff))
317         types &= ~B_ASN1_T61STRING;
318     if ((types & B_ASN1_BMPSTRING) && (value > 0xffff))
319         types &= ~B_ASN1_BMPSTRING;
320     if ((types & B_ASN1_UTF8STRING) && !is_unicode_valid(value))
321         types &= ~B_ASN1_UTF8STRING;
322     if (!types)
323         return -1;
324     *((unsigned long *)arg) = types;
325     return 1;
326 }
327 
328 /* Copy one byte per character ASCII like strings */
329 
cpy_asc(unsigned long value,void * arg)330 static int cpy_asc(unsigned long value, void *arg)
331 {
332     unsigned char **p, *q;
333     p = arg;
334     q = *p;
335     *q = (unsigned char)value;
336     (*p)++;
337     return 1;
338 }
339 
340 /* Copy two byte per character BMPStrings */
341 
cpy_bmp(unsigned long value,void * arg)342 static int cpy_bmp(unsigned long value, void *arg)
343 {
344     unsigned char **p, *q;
345     p = arg;
346     q = *p;
347     *q++ = (unsigned char)((value >> 8) & 0xff);
348     *q = (unsigned char)(value & 0xff);
349     *p += 2;
350     return 1;
351 }
352 
353 /* Copy four byte per character UniversalStrings */
354 
cpy_univ(unsigned long value,void * arg)355 static int cpy_univ(unsigned long value, void *arg)
356 {
357     unsigned char **p, *q;
358     p = arg;
359     q = *p;
360     *q++ = (unsigned char)((value >> 24) & 0xff);
361     *q++ = (unsigned char)((value >> 16) & 0xff);
362     *q++ = (unsigned char)((value >> 8) & 0xff);
363     *q = (unsigned char)(value & 0xff);
364     *p += 4;
365     return 1;
366 }
367 
368 /* Copy to a UTF8String */
369 
cpy_utf8(unsigned long value,void * arg)370 static int cpy_utf8(unsigned long value, void *arg)
371 {
372     unsigned char **p;
373     int ret;
374     p = arg;
375     /* We already know there is enough room so pass 0xff as the length */
376     ret = UTF8_putc(*p, 0xff, value);
377     if (ret < 0)
378         return ret;
379     *p += ret;
380     return 1;
381 }
382