1 /* 2 * Copyright 1995-2025 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 <stdlib.h> 12 #include <string.h> 13 #include "apps.h" 14 #include "progs.h" 15 #include <openssl/err.h> 16 #include <openssl/evp.h> 17 #include <openssl/x509.h> 18 #include <openssl/pem.h> 19 #include <openssl/asn1t.h> 20 21 typedef enum OPTION_choice { 22 OPT_COMMON, 23 OPT_INFORM, 24 OPT_IN, 25 OPT_OUT, 26 OPT_INDENT, 27 OPT_NOOUT, 28 OPT_OID, 29 OPT_OFFSET, 30 OPT_LENGTH, 31 OPT_DUMP, 32 OPT_DLIMIT, 33 OPT_STRPARSE, 34 OPT_GENSTR, 35 OPT_GENCONF, 36 OPT_STRICTPEM, 37 OPT_ITEM 38 } OPTION_CHOICE; 39 40 const OPTIONS asn1parse_options[] = { 41 OPT_SECTION("General"), 42 { "help", OPT_HELP, '-', "Display this summary" }, 43 { "oid", OPT_OID, '<', "file of extra oid definitions" }, 44 45 OPT_SECTION("I/O"), 46 { "inform", OPT_INFORM, 'A', "input format - one of DER PEM B64" }, 47 { "in", OPT_IN, '<', "input file" }, 48 { "out", OPT_OUT, '>', "output file (output format is always DER)" }, 49 { "noout", OPT_NOOUT, 0, "do not produce any output" }, 50 { "offset", OPT_OFFSET, 'p', "offset into file" }, 51 { "length", OPT_LENGTH, 'p', "length of section in file" }, 52 { "strparse", OPT_STRPARSE, 'p', 53 "offset; a series of these can be used to 'dig'" }, 54 { OPT_MORE_STR, 0, 0, "into multiple ASN1 blob wrappings" }, 55 { "genstr", OPT_GENSTR, 's', "string to generate ASN1 structure from" }, 56 { "genconf", OPT_GENCONF, 's', "file to generate ASN1 structure from" }, 57 { "strictpem", OPT_STRICTPEM, 0, 58 "equivalent to '-inform pem' (obsolete)" }, 59 { "item", OPT_ITEM, 's', "item to parse and print" }, 60 { OPT_MORE_STR, 0, 0, "(-inform will be ignored)" }, 61 62 OPT_SECTION("Formatting"), 63 { "i", OPT_INDENT, 0, "indents the output" }, 64 { "dump", OPT_DUMP, 0, "unknown data in hex form" }, 65 { "dlimit", OPT_DLIMIT, 'p', 66 "dump the first arg bytes of unknown data in hex form" }, 67 { NULL } 68 }; 69 70 static int do_generate(char *genstr, const char *genconf, BUF_MEM *buf); 71 72 int asn1parse_main(int argc, char **argv) 73 { 74 ASN1_TYPE *at = NULL; 75 BIO *in = NULL, *b64 = NULL, *derout = NULL; 76 BUF_MEM *buf = NULL; 77 STACK_OF(OPENSSL_STRING) *osk = NULL; 78 char *genstr = NULL, *genconf = NULL; 79 char *infile = NULL, *oidfile = NULL, *derfile = NULL; 80 unsigned char *str = NULL; 81 char *name = NULL, *header = NULL, *prog; 82 const unsigned char *ctmpbuf; 83 int indent = 0, noout = 0, dump = 0, informat = FORMAT_PEM; 84 int offset = 0, ret = 1, i, j; 85 long num, tmplen; 86 unsigned char *tmpbuf; 87 unsigned int length = 0; 88 OPTION_CHOICE o; 89 const ASN1_ITEM *it = NULL; 90 91 prog = opt_init(argc, argv, asn1parse_options); 92 93 if ((osk = sk_OPENSSL_STRING_new_null()) == NULL) { 94 BIO_printf(bio_err, "%s: Memory allocation failure\n", prog); 95 goto end; 96 } 97 98 while ((o = opt_next()) != OPT_EOF) { 99 switch (o) { 100 case OPT_EOF: 101 case OPT_ERR: 102 opthelp: 103 BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); 104 goto end; 105 case OPT_HELP: 106 opt_help(asn1parse_options); 107 ret = 0; 108 goto end; 109 case OPT_INFORM: 110 if (!opt_format(opt_arg(), OPT_FMT_ASN1, &informat)) 111 goto opthelp; 112 break; 113 case OPT_IN: 114 infile = opt_arg(); 115 break; 116 case OPT_OUT: 117 derfile = opt_arg(); 118 break; 119 case OPT_INDENT: 120 indent = 1; 121 break; 122 case OPT_NOOUT: 123 noout = 1; 124 break; 125 case OPT_OID: 126 oidfile = opt_arg(); 127 break; 128 case OPT_OFFSET: 129 offset = strtol(opt_arg(), NULL, 0); 130 break; 131 case OPT_LENGTH: 132 length = strtol(opt_arg(), NULL, 0); 133 break; 134 case OPT_DUMP: 135 dump = -1; 136 break; 137 case OPT_DLIMIT: 138 dump = strtol(opt_arg(), NULL, 0); 139 break; 140 case OPT_STRPARSE: 141 if (sk_OPENSSL_STRING_push(osk, opt_arg()) <= 0) 142 goto end; 143 break; 144 case OPT_GENSTR: 145 genstr = opt_arg(); 146 break; 147 case OPT_GENCONF: 148 genconf = opt_arg(); 149 break; 150 case OPT_STRICTPEM: 151 /* accepted for backward compatibility */ 152 informat = FORMAT_PEM; 153 break; 154 case OPT_ITEM: 155 it = ASN1_ITEM_lookup(opt_arg()); 156 if (it == NULL) { 157 size_t tmp; 158 159 BIO_printf(bio_err, "Unknown item name %s\n", opt_arg()); 160 BIO_puts(bio_err, "Supported types:\n"); 161 for (tmp = 0;; tmp++) { 162 it = ASN1_ITEM_get(tmp); 163 if (it == NULL) 164 break; 165 BIO_printf(bio_err, " %s\n", it->sname); 166 } 167 goto end; 168 } 169 break; 170 } 171 } 172 173 /* No extra args. */ 174 if (!opt_check_rest_arg(NULL)) 175 goto opthelp; 176 177 if (oidfile != NULL) { 178 in = bio_open_default(oidfile, 'r', FORMAT_TEXT); 179 if (in == NULL) 180 goto end; 181 OBJ_create_objects(in); 182 BIO_free(in); 183 } 184 185 if ((in = bio_open_default(infile, 'r', informat)) == NULL) 186 goto end; 187 188 if (derfile && (derout = bio_open_default(derfile, 'w', FORMAT_ASN1)) == NULL) 189 goto end; 190 191 if ((buf = BUF_MEM_new()) == NULL) 192 goto end; 193 if (genconf == NULL && genstr == NULL && informat == FORMAT_PEM) { 194 if (PEM_read_bio(in, &name, &header, &str, &num) != 1) { 195 BIO_printf(bio_err, "Error reading PEM file\n"); 196 ERR_print_errors(bio_err); 197 goto end; 198 } 199 buf->data = (char *)str; 200 buf->length = buf->max = num; 201 } else { 202 if (!BUF_MEM_grow(buf, BUFSIZ * 8)) 203 goto end; /* Pre-allocate :-) */ 204 205 if (genstr || genconf) { 206 num = do_generate(genstr, genconf, buf); 207 if (num < 0) { 208 ERR_print_errors(bio_err); 209 goto end; 210 } 211 } else { 212 213 if (informat == FORMAT_BASE64) { 214 BIO *tmp; 215 216 if ((b64 = BIO_new(BIO_f_base64())) == NULL) 217 goto end; 218 BIO_push(b64, in); 219 tmp = in; 220 in = b64; 221 b64 = tmp; 222 } 223 224 num = 0; 225 for (;;) { 226 if (!BUF_MEM_grow(buf, num + BUFSIZ)) 227 goto end; 228 i = BIO_read(in, &(buf->data[num]), BUFSIZ); 229 if (i <= 0) 230 break; 231 /* make sure num doesn't overflow */ 232 if (i > LONG_MAX - num) 233 goto end; 234 num += i; 235 } 236 } 237 str = (unsigned char *)buf->data; 238 } 239 240 /* If any structs to parse go through in sequence */ 241 242 if (sk_OPENSSL_STRING_num(osk)) { 243 tmpbuf = str; 244 tmplen = num; 245 for (i = 0; i < sk_OPENSSL_STRING_num(osk); i++) { 246 ASN1_TYPE *atmp; 247 int typ; 248 j = strtol(sk_OPENSSL_STRING_value(osk, i), NULL, 0); 249 if (j <= 0 || j >= tmplen) { 250 BIO_printf(bio_err, "'%s' is out of range\n", 251 sk_OPENSSL_STRING_value(osk, i)); 252 continue; 253 } 254 tmpbuf += j; 255 tmplen -= j; 256 atmp = at; 257 ctmpbuf = tmpbuf; 258 at = d2i_ASN1_TYPE(NULL, &ctmpbuf, tmplen); 259 ASN1_TYPE_free(atmp); 260 if (!at) { 261 BIO_printf(bio_err, "Error parsing structure\n"); 262 ERR_print_errors(bio_err); 263 goto end; 264 } 265 typ = ASN1_TYPE_get(at); 266 if ((typ == V_ASN1_OBJECT) 267 || (typ == V_ASN1_BOOLEAN) 268 || (typ == V_ASN1_NULL)) { 269 BIO_printf(bio_err, "Can't parse %s type\n", ASN1_tag2str(typ)); 270 ERR_print_errors(bio_err); 271 goto end; 272 } 273 /* hmm... this is a little evil but it works */ 274 tmpbuf = at->value.asn1_string->data; 275 tmplen = at->value.asn1_string->length; 276 } 277 str = tmpbuf; 278 num = tmplen; 279 } 280 281 if (offset < 0 || offset >= num) { 282 BIO_printf(bio_err, "Error: offset out of range\n"); 283 goto end; 284 } 285 286 num -= offset; 287 288 if (length == 0 || length > (unsigned int)num) 289 length = (unsigned int)num; 290 if (derout != NULL) { 291 if (BIO_write(derout, str + offset, length) != (int)length) { 292 BIO_printf(bio_err, "Error writing output\n"); 293 ERR_print_errors(bio_err); 294 goto end; 295 } 296 } 297 if (!noout) { 298 const unsigned char *p = str + offset; 299 300 if (it != NULL) { 301 ASN1_VALUE *value = ASN1_item_d2i(NULL, &p, length, it); 302 if (value == NULL) { 303 BIO_printf(bio_err, "Error parsing item %s\n", it->sname); 304 ERR_print_errors(bio_err); 305 goto end; 306 } 307 ASN1_item_print(bio_out, value, 0, it, NULL); 308 ASN1_item_free(value, it); 309 } else { 310 if (!ASN1_parse_dump(bio_out, p, length, indent, dump)) { 311 ERR_print_errors(bio_err); 312 goto end; 313 } 314 } 315 } 316 ret = 0; 317 end: 318 BIO_free(derout); 319 BIO_free(in); 320 BIO_free(b64); 321 if (ret != 0) 322 ERR_print_errors(bio_err); 323 BUF_MEM_free(buf); 324 OPENSSL_free(name); 325 OPENSSL_free(header); 326 ASN1_TYPE_free(at); 327 sk_OPENSSL_STRING_free(osk); 328 return ret; 329 } 330 331 static int do_generate(char *genstr, const char *genconf, BUF_MEM *buf) 332 { 333 CONF *cnf = NULL; 334 int len; 335 unsigned char *p; 336 ASN1_TYPE *atyp = NULL; 337 338 if (genconf != NULL) { 339 if ((cnf = app_load_config(genconf)) == NULL) 340 goto err; 341 if (genstr == NULL) 342 genstr = NCONF_get_string(cnf, "default", "asn1"); 343 if (genstr == NULL) { 344 BIO_printf(bio_err, "Can't find 'asn1' in '%s'\n", genconf); 345 goto err; 346 } 347 } 348 349 atyp = ASN1_generate_nconf(genstr, cnf); 350 NCONF_free(cnf); 351 cnf = NULL; 352 353 if (atyp == NULL) 354 return -1; 355 356 len = i2d_ASN1_TYPE(atyp, NULL); 357 358 if (len <= 0) 359 goto err; 360 361 if (!BUF_MEM_grow(buf, len)) 362 goto err; 363 364 p = (unsigned char *)buf->data; 365 366 i2d_ASN1_TYPE(atyp, &p); 367 368 ASN1_TYPE_free(atyp); 369 return len; 370 371 err: 372 NCONF_free(cnf); 373 ASN1_TYPE_free(atyp); 374 return -1; 375 } 376