diff options
| author | tb <> | 2026-08-30 16:52:07 +0000 |
|---|---|---|
| committer | tb <> | 2026-08-30 16:52:07 +0000 |
| commit | f1d663df044f339b954b1cc4da10dceb9675130e (patch) | |
| tree | e7192542c7c13523b2585c40d9c1c6d40ce83694 | |
| parent | 243bc4037954a2ed4a91d6ec8f2c796c1c5da943 (diff) | |
| download | openbsd-f1d663df044f339b954b1cc4da10dceb9675130e.tar.gz openbsd-f1d663df044f339b954b1cc4da10dceb9675130e.tar.bz2 openbsd-f1d663df044f339b954b1cc4da10dceb9675130e.zip | |
"Stream" valid PKCS7 objects with omitted content
The PKCS#7 standard marks the content element of the ContentInfo OPTIONAL.
Accordingly, a PKCS#7 object only containing a Content Type OID is valid:
SEQUENCE {
OBJECT_IDENTIFIER { 1.2.840.113549.1.7.4 }
}
Deserializing such an object works and therefore streaming should at least
have the decency of not segfaulting. Of course there's nothing decent about
PKCS#7 be it the standard or its OpenSSL "implementation".
Exercises a problem reported by Acts1361 and currently crashes.
To be fixed in pk7_lib.c r1.32.
| -rw-r--r-- | src/regress/lib/libcrypto/pkcs7/pkcs7test.c | 108 |
1 files changed, 104 insertions, 4 deletions
diff --git a/src/regress/lib/libcrypto/pkcs7/pkcs7test.c b/src/regress/lib/libcrypto/pkcs7/pkcs7test.c index 88126ea082..ae7c41299b 100644 --- a/src/regress/lib/libcrypto/pkcs7/pkcs7test.c +++ b/src/regress/lib/libcrypto/pkcs7/pkcs7test.c | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | /* $OpenBSD: pkcs7test.c,v 1.6 2026/04/21 05:18:35 tb Exp $ */ | 1 | /* $OpenBSD: pkcs7test.c,v 1.7 2026/08/30 16:52:07 tb Exp $ */ |
| 2 | /* | 2 | /* |
| 3 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> | 3 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> |
| 4 | * Copyright (c) 2026 Theo Buehler <tb@openbsd.org> | ||
| 4 | * | 5 | * |
| 5 | * Permission to use, copy, modify, and distribute this software for any | 6 | * Permission to use, copy, modify, and distribute this software for any |
| 6 | * purpose with or without fee is hereby granted, provided that the above | 7 | * purpose with or without fee is hereby granted, provided that the above |
| @@ -16,13 +17,16 @@ | |||
| 16 | */ | 17 | */ |
| 17 | 18 | ||
| 18 | #include <err.h> | 19 | #include <err.h> |
| 20 | #include <stdarg.h> | ||
| 21 | #include <stdio.h> | ||
| 19 | #include <stdlib.h> | 22 | #include <stdlib.h> |
| 20 | #include <string.h> | 23 | #include <string.h> |
| 21 | #include <unistd.h> | ||
| 22 | 24 | ||
| 25 | #include <openssl/asn1.h> | ||
| 23 | #include <openssl/bio.h> | 26 | #include <openssl/bio.h> |
| 24 | #include <openssl/err.h> | 27 | #include <openssl/err.h> |
| 25 | #include <openssl/evp.h> | 28 | #include <openssl/evp.h> |
| 29 | #include <openssl/objects.h> | ||
| 26 | #include <openssl/pem.h> | 30 | #include <openssl/pem.h> |
| 27 | #include <openssl/pkcs7.h> | 31 | #include <openssl/pkcs7.h> |
| 28 | #include <openssl/x509.h> | 32 | #include <openssl/x509.h> |
| @@ -100,9 +104,13 @@ x509_store_callback(int ok, X509_STORE_CTX *ctx) | |||
| 100 | } | 104 | } |
| 101 | 105 | ||
| 102 | static void | 106 | static void |
| 103 | fatal(const char *msg) | 107 | fatal(const char *msg, ...) |
| 104 | { | 108 | { |
| 105 | warnx("%s", msg); | 109 | va_list ap; |
| 110 | |||
| 111 | va_start(ap, msg); | ||
| 112 | vwarnx("%s", ap); | ||
| 113 | va_end(ap); | ||
| 106 | ERR_print_errors_fp(stderr); | 114 | ERR_print_errors_fp(stderr); |
| 107 | exit(1); | 115 | exit(1); |
| 108 | } | 116 | } |
| @@ -300,12 +308,104 @@ pkcs7_basics(void) | |||
| 300 | return 0; | 308 | return 0; |
| 301 | } | 309 | } |
| 302 | 310 | ||
| 311 | static int | ||
| 312 | pkcs7_stream_missing_content_nid(int nid) | ||
| 313 | { | ||
| 314 | PKCS7 *p7 = NULL; | ||
| 315 | const unsigned char *p, *name; | ||
| 316 | unsigned char **boundary = NULL; | ||
| 317 | unsigned char *der = NULL; | ||
| 318 | int der_len = 0; | ||
| 319 | int ret; | ||
| 320 | int failed = 1; | ||
| 321 | |||
| 322 | name = OBJ_nid2sn(nid); | ||
| 323 | |||
| 324 | /* | ||
| 325 | * Create PKCS7 object with Content Type corresponding to nid | ||
| 326 | * and omit the optional content. | ||
| 327 | */ | ||
| 328 | |||
| 329 | if ((p7 = PKCS7_new()) == NULL) | ||
| 330 | fatal("PKCS7_new NID %d (%s)", nid, name); | ||
| 331 | ASN1_OBJECT_free(p7->type); | ||
| 332 | if ((p7->type = OBJ_nid2obj(nid)) == NULL) | ||
| 333 | fatal("OBJ_nid2obj NID %d (%s)", nid, name); | ||
| 334 | |||
| 335 | /* | ||
| 336 | * Round trip this through DER. | ||
| 337 | */ | ||
| 338 | |||
| 339 | if ((der_len = i2d_PKCS7(p7, &der)) <= 0) | ||
| 340 | fatal("i2d_PKCS7 NID %d (%s)", nid, name); | ||
| 341 | |||
| 342 | PKCS7_free(p7); | ||
| 343 | p7 = NULL; | ||
| 344 | |||
| 345 | p = der; | ||
| 346 | if ((p7 = d2i_PKCS7(NULL, &p, der_len)) == NULL) | ||
| 347 | fatal("d2i_PKCS7 NID %d (%s)", nid, name); | ||
| 348 | |||
| 349 | /* | ||
| 350 | * It deserialized, so we can safely stream it, right? | ||
| 351 | */ | ||
| 352 | |||
| 353 | if ((ret = PKCS7_stream(&boundary, p7)) != 0) { | ||
| 354 | fprintf(stderr, "FAILURE: PKCS7_stream for NID %d (%s) " | ||
| 355 | "want 0, got %d\n", nid, name, ret); | ||
| 356 | goto out; | ||
| 357 | } | ||
| 358 | |||
| 359 | failed = 0; | ||
| 360 | |||
| 361 | out: | ||
| 362 | PKCS7_free(p7); | ||
| 363 | freezero(der, der_len); | ||
| 364 | |||
| 365 | return failed; | ||
| 366 | } | ||
| 367 | |||
| 368 | /* | ||
| 369 | * For each Content Type OID (RFC 2315, section 14), create a PKCS7 object that | ||
| 370 | * d2i_PKCS7() accepts. For x in [1..6] we use an object that encodes to | ||
| 371 | * | ||
| 372 | * SEQUENCE { | ||
| 373 | * OBJECT_IDENTIFIER { 1.2.840.113549.1.7.x } | ||
| 374 | * } | ||
| 375 | * | ||
| 376 | * This works because RFC 2315 section 7 marks the content optional: | ||
| 377 | * | ||
| 378 | * ContentInfo ::= SEQUENCE { | ||
| 379 | * contentType ContentType, | ||
| 380 | * content | ||
| 381 | * [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL } | ||
| 382 | * | ||
| 383 | * reflected in the ASN1_TFLG_OPTIONAL in pk7_asn1.c's p7default_tt. | ||
| 384 | */ | ||
| 385 | |||
| 386 | static int | ||
| 387 | pkcs7_stream_missing_content(void) | ||
| 388 | { | ||
| 389 | int failed = 0; | ||
| 390 | |||
| 391 | /* NID naming consistency is king. */ | ||
| 392 | failed |= pkcs7_stream_missing_content_nid(NID_pkcs7_data); | ||
| 393 | failed |= pkcs7_stream_missing_content_nid(NID_pkcs7_signed); | ||
| 394 | failed |= pkcs7_stream_missing_content_nid(NID_pkcs7_enveloped); | ||
| 395 | failed |= pkcs7_stream_missing_content_nid(NID_pkcs7_signedAndEnveloped); | ||
| 396 | failed |= pkcs7_stream_missing_content_nid(NID_pkcs7_digest); | ||
| 397 | failed |= pkcs7_stream_missing_content_nid(NID_pkcs7_encrypted); | ||
| 398 | |||
| 399 | return failed; | ||
| 400 | } | ||
| 401 | |||
| 303 | int | 402 | int |
| 304 | main(int argc, char **argv) | 403 | main(int argc, char **argv) |
| 305 | { | 404 | { |
| 306 | int failed = 0; | 405 | int failed = 0; |
| 307 | 406 | ||
| 308 | failed |= pkcs7_basics(); | 407 | failed |= pkcs7_basics(); |
| 408 | failed |= pkcs7_stream_missing_content(); | ||
| 309 | 409 | ||
| 310 | return failed; | 410 | return failed; |
| 311 | } | 411 | } |
