More multibyte character support.
[openssl.git] / crypto / asn1 / a_mbstr.c
1 /* a_mbstr.c */
2 /* Written by Dr Stephen N Henson (shenson@bigfoot.com) for the OpenSSL
3  * project 1999.
4  */
5 /* ====================================================================
6  * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer. 
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  *
20  * 3. All advertising materials mentioning features or use of this
21  *    software must display the following acknowledgment:
22  *    "This product includes software developed by the OpenSSL Project
23  *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
24  *
25  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
26  *    endorse or promote products derived from this software without
27  *    prior written permission. For written permission, please contact
28  *    licensing@OpenSSL.org.
29  *
30  * 5. Products derived from this software may not be called "OpenSSL"
31  *    nor may "OpenSSL" appear in their names without prior written
32  *    permission of the OpenSSL Project.
33  *
34  * 6. Redistributions of any form whatsoever must retain the following
35  *    acknowledgment:
36  *    "This product includes software developed by the OpenSSL Project
37  *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
38  *
39  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
40  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
43  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
45  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
48  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
49  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
50  * OF THE POSSIBILITY OF SUCH DAMAGE.
51  * ====================================================================
52  *
53  * This product includes cryptographic software written by Eric Young
54  * (eay@cryptsoft.com).  This product includes software written by Tim
55  * Hudson (tjh@cryptsoft.com).
56  *
57  */
58
59 #include <stdio.h>
60 #include <ctype.h>
61 #include "cryptlib.h"
62 #include <openssl/asn1.h>
63
64 static int traverse_string(const unsigned char *p, int len, int inform,
65                  int (*rfunc)(unsigned long value, void *in), void *arg);
66 static int in_utf8(unsigned long value, void *arg);
67 static int out_utf8(unsigned long value, void *arg);
68 static int type_str(unsigned long value, void *arg);
69 static int cpy_asc(unsigned long value, void *arg);
70 static int cpy_bmp(unsigned long value, void *arg);
71 static int cpy_univ(unsigned long value, void *arg);
72 static int cpy_utf8(unsigned long value, void *arg);
73 static int is_printable(unsigned long value);
74
75 /* This is the default mask for the mbstring functions: it is designed
76  * to be a "safe" DirectoryString. Netscape messenger crashes when it
77  * receives a certificate containing a BMPString so by default we don't
78  * use them unless we have to.
79  */
80
81 static long dirstring_mask = B_ASN1_PRINTABLESTRING
82                                 | B_ASN1_T61STRING | B_ASN1_BMPSTRING;
83
84 void ASN1_STRING_set_default_mask(unsigned long mask)
85 {
86         dirstring_mask = mask;
87 }
88
89 unsigned long ASN1_STRING_get_default_mask(void)
90 {
91         return dirstring_mask;
92 }
93
94 /* These functions take a string in UTF8, ASCII or multibyte form and
95  * a mask of permissible ASN1 string types. It then works out the minimal
96  * type (using the order Printable < IA5 < T61 < BMP < Universal < UTF8)
97  * and creates a string of the correct type with the supplied data.
98  * Yes this is horrible: it has to be :-(
99  * The 'ncopy' form checks minimum and maximum size limits too.
100  */
101
102 int ASN1_mbstring_copy(ASN1_STRING **out, const unsigned char *in, int len,
103                                         int inform, unsigned long mask)
104 {
105         return ASN1_mbstring_ncopy(out, in, len, inform, mask, 0, 0);
106 }
107
108 int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len,
109                                         int inform, unsigned long mask, 
110                                         long minsize, long maxsize)
111 {
112         int str_type;
113         int ret;
114         int outform, outlen;
115         ASN1_STRING *dest;
116         unsigned char *p;
117         int nchar;
118         unsigned char strbuf[32];
119         int (*cpyfunc)(unsigned long,void *) = NULL;
120         if(len == -1) len = strlen((const char *)in);
121         if(!mask) mask = dirstring_mask;
122
123         /* First do a string check and work out the number of characters */
124         switch(inform) {
125
126                 case MBSTRING_BMP:
127                 if(len & 1) {
128                         ASN1err(ASN1_F_ASN1_MBSTRING_COPY,
129                                          ASN1_R_INVALID_BMPSTRING_LENGTH);
130                         return -1;
131                 }
132                 nchar = len >> 1;
133                 break;
134
135                 case MBSTRING_UNIV:
136                 if(len & 3) {
137                         ASN1err(ASN1_F_ASN1_MBSTRING_COPY,
138                                          ASN1_R_INVALID_UNIVERSALSTRING_LENGTH);
139                         return -1;
140                 }
141                 nchar = len >> 2;
142                 break;
143
144                 case MBSTRING_UTF8:
145                 nchar = 0;
146                 /* This counts the characters and does utf8 syntax checking */
147                 ret = traverse_string(in, len, MBSTRING_UTF8, in_utf8, &nchar);
148                 if(ret < 0) {
149                         ASN1err(ASN1_F_ASN1_MBSTRING_COPY,
150                                                  ASN1_R_INVALID_UTF8STRING);
151                         return -1;
152                 }
153                 break;
154
155                 case MBSTRING_ASC:
156                 nchar = len;
157                 break;
158
159                 default:
160                 ASN1err(ASN1_F_ASN1_MBSTRING_COPY, ASN1_R_UNKNOWN_FORMAT);
161                 return -1;
162         }
163
164         if(minsize && (nchar < minsize)) {
165                 ASN1err(ASN1_F_ASN1_MBSTRING_COPY, ASN1_R_STRING_TOO_SHORT);
166                 sprintf(strbuf, "%ld", minsize);
167                 ERR_add_error_data(2, "minsize=", strbuf);
168                 return -1;
169         }
170
171         if(maxsize && (nchar > maxsize)) {
172                 ASN1err(ASN1_F_ASN1_MBSTRING_COPY, ASN1_R_STRING_TOO_LONG);
173                 sprintf(strbuf, "%ld", maxsize);
174                 ERR_add_error_data(2, "maxsize=", strbuf);
175                 return -1;
176         }
177
178         /* Now work out minimal type (if any) */
179         if(traverse_string(in, len, inform, type_str, &mask) < 0) {
180                 ASN1err(ASN1_F_ASN1_MBSTRING_COPY, ASN1_R_ILLEGAL_CHARACTERS);
181                 return -1;
182         }
183
184
185         /* Now work out output format and string type */
186         outform = MBSTRING_ASC;
187         if(mask & B_ASN1_PRINTABLESTRING) str_type = V_ASN1_PRINTABLESTRING;
188         else if(mask & B_ASN1_IA5STRING) str_type = V_ASN1_IA5STRING;
189         else if(mask & B_ASN1_T61STRING) str_type = V_ASN1_T61STRING;
190         else if(mask & B_ASN1_BMPSTRING) {
191                 str_type = V_ASN1_BMPSTRING;
192                 outform = MBSTRING_BMP;
193         } else if(mask & B_ASN1_UNIVERSALSTRING) {
194                 str_type = V_ASN1_UNIVERSALSTRING;
195                 outform = MBSTRING_UNIV;
196         } else {
197                 str_type = V_ASN1_UTF8STRING;
198                 outform = MBSTRING_UTF8;
199         }
200         if(!out) return str_type;
201         if(*out) {
202                 dest = *out;
203                 if(dest->data) {
204                         dest->length = 0;
205                         Free(dest->data);
206                         dest->data = NULL;
207                 }
208                 dest->type = str_type;
209         } else {
210                 dest = ASN1_STRING_type_new(str_type);
211                 if(!dest) {
212                         ASN1err(ASN1_F_ASN1_MBSTRING_COPY,
213                                                         ERR_R_MALLOC_FAILURE);
214                         return -1;
215                 }
216                 *out = dest;
217         }
218         /* If both the same type just copy across */
219         if(inform == outform) {
220                 if(!ASN1_STRING_set(dest, in, len)) {
221                         ASN1err(ASN1_F_ASN1_MBSTRING_COPY,ERR_R_MALLOC_FAILURE);
222                         return -1;
223                 }
224                 return str_type;
225         } 
226
227         /* Work out how much space the destination will need */
228         switch(outform) {
229                 case MBSTRING_ASC:
230                 outlen = nchar;
231                 cpyfunc = cpy_asc;
232                 break;
233
234                 case MBSTRING_BMP:
235                 outlen = nchar << 1;
236                 cpyfunc = cpy_bmp;
237                 break;
238
239                 case MBSTRING_UNIV:
240                 outlen = nchar << 2;
241                 cpyfunc = cpy_univ;
242                 break;
243
244                 case MBSTRING_UTF8:
245                 outlen = 0;
246                 traverse_string(in, len, inform, out_utf8, &outlen);
247                 cpyfunc = cpy_utf8;
248                 break;
249         }
250         if(!(p = Malloc(outlen + 1))) {
251                 ASN1_STRING_free(dest);
252                 ASN1err(ASN1_F_ASN1_MBSTRING_COPY,ERR_R_MALLOC_FAILURE);
253                 return -1;
254         }
255         dest->length = outlen;
256         dest->data = p;
257         p[outlen] = 0;
258         traverse_string(in, len, inform, cpyfunc, &p);
259         return str_type;        
260 }
261
262 /* This function traverses a string and passes the value of each character
263  * to an optional function along with a void * argument.
264  */
265
266 static int traverse_string(const unsigned char *p, int len, int inform,
267                  int (*rfunc)(unsigned long value, void *in), void *arg)
268 {
269         unsigned long value;
270         int ret;
271         while(len) {
272                 if(inform == MBSTRING_ASC) {
273                         value = *p++;
274                         len--;
275                 } else if(inform == MBSTRING_BMP) {
276                         value = *p++ << 8;
277                         value |= *p++;
278                         len -= 2;
279                 } else if(inform == MBSTRING_UNIV) {
280                         value = *p++ << 24;
281                         value |= *p++ << 16;
282                         value |= *p++ << 8;
283                         value |= *p++;
284                         len -= 4;
285                 } else {
286                         ret = UTF8_getc(p, len, &value);
287                         if(ret < 0) return -1;
288                         len -= ret;
289                         p += ret;
290                 }
291                 if(rfunc) {
292                         ret = rfunc(value, arg);
293                         if(ret <= 0) return ret;
294                 }
295         }
296         return 1;
297 }
298
299 /* Various utility functions for traverse_string */
300
301 /* Just count number of characters */
302
303 static int in_utf8(unsigned long value, void *arg)
304 {
305         int *nchar;
306         nchar = arg;
307         (*nchar)++;
308         return 1;
309 }
310
311 /* Determine size of output as a UTF8 String */
312
313 static int out_utf8(unsigned long value, void *arg)
314 {
315         long *outlen;
316         outlen = arg;
317         *outlen += UTF8_putc(NULL, -1, value);
318         return 1;
319 }
320
321 /* Determine the "type" of a string: check each character against a
322  * supplied "mask".
323  */
324
325 static int type_str(unsigned long value, void *arg)
326 {
327         unsigned long types;
328         types = *((unsigned long *)arg);
329         if((types & B_ASN1_PRINTABLESTRING) && !is_printable(value))
330                                         types &= ~B_ASN1_PRINTABLESTRING;
331         if((types & B_ASN1_IA5STRING) && (value > 127))
332                                         types &= ~B_ASN1_IA5STRING;
333         if((types & B_ASN1_T61STRING) && (value > 0xff))
334                                         types &= ~B_ASN1_T61STRING;
335         if((types & B_ASN1_BMPSTRING) && (value > 0xffff))
336                                         types &= ~B_ASN1_BMPSTRING;
337         if(!types) return -1;
338         *((unsigned long *)arg) = types;
339         return 1;
340 }
341
342 /* Copy one byte per character ASCII like strings */
343
344 static int cpy_asc(unsigned long value, void *arg)
345 {
346         unsigned char **p, *q;
347         p = arg;
348         q = *p;
349         *q = (unsigned char) value;
350         (*p)++;
351         return 1;
352 }
353
354 /* Copy two byte per character BMPStrings */
355
356 static int cpy_bmp(unsigned long value, void *arg)
357 {
358         unsigned char **p, *q;
359         p = arg;
360         q = *p;
361         *q++ = (unsigned char) ((value >> 8) & 0xff);
362         *q = (unsigned char) (value & 0xff);
363         *p += 2;
364         return 1;
365 }
366
367 /* Copy four byte per character UniversalStrings */
368
369 static int cpy_univ(unsigned long value, void *arg)
370 {
371         unsigned char **p, *q;
372         p = arg;
373         q = *p;
374         *q++ = (unsigned char) ((value >> 24) & 0xff);
375         *q++ = (unsigned char) ((value >> 16) & 0xff);
376         *q++ = (unsigned char) ((value >> 8) & 0xff);
377         *q = (unsigned char) (value & 0xff);
378         *p += 4;
379         return 1;
380 }
381
382 /* Copy to a UTF8String */
383
384 static int cpy_utf8(unsigned long value, void *arg)
385 {
386         unsigned char **p;
387         int ret;
388         p = arg;
389         /* We already know there is enough room so pass 0xff as the length */
390         ret = UTF8_putc(*p, 0xff, value);
391         *p += ret;
392         return 1;
393 }
394
395 /* Return 1 if the character is permitted in a PrintableString */
396 static int is_printable(unsigned long value)
397 {
398         int ch;
399         if(value > 0x7f) return 0;
400         ch = (int) value;
401         /* Note: we can't use 'isalnum' because certain accented 
402          * characters may count as alphanumeric in some environments.
403          */
404         if((ch >= 'a') && (ch <= 'z')) return 1;
405         if((ch >= 'A') && (ch <= 'Z')) return 1;
406         if((ch >= '0') && (ch <= '9')) return 1;
407         if ((ch == ' ') || strchr("'()+,-./:=?", ch)) return 1;
408         return 0;
409 }