159191Skris/* a_mbstr.c */
2194206Ssimon/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
359191Skris * project 1999.
459191Skris */
559191Skris/* ====================================================================
659191Skris * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
759191Skris *
859191Skris * Redistribution and use in source and binary forms, with or without
959191Skris * modification, are permitted provided that the following conditions
1059191Skris * are met:
1159191Skris *
1259191Skris * 1. Redistributions of source code must retain the above copyright
1359191Skris *    notice, this list of conditions and the following disclaimer.
1459191Skris *
1559191Skris * 2. Redistributions in binary form must reproduce the above copyright
1659191Skris *    notice, this list of conditions and the following disclaimer in
1759191Skris *    the documentation and/or other materials provided with the
1859191Skris *    distribution.
1959191Skris *
2059191Skris * 3. All advertising materials mentioning features or use of this
2159191Skris *    software must display the following acknowledgment:
2259191Skris *    "This product includes software developed by the OpenSSL Project
2359191Skris *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
2459191Skris *
2559191Skris * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
2659191Skris *    endorse or promote products derived from this software without
2759191Skris *    prior written permission. For written permission, please contact
2859191Skris *    licensing@OpenSSL.org.
2959191Skris *
3059191Skris * 5. Products derived from this software may not be called "OpenSSL"
3159191Skris *    nor may "OpenSSL" appear in their names without prior written
3259191Skris *    permission of the OpenSSL Project.
3359191Skris *
3459191Skris * 6. Redistributions of any form whatsoever must retain the following
3559191Skris *    acknowledgment:
3659191Skris *    "This product includes software developed by the OpenSSL Project
3759191Skris *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
3859191Skris *
3959191Skris * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
4059191Skris * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4159191Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
4259191Skris * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
4359191Skris * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
4459191Skris * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
4559191Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
4659191Skris * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
4759191Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
4859191Skris * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
4959191Skris * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
5059191Skris * OF THE POSSIBILITY OF SUCH DAMAGE.
5159191Skris * ====================================================================
5259191Skris *
5359191Skris * This product includes cryptographic software written by Eric Young
5459191Skris * (eay@cryptsoft.com).  This product includes software written by Tim
5559191Skris * Hudson (tjh@cryptsoft.com).
5659191Skris *
5759191Skris */
5859191Skris
5959191Skris#include <stdio.h>
6059191Skris#include <ctype.h>
6159191Skris#include "cryptlib.h"
6259191Skris#include <openssl/asn1.h>
6359191Skris
6459191Skrisstatic int traverse_string(const unsigned char *p, int len, int inform,
6559191Skris		 int (*rfunc)(unsigned long value, void *in), void *arg);
6659191Skrisstatic int in_utf8(unsigned long value, void *arg);
6759191Skrisstatic int out_utf8(unsigned long value, void *arg);
6859191Skrisstatic int type_str(unsigned long value, void *arg);
6959191Skrisstatic int cpy_asc(unsigned long value, void *arg);
7059191Skrisstatic int cpy_bmp(unsigned long value, void *arg);
7159191Skrisstatic int cpy_univ(unsigned long value, void *arg);
7259191Skrisstatic int cpy_utf8(unsigned long value, void *arg);
7359191Skrisstatic int is_printable(unsigned long value);
7459191Skris
7559191Skris/* These functions take a string in UTF8, ASCII or multibyte form and
7659191Skris * a mask of permissible ASN1 string types. It then works out the minimal
7759191Skris * type (using the order Printable < IA5 < T61 < BMP < Universal < UTF8)
7859191Skris * and creates a string of the correct type with the supplied data.
7959191Skris * Yes this is horrible: it has to be :-(
8059191Skris * The 'ncopy' form checks minimum and maximum size limits too.
8159191Skris */
8259191Skris
8359191Skrisint ASN1_mbstring_copy(ASN1_STRING **out, const unsigned char *in, int len,
8459191Skris					int inform, unsigned long mask)
8559191Skris{
8659191Skris	return ASN1_mbstring_ncopy(out, in, len, inform, mask, 0, 0);
8759191Skris}
8859191Skris
8959191Skrisint ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len,
9059191Skris					int inform, unsigned long mask,
9159191Skris					long minsize, long maxsize)
9259191Skris{
9359191Skris	int str_type;
9459191Skris	int ret;
9568651Skris	char free_out;
96205128Ssimon	int outform, outlen = 0;
9759191Skris	ASN1_STRING *dest;
9859191Skris	unsigned char *p;
9959191Skris	int nchar;
10059191Skris	char strbuf[32];
10159191Skris	int (*cpyfunc)(unsigned long,void *) = NULL;
10259191Skris	if(len == -1) len = strlen((const char *)in);
10359191Skris	if(!mask) mask = DIRSTRING_TYPE;
10459191Skris
10559191Skris	/* First do a string check and work out the number of characters */
10659191Skris	switch(inform) {
10759191Skris
10859191Skris		case MBSTRING_BMP:
10959191Skris		if(len & 1) {
110160814Ssimon			ASN1err(ASN1_F_ASN1_MBSTRING_NCOPY,
11159191Skris					 ASN1_R_INVALID_BMPSTRING_LENGTH);
11259191Skris			return -1;
11359191Skris		}
11459191Skris		nchar = len >> 1;
11559191Skris		break;
11659191Skris
11759191Skris		case MBSTRING_UNIV:
11859191Skris		if(len & 3) {
119160814Ssimon			ASN1err(ASN1_F_ASN1_MBSTRING_NCOPY,
12059191Skris					 ASN1_R_INVALID_UNIVERSALSTRING_LENGTH);
12159191Skris			return -1;
12259191Skris		}
12359191Skris		nchar = len >> 2;
12459191Skris		break;
12559191Skris
12659191Skris		case MBSTRING_UTF8:
12759191Skris		nchar = 0;
12859191Skris		/* This counts the characters and does utf8 syntax checking */
12959191Skris		ret = traverse_string(in, len, MBSTRING_UTF8, in_utf8, &nchar);
13059191Skris		if(ret < 0) {
131160814Ssimon			ASN1err(ASN1_F_ASN1_MBSTRING_NCOPY,
13259191Skris						 ASN1_R_INVALID_UTF8STRING);
13359191Skris			return -1;
13459191Skris		}
13559191Skris		break;
13659191Skris
13759191Skris		case MBSTRING_ASC:
13859191Skris		nchar = len;
13959191Skris		break;
14059191Skris
14159191Skris		default:
142160814Ssimon		ASN1err(ASN1_F_ASN1_MBSTRING_NCOPY, ASN1_R_UNKNOWN_FORMAT);
14359191Skris		return -1;
14459191Skris	}
14559191Skris
14659191Skris	if((minsize > 0) && (nchar < minsize)) {
147160814Ssimon		ASN1err(ASN1_F_ASN1_MBSTRING_NCOPY, ASN1_R_STRING_TOO_SHORT);
148127128Snectar		BIO_snprintf(strbuf, sizeof strbuf, "%ld", minsize);
14959191Skris		ERR_add_error_data(2, "minsize=", strbuf);
15059191Skris		return -1;
15159191Skris	}
15259191Skris
15359191Skris	if((maxsize > 0) && (nchar > maxsize)) {
154160814Ssimon		ASN1err(ASN1_F_ASN1_MBSTRING_NCOPY, ASN1_R_STRING_TOO_LONG);
155127128Snectar		BIO_snprintf(strbuf, sizeof strbuf, "%ld", maxsize);
15659191Skris		ERR_add_error_data(2, "maxsize=", strbuf);
15759191Skris		return -1;
15859191Skris	}
15959191Skris
16059191Skris	/* Now work out minimal type (if any) */
16159191Skris	if(traverse_string(in, len, inform, type_str, &mask) < 0) {
162160814Ssimon		ASN1err(ASN1_F_ASN1_MBSTRING_NCOPY, ASN1_R_ILLEGAL_CHARACTERS);
16359191Skris		return -1;
16459191Skris	}
16559191Skris
16659191Skris
16759191Skris	/* Now work out output format and string type */
16859191Skris	outform = MBSTRING_ASC;
16959191Skris	if(mask & B_ASN1_PRINTABLESTRING) str_type = V_ASN1_PRINTABLESTRING;
17059191Skris	else if(mask & B_ASN1_IA5STRING) str_type = V_ASN1_IA5STRING;
17159191Skris	else if(mask & B_ASN1_T61STRING) str_type = V_ASN1_T61STRING;
17259191Skris	else if(mask & B_ASN1_BMPSTRING) {
17359191Skris		str_type = V_ASN1_BMPSTRING;
17459191Skris		outform = MBSTRING_BMP;
17559191Skris	} else if(mask & B_ASN1_UNIVERSALSTRING) {
17659191Skris		str_type = V_ASN1_UNIVERSALSTRING;
17759191Skris		outform = MBSTRING_UNIV;
17859191Skris	} else {
17959191Skris		str_type = V_ASN1_UTF8STRING;
18059191Skris		outform = MBSTRING_UTF8;
18159191Skris	}
18259191Skris	if(!out) return str_type;
18359191Skris	if(*out) {
18468651Skris		free_out = 0;
18559191Skris		dest = *out;
18659191Skris		if(dest->data) {
18759191Skris			dest->length = 0;
18868651Skris			OPENSSL_free(dest->data);
18959191Skris			dest->data = NULL;
19059191Skris		}
19159191Skris		dest->type = str_type;
19259191Skris	} else {
19368651Skris		free_out = 1;
19459191Skris		dest = ASN1_STRING_type_new(str_type);
19559191Skris		if(!dest) {
196160814Ssimon			ASN1err(ASN1_F_ASN1_MBSTRING_NCOPY,
19759191Skris							ERR_R_MALLOC_FAILURE);
19859191Skris			return -1;
19959191Skris		}
20059191Skris		*out = dest;
20159191Skris	}
20259191Skris	/* If both the same type just copy across */
20359191Skris	if(inform == outform) {
20459191Skris		if(!ASN1_STRING_set(dest, in, len)) {
205160814Ssimon			ASN1err(ASN1_F_ASN1_MBSTRING_NCOPY,ERR_R_MALLOC_FAILURE);
20659191Skris			return -1;
20759191Skris		}
20859191Skris		return str_type;
20959191Skris	}
21059191Skris
21159191Skris	/* Work out how much space the destination will need */
21259191Skris	switch(outform) {
21359191Skris		case MBSTRING_ASC:
21459191Skris		outlen = nchar;
21559191Skris		cpyfunc = cpy_asc;
21659191Skris		break;
21759191Skris
21859191Skris		case MBSTRING_BMP:
21959191Skris		outlen = nchar << 1;
22059191Skris		cpyfunc = cpy_bmp;
22159191Skris		break;
22259191Skris
22359191Skris		case MBSTRING_UNIV:
22459191Skris		outlen = nchar << 2;
22559191Skris		cpyfunc = cpy_univ;
22659191Skris		break;
22759191Skris
22859191Skris		case MBSTRING_UTF8:
22959191Skris		outlen = 0;
23059191Skris		traverse_string(in, len, inform, out_utf8, &outlen);
23159191Skris		cpyfunc = cpy_utf8;
23259191Skris		break;
23359191Skris	}
23468651Skris	if(!(p = OPENSSL_malloc(outlen + 1))) {
23568651Skris		if(free_out) ASN1_STRING_free(dest);
236160814Ssimon		ASN1err(ASN1_F_ASN1_MBSTRING_NCOPY,ERR_R_MALLOC_FAILURE);
23759191Skris		return -1;
23859191Skris	}
23959191Skris	dest->length = outlen;
24059191Skris	dest->data = p;
24159191Skris	p[outlen] = 0;
24259191Skris	traverse_string(in, len, inform, cpyfunc, &p);
24359191Skris	return str_type;
24459191Skris}
24559191Skris
24659191Skris/* This function traverses a string and passes the value of each character
24759191Skris * to an optional function along with a void * argument.
24859191Skris */
24959191Skris
25059191Skrisstatic int traverse_string(const unsigned char *p, int len, int inform,
25159191Skris		 int (*rfunc)(unsigned long value, void *in), void *arg)
25259191Skris{
25359191Skris	unsigned long value;
25459191Skris	int ret;
25559191Skris	while(len) {
25659191Skris		if(inform == MBSTRING_ASC) {
25759191Skris			value = *p++;
25859191Skris			len--;
25959191Skris		} else if(inform == MBSTRING_BMP) {
26059191Skris			value = *p++ << 8;
26159191Skris			value |= *p++;
26259191Skris			len -= 2;
26359191Skris		} else if(inform == MBSTRING_UNIV) {
26468651Skris			value = ((unsigned long)*p++) << 24;
26568651Skris			value |= ((unsigned long)*p++) << 16;
26659191Skris			value |= *p++ << 8;
26759191Skris			value |= *p++;
26859191Skris			len -= 4;
26959191Skris		} else {
27059191Skris			ret = UTF8_getc(p, len, &value);
27159191Skris			if(ret < 0) return -1;
27259191Skris			len -= ret;
27359191Skris			p += ret;
27459191Skris		}
27559191Skris		if(rfunc) {
27659191Skris			ret = rfunc(value, arg);
27759191Skris			if(ret <= 0) return ret;
27859191Skris		}
27959191Skris	}
28059191Skris	return 1;
28159191Skris}
28259191Skris
28359191Skris/* Various utility functions for traverse_string */
28459191Skris
28559191Skris/* Just count number of characters */
28659191Skris
28759191Skrisstatic int in_utf8(unsigned long value, void *arg)
28859191Skris{
28959191Skris	int *nchar;
29059191Skris	nchar = arg;
29159191Skris	(*nchar)++;
29259191Skris	return 1;
29359191Skris}
29459191Skris
29559191Skris/* Determine size of output as a UTF8 String */
29659191Skris
29759191Skrisstatic int out_utf8(unsigned long value, void *arg)
29859191Skris{
299120631Snectar	int *outlen;
30059191Skris	outlen = arg;
30159191Skris	*outlen += UTF8_putc(NULL, -1, value);
30259191Skris	return 1;
30359191Skris}
30459191Skris
30559191Skris/* Determine the "type" of a string: check each character against a
30659191Skris * supplied "mask".
30759191Skris */
30859191Skris
30959191Skrisstatic int type_str(unsigned long value, void *arg)
31059191Skris{
31159191Skris	unsigned long types;
31259191Skris	types = *((unsigned long *)arg);
31359191Skris	if((types & B_ASN1_PRINTABLESTRING) && !is_printable(value))
31459191Skris					types &= ~B_ASN1_PRINTABLESTRING;
31559191Skris	if((types & B_ASN1_IA5STRING) && (value > 127))
31659191Skris					types &= ~B_ASN1_IA5STRING;
31759191Skris	if((types & B_ASN1_T61STRING) && (value > 0xff))
31859191Skris					types &= ~B_ASN1_T61STRING;
31959191Skris	if((types & B_ASN1_BMPSTRING) && (value > 0xffff))
32059191Skris					types &= ~B_ASN1_BMPSTRING;
32159191Skris	if(!types) return -1;
32259191Skris	*((unsigned long *)arg) = types;
32359191Skris	return 1;
32459191Skris}
32559191Skris
32659191Skris/* Copy one byte per character ASCII like strings */
32759191Skris
32859191Skrisstatic int cpy_asc(unsigned long value, void *arg)
32959191Skris{
33059191Skris	unsigned char **p, *q;
33159191Skris	p = arg;
33259191Skris	q = *p;
33359191Skris	*q = (unsigned char) value;
33459191Skris	(*p)++;
33559191Skris	return 1;
33659191Skris}
33759191Skris
33859191Skris/* Copy two byte per character BMPStrings */
33959191Skris
34059191Skrisstatic int cpy_bmp(unsigned long value, void *arg)
34159191Skris{
34259191Skris	unsigned char **p, *q;
34359191Skris	p = arg;
34459191Skris	q = *p;
34559191Skris	*q++ = (unsigned char) ((value >> 8) & 0xff);
34659191Skris	*q = (unsigned char) (value & 0xff);
34759191Skris	*p += 2;
34859191Skris	return 1;
34959191Skris}
35059191Skris
35159191Skris/* Copy four byte per character UniversalStrings */
35259191Skris
35359191Skrisstatic int cpy_univ(unsigned long value, void *arg)
35459191Skris{
35559191Skris	unsigned char **p, *q;
35659191Skris	p = arg;
35759191Skris	q = *p;
35859191Skris	*q++ = (unsigned char) ((value >> 24) & 0xff);
35959191Skris	*q++ = (unsigned char) ((value >> 16) & 0xff);
36059191Skris	*q++ = (unsigned char) ((value >> 8) & 0xff);
36159191Skris	*q = (unsigned char) (value & 0xff);
36259191Skris	*p += 4;
36359191Skris	return 1;
36459191Skris}
36559191Skris
36659191Skris/* Copy to a UTF8String */
36759191Skris
36859191Skrisstatic int cpy_utf8(unsigned long value, void *arg)
36959191Skris{
37059191Skris	unsigned char **p;
37159191Skris	int ret;
37259191Skris	p = arg;
37359191Skris	/* We already know there is enough room so pass 0xff as the length */
37459191Skris	ret = UTF8_putc(*p, 0xff, value);
37559191Skris	*p += ret;
37659191Skris	return 1;
37759191Skris}
37859191Skris
37959191Skris/* Return 1 if the character is permitted in a PrintableString */
38059191Skrisstatic int is_printable(unsigned long value)
38159191Skris{
38259191Skris	int ch;
38359191Skris	if(value > 0x7f) return 0;
38459191Skris	ch = (int) value;
38559191Skris	/* Note: we can't use 'isalnum' because certain accented
38659191Skris	 * characters may count as alphanumeric in some environments.
38759191Skris	 */
38868651Skris#ifndef CHARSET_EBCDIC
38959191Skris	if((ch >= 'a') && (ch <= 'z')) return 1;
39059191Skris	if((ch >= 'A') && (ch <= 'Z')) return 1;
39159191Skris	if((ch >= '0') && (ch <= '9')) return 1;
39259191Skris	if ((ch == ' ') || strchr("'()+,-./:=?", ch)) return 1;
39368651Skris#else /*CHARSET_EBCDIC*/
39468651Skris	if((ch >= os_toascii['a']) && (ch <= os_toascii['z'])) return 1;
39568651Skris	if((ch >= os_toascii['A']) && (ch <= os_toascii['Z'])) return 1;
39668651Skris	if((ch >= os_toascii['0']) && (ch <= os_toascii['9'])) return 1;
39768651Skris	if ((ch == os_toascii[' ']) || strchr("'()+,-./:=?", os_toebcdic[ch])) return 1;
39868651Skris#endif /*CHARSET_EBCDIC*/
39959191Skris	return 0;
40059191Skris}
401