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