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