283 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			283 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From 22ecb8a2c00db7a53508d2106957e0c112a5a020 Mon Sep 17 00:00:00 2001
 | |
| From: Petr Gotthard <petr.gotthard@centrum.cz>
 | |
| Date: Tue, 31 Aug 2021 12:27:35 +0200
 | |
| Subject: [PATCH 17/17] Generate partial X.509 certificate using own struct
 | |
| 
 | |
| The tpm2_certifyX509certutil.c:generate_partial_X509() creates a partial
 | |
| (in fact, an incomplete) certificate, see Part 3, 18.8.1. The OpenSSL
 | |
| team decided that starting from 3.0 the i2d should not create invalid
 | |
| encodings, so we need to create an own ASN.1 structure for this.
 | |
| 
 | |
| The structure is adapted from
 | |
| https://github.com/openssl/openssl/issues/16257#issuecomment-895448370.
 | |
| 
 | |
| Fixes: #2813
 | |
| 
 | |
| Signed-off-by: Petr Gotthard <petr.gotthard@centrum.cz>
 | |
| ---
 | |
|  tools/misc/tpm2_certifyX509certutil.c | 179 +++++++++-----------------
 | |
|  1 file changed, 59 insertions(+), 120 deletions(-)
 | |
| 
 | |
| diff --git a/tools/misc/tpm2_certifyX509certutil.c b/tools/misc/tpm2_certifyX509certutil.c
 | |
| index 62ed644a..5eea08e8 100644
 | |
| --- a/tools/misc/tpm2_certifyX509certutil.c
 | |
| +++ b/tools/misc/tpm2_certifyX509certutil.c
 | |
| @@ -8,6 +8,8 @@
 | |
|  #include <sys/stat.h>
 | |
|  #include <unistd.h>
 | |
|  
 | |
| +#include <openssl/asn1.h>
 | |
| +#include <openssl/asn1t.h>
 | |
|  #include <openssl/x509.h>
 | |
|  #include <openssl/x509v3.h>
 | |
|  #include <openssl/bio.h>
 | |
| @@ -15,6 +17,46 @@
 | |
|  #include "log.h"
 | |
|  #include "tpm2_tool.h"
 | |
|  
 | |
| +typedef struct {
 | |
| +    ASN1_TIME *notBefore;
 | |
| +    ASN1_TIME *notAfter;
 | |
| +} TPM2_PARTIAL_CERT_VALIDITY;
 | |
| +
 | |
| +typedef struct {
 | |
| +    X509_ALGOR *algorithm;
 | |
| +    X509_NAME *issuer;
 | |
| +    TPM2_PARTIAL_CERT_VALIDITY *validity;
 | |
| +    X509_NAME *subject;
 | |
| +    STACK_OF(X509_EXTENSION) *extensions;
 | |
| +} TPM2_PARTIAL_CERT;
 | |
| +
 | |
| +ASN1_SEQUENCE(TPM2_PARTIAL_CERT_VALIDITY) = {
 | |
| +    ASN1_SIMPLE(TPM2_PARTIAL_CERT_VALIDITY, notBefore, ASN1_TIME),
 | |
| +    ASN1_SIMPLE(TPM2_PARTIAL_CERT_VALIDITY, notAfter, ASN1_TIME),
 | |
| +} ASN1_SEQUENCE_END(TPM2_PARTIAL_CERT_VALIDITY)
 | |
| +
 | |
| +/* partialCertificate per Part 3, 18.8.1 */
 | |
| +ASN1_SEQUENCE(TPM2_PARTIAL_CERT) = {
 | |
| +    ASN1_OPT(TPM2_PARTIAL_CERT, algorithm, X509_ALGOR),
 | |
| +    ASN1_SIMPLE(TPM2_PARTIAL_CERT, issuer, X509_NAME),
 | |
| +    ASN1_SIMPLE(TPM2_PARTIAL_CERT, validity, TPM2_PARTIAL_CERT_VALIDITY),
 | |
| +    ASN1_SIMPLE(TPM2_PARTIAL_CERT, subject, X509_NAME),
 | |
| +    ASN1_EXP_SEQUENCE_OF(TPM2_PARTIAL_CERT, extensions, X509_EXTENSION, 3),
 | |
| +} ASN1_SEQUENCE_END(TPM2_PARTIAL_CERT)
 | |
| +
 | |
| +IMPLEMENT_ASN1_FUNCTIONS(TPM2_PARTIAL_CERT)
 | |
| +
 | |
| +int i2d_TPM2_PARTIAL_CERT_bio(BIO *bp, const TPM2_PARTIAL_CERT *a)
 | |
| +{
 | |
| +    return ASN1_i2d_bio_of(TPM2_PARTIAL_CERT, i2d_TPM2_PARTIAL_CERT, bp, a);
 | |
| +}
 | |
| +
 | |
| +int TPM2_add_ext(TPM2_PARTIAL_CERT *x, X509_EXTENSION *ex, int loc)
 | |
| +{
 | |
| +    return (X509v3_add_ext(&(x->extensions), ex, loc) != NULL);
 | |
| +}
 | |
| +
 | |
| +
 | |
|  struct tpm_gen_partial_cert {
 | |
|      const char *out_path;
 | |
|      const char *valid_str;
 | |
| @@ -95,80 +137,6 @@ static struct name_fields names[] = {
 | |
|        .def = "CA Unit" },
 | |
|  };
 | |
|  
 | |
| -static tool_rc fixup_cert(const char *cert) {
 | |
| -
 | |
| -    int fd = open(cert, O_RDONLY);
 | |
| -    if (fd < 0) {
 | |
| -        LOG_ERR("open failed");
 | |
| -        return tool_rc_general_error;
 | |
| -    }
 | |
| -
 | |
| -    struct stat fs;
 | |
| -    int ret = fstat(fd, &fs);
 | |
| -    if (ret < 0) {
 | |
| -        close(fd);
 | |
| -        return tool_rc_general_error;
 | |
| -    }
 | |
| -
 | |
| -    ssize_t size = fs.st_size;
 | |
| -    if (size < 100 || size > 255) {
 | |
| -        LOG_ERR("Wrong cert size %zd", size);
 | |
| -        close(fd);
 | |
| -        return tool_rc_general_error; /* there is something wrong with this cert */
 | |
| -    }
 | |
| -
 | |
| -    char* buf = calloc(1, size);
 | |
| -    if (!buf) {
 | |
| -        LOG_ERR("Alloc failed");
 | |
| -        close(fd);
 | |
| -        return tool_rc_general_error;
 | |
| -    }
 | |
| -
 | |
| -    tool_rc rc = tool_rc_success;
 | |
| -    ret = read(fd, buf, size);
 | |
| -    close(fd);
 | |
| -    if (ret != size) {
 | |
| -        LOG_ERR("read failed");
 | |
| -        rc = tool_rc_general_error;
 | |
| -        goto out;
 | |
| -    }
 | |
| -
 | |
| -    fd = open(cert, O_WRONLY | O_TRUNC);
 | |
| -    if (fd < 0) {
 | |
| -        LOG_ERR("second open failed");
 | |
| -        rc = tool_rc_general_error;
 | |
| -        goto out;
 | |
| -    }
 | |
| -
 | |
| -    /* We need to skip one wrapping sequence (8 bytes) and one
 | |
| -     * sequence with one empty byte field at the end (5 bytes).
 | |
| -     * Fix the size here */
 | |
| -    buf[2] = size - 16;
 | |
| -
 | |
| -    /* Write the external sequence with the fixed size */
 | |
| -    ret = write(fd, buf, 3);
 | |
| -    if (ret != 3) {
 | |
| -        LOG_ERR("write failed");
 | |
| -        rc = tool_rc_general_error;
 | |
| -        close(fd);
 | |
| -        goto out;
 | |
| -    }
 | |
| -
 | |
| -    /* skip the wrapping sequence the write the rest
 | |
| -     * without the 5 bytes at the end */
 | |
| -    ret = write(fd, buf + 11, size - 16);
 | |
| -    close(fd);
 | |
| -    if (ret != size - 16) {
 | |
| -        LOG_ERR("second write failed");
 | |
| -        rc = tool_rc_general_error;
 | |
| -    }
 | |
| -
 | |
| -out:
 | |
| -    free(buf);
 | |
| -
 | |
| -    return rc;
 | |
| -}
 | |
| -
 | |
|  static int populate_fields(X509_NAME *name, const char *opt) {
 | |
|  
 | |
|      char *name_opt = strdup(opt);
 | |
| @@ -228,19 +196,14 @@ static tool_rc generate_partial_X509() {
 | |
|      }
 | |
|  
 | |
|      X509_EXTENSION *extv3 = NULL;
 | |
| -    X509 *cert = X509_new();
 | |
| +    TPM2_PARTIAL_CERT *cert = TPM2_PARTIAL_CERT_new();
 | |
|      if (!cert) {
 | |
| -        LOG_ERR("X509_new");
 | |
| -        goto out_err;
 | |
| -    }
 | |
| -
 | |
| -    X509_NAME *issuer = X509_get_issuer_name(cert);
 | |
| -    if (!issuer) {
 | |
| -        LOG_ERR("X509_get_issuer_name");
 | |
| +        LOG_ERR("TPM2_PARTIAL_CERT_new");
 | |
|          goto out_err;
 | |
|      }
 | |
|  
 | |
| -    int fields_added = populate_fields(issuer, ctx.issuer);
 | |
| +    /* populate issuer */
 | |
| +    int fields_added = populate_fields(cert->issuer, ctx.issuer);
 | |
|      if (fields_added <= 0) {
 | |
|          LOG_ERR("Could not parse any issuer fields");
 | |
|          goto out_err;
 | |
| @@ -248,28 +211,18 @@ static tool_rc generate_partial_X509() {
 | |
|          LOG_INFO("Added %d issuer fields", fields_added);
 | |
|      }
 | |
|  
 | |
| -    int ret = X509_set_issuer_name(cert, issuer); // add issuer
 | |
| -    if (ret != 1) {
 | |
| -        LOG_ERR("X509_set_issuer_name");
 | |
| -        goto out_err;
 | |
| -    }
 | |
| -
 | |
| +    /* populate validity */
 | |
|      unsigned int valid_days;
 | |
|      if (!tpm2_util_string_to_uint32(ctx.valid_str, &valid_days)) {
 | |
|          LOG_ERR("string_to_uint32");
 | |
|          goto out_err;
 | |
|      }
 | |
|  
 | |
| -    X509_gmtime_adj(X509_getm_notBefore(cert), 0); // add valid not before
 | |
| -    X509_gmtime_adj(X509_getm_notAfter(cert), valid_days * 86400); // add valid not after
 | |
| -
 | |
| -    X509_NAME *subject = X509_get_subject_name(cert);
 | |
| -    if (!subject) {
 | |
| -        LOG_ERR("X509_get_subject_name");
 | |
| -        goto out_err;
 | |
| -    }
 | |
| +    X509_gmtime_adj(cert->validity->notBefore, 0); // add valid not before
 | |
| +    X509_gmtime_adj(cert->validity->notAfter, valid_days * 86400); // add valid not after
 | |
|  
 | |
| -    fields_added = populate_fields(subject, ctx.subject);
 | |
| +    /* populate subject */
 | |
| +    fields_added = populate_fields(cert->subject, ctx.subject);
 | |
|      if (fields_added <= 0) {
 | |
|          LOG_ERR("Could not parse any subject fields");
 | |
|          goto out_err;
 | |
| @@ -277,51 +230,37 @@ static tool_rc generate_partial_X509() {
 | |
|          LOG_INFO("Added %d subject fields", fields_added);
 | |
|      }
 | |
|  
 | |
| -    ret = X509_set_subject_name(cert, subject);  // add subject
 | |
| -    if (ret != 1) {
 | |
| -        LOG_ERR("X509_NAME_add_entry_by_txt");
 | |
| -        goto out_err;
 | |
| -    }
 | |
| -
 | |
| +    /* populate extensions */
 | |
|      extv3 = X509V3_EXT_conf_nid(NULL, NULL, NID_key_usage,
 | |
| -    "critical,digitalSignature,keyCertSign,cRLSign");
 | |
| +                "critical,digitalSignature,keyCertSign,cRLSign");
 | |
|      if (!extv3) {
 | |
|          LOG_ERR("X509V3_EXT_conf_nid");
 | |
|          goto out_err;
 | |
|      }
 | |
|  
 | |
| -    ret = X509_add_ext(cert, extv3, -1); // add required v3 extention: key usage
 | |
| +    int ret = TPM2_add_ext(cert, extv3, -1); // add required v3 extention: key usage
 | |
|      if (ret != 1) {
 | |
|          LOG_ERR("X509_add_ext");
 | |
|          goto out_err;
 | |
|      }
 | |
|  
 | |
| -    ret = i2d_X509_bio(cert_out, cert); // print cert in DER format
 | |
| +    /* output */
 | |
| +    ret = i2d_TPM2_PARTIAL_CERT_bio(cert_out, cert); // print cert in DER format
 | |
|      if (ret != 1) {
 | |
|          LOG_ERR("i2d_X509_bio");
 | |
|          goto out_err;
 | |
|      }
 | |
|  
 | |
|      X509_EXTENSION_free(extv3);
 | |
| -    X509_free(cert);
 | |
| +    TPM2_PARTIAL_CERT_free(cert);
 | |
|      BIO_free_all(cert_out);
 | |
|  
 | |
| -    ret = fixup_cert(ctx.out_path);
 | |
| -    if (ret) {
 | |
| -        LOG_ERR("fixup_cert");
 | |
| -        return tool_rc_general_error;
 | |
| -    }
 | |
| -
 | |
|      return tool_rc_success;
 | |
|  
 | |
|  out_err:
 | |
|      BIO_free_all(cert_out);
 | |
| -    if (cert) {
 | |
| -        X509_free(cert);
 | |
| -    }
 | |
| -    if (extv3) {
 | |
| -        X509_EXTENSION_free(extv3);
 | |
| -    }
 | |
| +    X509_EXTENSION_free(extv3);
 | |
| +    TPM2_PARTIAL_CERT_free(cert);
 | |
|  
 | |
|      return tool_rc_general_error;
 | |
|  }
 | |
| -- 
 | |
| 2.31.1
 | |
| 
 |