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