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