a_strex.c revision 160814
1/* a_strex.c */
2/* Written by Dr Stephen N Henson (shenson@bigfoot.com) for the OpenSSL
3 * project 2000.
4 */
5/* ====================================================================
6 * Copyright (c) 2000 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 <string.h>
61#include "cryptlib.h"
62#include <openssl/crypto.h>
63#include <openssl/x509.h>
64#include <openssl/asn1.h>
65
66#include "charmap.h"
67
68/* ASN1_STRING_print_ex() and X509_NAME_print_ex().
69 * Enhanced string and name printing routines handling
70 * multibyte characters, RFC2253 and a host of other
71 * options.
72 */
73
74
75#define CHARTYPE_BS_ESC		(ASN1_STRFLGS_ESC_2253 | CHARTYPE_FIRST_ESC_2253 | CHARTYPE_LAST_ESC_2253)
76
77
78/* Three IO functions for sending data to memory, a BIO and
79 * and a FILE pointer.
80 */
81#if 0				/* never used */
82static int send_mem_chars(void *arg, const void *buf, int len)
83{
84	unsigned char **out = arg;
85	if(!out) return 1;
86	memcpy(*out, buf, len);
87	*out += len;
88	return 1;
89}
90#endif
91
92static int send_bio_chars(void *arg, const void *buf, int len)
93{
94	if(!arg) return 1;
95	if(BIO_write(arg, buf, len) != len) return 0;
96	return 1;
97}
98
99static int send_fp_chars(void *arg, const void *buf, int len)
100{
101	if(!arg) return 1;
102	if(fwrite(buf, 1, len, arg) != (unsigned int)len) return 0;
103	return 1;
104}
105
106typedef int char_io(void *arg, const void *buf, int len);
107
108/* This function handles display of
109 * strings, one character at a time.
110 * It is passed an unsigned long for each
111 * character because it could come from 2 or even
112 * 4 byte forms.
113 */
114
115static int do_esc_char(unsigned long c, unsigned char flags, char *do_quotes, char_io *io_ch, void *arg)
116{
117	unsigned char chflgs, chtmp;
118	char tmphex[HEX_SIZE(long)+3];
119
120	if(c > 0xffffffffL)
121		return -1;
122	if(c > 0xffff) {
123		BIO_snprintf(tmphex, sizeof tmphex, "\\W%08lX", c);
124		if(!io_ch(arg, tmphex, 10)) return -1;
125		return 10;
126	}
127	if(c > 0xff) {
128		BIO_snprintf(tmphex, sizeof tmphex, "\\U%04lX", c);
129		if(!io_ch(arg, tmphex, 6)) return -1;
130		return 6;
131	}
132	chtmp = (unsigned char)c;
133	if(chtmp > 0x7f) chflgs = flags & ASN1_STRFLGS_ESC_MSB;
134	else chflgs = char_type[chtmp] & flags;
135	if(chflgs & CHARTYPE_BS_ESC) {
136		/* If we don't escape with quotes, signal we need quotes */
137		if(chflgs & ASN1_STRFLGS_ESC_QUOTE) {
138			if(do_quotes) *do_quotes = 1;
139			if(!io_ch(arg, &chtmp, 1)) return -1;
140			return 1;
141		}
142		if(!io_ch(arg, "\\", 1)) return -1;
143		if(!io_ch(arg, &chtmp, 1)) return -1;
144		return 2;
145	}
146	if(chflgs & (ASN1_STRFLGS_ESC_CTRL|ASN1_STRFLGS_ESC_MSB)) {
147		BIO_snprintf(tmphex, 11, "\\%02X", chtmp);
148		if(!io_ch(arg, tmphex, 3)) return -1;
149		return 3;
150	}
151	if(!io_ch(arg, &chtmp, 1)) return -1;
152	return 1;
153}
154
155#define BUF_TYPE_WIDTH_MASK	0x7
156#define BUF_TYPE_CONVUTF8	0x8
157
158/* This function sends each character in a buffer to
159 * do_esc_char(). It interprets the content formats
160 * and converts to or from UTF8 as appropriate.
161 */
162
163static int do_buf(unsigned char *buf, int buflen,
164			int type, unsigned char flags, char *quotes, char_io *io_ch, void *arg)
165{
166	int i, outlen, len;
167	unsigned char orflags, *p, *q;
168	unsigned long c;
169	p = buf;
170	q = buf + buflen;
171	outlen = 0;
172	while(p != q) {
173		if(p == buf) orflags = CHARTYPE_FIRST_ESC_2253;
174		else orflags = 0;
175		switch(type & BUF_TYPE_WIDTH_MASK) {
176			case 4:
177			c = ((unsigned long)*p++) << 24;
178			c |= ((unsigned long)*p++) << 16;
179			c |= ((unsigned long)*p++) << 8;
180			c |= *p++;
181			break;
182
183			case 2:
184			c = ((unsigned long)*p++) << 8;
185			c |= *p++;
186			break;
187
188			case 1:
189			c = *p++;
190			break;
191
192			case 0:
193			i = UTF8_getc(p, buflen, &c);
194			if(i < 0) return -1;	/* Invalid UTF8String */
195			p += i;
196			break;
197			default:
198			return -1;	/* invalid width */
199		}
200		if (p == q) orflags = CHARTYPE_LAST_ESC_2253;
201		if(type & BUF_TYPE_CONVUTF8) {
202			unsigned char utfbuf[6];
203			int utflen;
204			utflen = UTF8_putc(utfbuf, sizeof utfbuf, c);
205			for(i = 0; i < utflen; i++) {
206				/* We don't need to worry about setting orflags correctly
207				 * because if utflen==1 its value will be correct anyway
208				 * otherwise each character will be > 0x7f and so the
209				 * character will never be escaped on first and last.
210				 */
211				len = do_esc_char(utfbuf[i], (unsigned char)(flags | orflags), quotes, io_ch, arg);
212				if(len < 0) return -1;
213				outlen += len;
214			}
215		} else {
216			len = do_esc_char(c, (unsigned char)(flags | orflags), quotes, io_ch, arg);
217			if(len < 0) return -1;
218			outlen += len;
219		}
220	}
221	return outlen;
222}
223
224/* This function hex dumps a buffer of characters */
225
226static int do_hex_dump(char_io *io_ch, void *arg, unsigned char *buf, int buflen)
227{
228	static const char hexdig[] = "0123456789ABCDEF";
229	unsigned char *p, *q;
230	char hextmp[2];
231	if(arg) {
232		p = buf;
233		q = buf + buflen;
234		while(p != q) {
235			hextmp[0] = hexdig[*p >> 4];
236			hextmp[1] = hexdig[*p & 0xf];
237			if(!io_ch(arg, hextmp, 2)) return -1;
238			p++;
239		}
240	}
241	return buflen << 1;
242}
243
244/* "dump" a string. This is done when the type is unknown,
245 * or the flags request it. We can either dump the content
246 * octets or the entire DER encoding. This uses the RFC2253
247 * #01234 format.
248 */
249
250static int do_dump(unsigned long lflags, char_io *io_ch, void *arg, ASN1_STRING *str)
251{
252	/* Placing the ASN1_STRING in a temp ASN1_TYPE allows
253	 * the DER encoding to readily obtained
254	 */
255	ASN1_TYPE t;
256	unsigned char *der_buf, *p;
257	int outlen, der_len;
258
259	if(!io_ch(arg, "#", 1)) return -1;
260	/* If we don't dump DER encoding just dump content octets */
261	if(!(lflags & ASN1_STRFLGS_DUMP_DER)) {
262		outlen = do_hex_dump(io_ch, arg, str->data, str->length);
263		if(outlen < 0) return -1;
264		return outlen + 1;
265	}
266	t.type = str->type;
267	t.value.ptr = (char *)str;
268	der_len = i2d_ASN1_TYPE(&t, NULL);
269	der_buf = OPENSSL_malloc(der_len);
270	if(!der_buf) return -1;
271	p = der_buf;
272	i2d_ASN1_TYPE(&t, &p);
273	outlen = do_hex_dump(io_ch, arg, der_buf, der_len);
274	OPENSSL_free(der_buf);
275	if(outlen < 0) return -1;
276	return outlen + 1;
277}
278
279/* Lookup table to convert tags to character widths,
280 * 0 = UTF8 encoded, -1 is used for non string types
281 * otherwise it is the number of bytes per character
282 */
283
284static const signed char tag2nbyte[] = {
285	-1, -1, -1, -1, -1,	/* 0-4 */
286	-1, -1, -1, -1, -1,	/* 5-9 */
287	-1, -1, 0, -1,		/* 10-13 */
288	-1, -1, -1, -1,		/* 15-17 */
289	-1, 1, 1,		/* 18-20 */
290	-1, 1, 1, 1,		/* 21-24 */
291	-1, 1, -1,		/* 25-27 */
292	4, -1, 2		/* 28-30 */
293};
294
295#define ESC_FLAGS (ASN1_STRFLGS_ESC_2253 | \
296		  ASN1_STRFLGS_ESC_QUOTE | \
297		  ASN1_STRFLGS_ESC_CTRL | \
298		  ASN1_STRFLGS_ESC_MSB)
299
300/* This is the main function, print out an
301 * ASN1_STRING taking note of various escape
302 * and display options. Returns number of
303 * characters written or -1 if an error
304 * occurred.
305 */
306
307static int do_print_ex(char_io *io_ch, void *arg, unsigned long lflags, ASN1_STRING *str)
308{
309	int outlen, len;
310	int type;
311	char quotes;
312	unsigned char flags;
313	quotes = 0;
314	/* Keep a copy of escape flags */
315	flags = (unsigned char)(lflags & ESC_FLAGS);
316
317	type = str->type;
318
319	outlen = 0;
320
321
322	if(lflags & ASN1_STRFLGS_SHOW_TYPE) {
323		const char *tagname;
324		tagname = ASN1_tag2str(type);
325		outlen += strlen(tagname);
326		if(!io_ch(arg, tagname, outlen) || !io_ch(arg, ":", 1)) return -1;
327		outlen++;
328	}
329
330	/* Decide what to do with type, either dump content or display it */
331
332	/* Dump everything */
333	if(lflags & ASN1_STRFLGS_DUMP_ALL) type = -1;
334	/* Ignore the string type */
335	else if(lflags & ASN1_STRFLGS_IGNORE_TYPE) type = 1;
336	else {
337		/* Else determine width based on type */
338		if((type > 0) && (type < 31)) type = tag2nbyte[type];
339		else type = -1;
340		if((type == -1) && !(lflags & ASN1_STRFLGS_DUMP_UNKNOWN)) type = 1;
341	}
342
343	if(type == -1) {
344		len = do_dump(lflags, io_ch, arg, str);
345		if(len < 0) return -1;
346		outlen += len;
347		return outlen;
348	}
349
350	if(lflags & ASN1_STRFLGS_UTF8_CONVERT) {
351		/* Note: if string is UTF8 and we want
352		 * to convert to UTF8 then we just interpret
353		 * it as 1 byte per character to avoid converting
354		 * twice.
355		 */
356		if(!type) type = 1;
357		else type |= BUF_TYPE_CONVUTF8;
358	}
359
360	len = do_buf(str->data, str->length, type, flags, &quotes, io_ch, NULL);
361	if(len < 0) return -1;
362	outlen += len;
363	if(quotes) outlen += 2;
364	if(!arg) return outlen;
365	if(quotes && !io_ch(arg, "\"", 1)) return -1;
366	if(do_buf(str->data, str->length, type, flags, NULL, io_ch, arg) < 0)
367		return -1;
368	if(quotes && !io_ch(arg, "\"", 1)) return -1;
369	return outlen;
370}
371
372/* Used for line indenting: print 'indent' spaces */
373
374static int do_indent(char_io *io_ch, void *arg, int indent)
375{
376	int i;
377	for(i = 0; i < indent; i++)
378			if(!io_ch(arg, " ", 1)) return 0;
379	return 1;
380}
381
382#define FN_WIDTH_LN	25
383#define FN_WIDTH_SN	10
384
385static int do_name_ex(char_io *io_ch, void *arg, X509_NAME *n,
386				int indent, unsigned long flags)
387{
388	int i, prev = -1, orflags, cnt;
389	int fn_opt, fn_nid;
390	ASN1_OBJECT *fn;
391	ASN1_STRING *val;
392	X509_NAME_ENTRY *ent;
393	char objtmp[80];
394	const char *objbuf;
395	int outlen, len;
396	char *sep_dn, *sep_mv, *sep_eq;
397	int sep_dn_len, sep_mv_len, sep_eq_len;
398	if(indent < 0) indent = 0;
399	outlen = indent;
400	if(!do_indent(io_ch, arg, indent)) return -1;
401	switch (flags & XN_FLAG_SEP_MASK)
402	{
403		case XN_FLAG_SEP_MULTILINE:
404		sep_dn = "\n";
405		sep_dn_len = 1;
406		sep_mv = " + ";
407		sep_mv_len = 3;
408		break;
409
410		case XN_FLAG_SEP_COMMA_PLUS:
411		sep_dn = ",";
412		sep_dn_len = 1;
413		sep_mv = "+";
414		sep_mv_len = 1;
415		indent = 0;
416		break;
417
418		case XN_FLAG_SEP_CPLUS_SPC:
419		sep_dn = ", ";
420		sep_dn_len = 2;
421		sep_mv = " + ";
422		sep_mv_len = 3;
423		indent = 0;
424		break;
425
426		case XN_FLAG_SEP_SPLUS_SPC:
427		sep_dn = "; ";
428		sep_dn_len = 2;
429		sep_mv = " + ";
430		sep_mv_len = 3;
431		indent = 0;
432		break;
433
434		default:
435		return -1;
436	}
437
438	if(flags & XN_FLAG_SPC_EQ) {
439		sep_eq = " = ";
440		sep_eq_len = 3;
441	} else {
442		sep_eq = "=";
443		sep_eq_len = 1;
444	}
445
446	fn_opt = flags & XN_FLAG_FN_MASK;
447
448	cnt = X509_NAME_entry_count(n);
449	for(i = 0; i < cnt; i++) {
450		if(flags & XN_FLAG_DN_REV)
451				ent = X509_NAME_get_entry(n, cnt - i - 1);
452		else ent = X509_NAME_get_entry(n, i);
453		if(prev != -1) {
454			if(prev == ent->set) {
455				if(!io_ch(arg, sep_mv, sep_mv_len)) return -1;
456				outlen += sep_mv_len;
457			} else {
458				if(!io_ch(arg, sep_dn, sep_dn_len)) return -1;
459				outlen += sep_dn_len;
460				if(!do_indent(io_ch, arg, indent)) return -1;
461				outlen += indent;
462			}
463		}
464		prev = ent->set;
465		fn = X509_NAME_ENTRY_get_object(ent);
466		val = X509_NAME_ENTRY_get_data(ent);
467		fn_nid = OBJ_obj2nid(fn);
468		if(fn_opt != XN_FLAG_FN_NONE) {
469			int objlen, fld_len;
470			if((fn_opt == XN_FLAG_FN_OID) || (fn_nid==NID_undef) ) {
471				OBJ_obj2txt(objtmp, sizeof objtmp, fn, 1);
472				fld_len = 0; /* XXX: what should this be? */
473				objbuf = objtmp;
474			} else {
475				if(fn_opt == XN_FLAG_FN_SN) {
476					fld_len = FN_WIDTH_SN;
477					objbuf = OBJ_nid2sn(fn_nid);
478				} else if(fn_opt == XN_FLAG_FN_LN) {
479					fld_len = FN_WIDTH_LN;
480					objbuf = OBJ_nid2ln(fn_nid);
481				} else {
482					fld_len = 0; /* XXX: what should this be? */
483					objbuf = "";
484				}
485			}
486			objlen = strlen(objbuf);
487			if(!io_ch(arg, objbuf, objlen)) return -1;
488			if ((objlen < fld_len) && (flags & XN_FLAG_FN_ALIGN)) {
489				if (!do_indent(io_ch, arg, fld_len - objlen)) return -1;
490				outlen += fld_len - objlen;
491			}
492			if(!io_ch(arg, sep_eq, sep_eq_len)) return -1;
493			outlen += objlen + sep_eq_len;
494		}
495		/* If the field name is unknown then fix up the DER dump
496		 * flag. We might want to limit this further so it will
497 		 * DER dump on anything other than a few 'standard' fields.
498		 */
499		if((fn_nid == NID_undef) && (flags & XN_FLAG_DUMP_UNKNOWN_FIELDS))
500					orflags = ASN1_STRFLGS_DUMP_ALL;
501		else orflags = 0;
502
503		len = do_print_ex(io_ch, arg, flags | orflags, val);
504		if(len < 0) return -1;
505		outlen += len;
506	}
507	return outlen;
508}
509
510/* Wrappers round the main functions */
511
512int X509_NAME_print_ex(BIO *out, X509_NAME *nm, int indent, unsigned long flags)
513{
514	if(flags == XN_FLAG_COMPAT)
515		return X509_NAME_print(out, nm, indent);
516	return do_name_ex(send_bio_chars, out, nm, indent, flags);
517}
518
519#ifndef OPENSSL_NO_FP_API
520int X509_NAME_print_ex_fp(FILE *fp, X509_NAME *nm, int indent, unsigned long flags)
521{
522	if(flags == XN_FLAG_COMPAT)
523		{
524		BIO *btmp;
525		int ret;
526		btmp = BIO_new_fp(fp, BIO_NOCLOSE);
527		if(!btmp) return -1;
528		ret = X509_NAME_print(btmp, nm, indent);
529		BIO_free(btmp);
530		return ret;
531		}
532	return do_name_ex(send_fp_chars, fp, nm, indent, flags);
533}
534#endif
535
536int ASN1_STRING_print_ex(BIO *out, ASN1_STRING *str, unsigned long flags)
537{
538	return do_print_ex(send_bio_chars, out, flags, str);
539}
540
541#ifndef OPENSSL_NO_FP_API
542int ASN1_STRING_print_ex_fp(FILE *fp, ASN1_STRING *str, unsigned long flags)
543{
544	return do_print_ex(send_fp_chars, fp, flags, str);
545}
546#endif
547
548/* Utility function: convert any string type to UTF8, returns number of bytes
549 * in output string or a negative error code
550 */
551
552int ASN1_STRING_to_UTF8(unsigned char **out, ASN1_STRING *in)
553{
554	ASN1_STRING stmp, *str = &stmp;
555	int mbflag, type, ret;
556	if(!in) return -1;
557	type = in->type;
558	if((type < 0) || (type > 30)) return -1;
559	mbflag = tag2nbyte[type];
560	if(mbflag == -1) return -1;
561	mbflag |= MBSTRING_FLAG;
562	stmp.data = NULL;
563	ret = ASN1_mbstring_copy(&str, in->data, in->length, mbflag, B_ASN1_UTF8STRING);
564	if(ret < 0) return ret;
565	*out = stmp.data;
566	return stmp.length;
567}
568