diff --git a/CHANGES b/CHANGES index a03aa4bbe33328795c9429be9b73e569d6de7919..5f84cfd403c3d730f0ab578155020f3d995e013a 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,10 @@ Changes between 1.0.x and 1.1.0 [xx XXX xxxx] + *) New function X509_CRL_diff to generate a delta CRL from the difference + of two full CRLs. Add support to "crl" utility. + [Steve Henson] + *) New options -CRL and -CRLform for s_client and s_server for CRLs. [Steve Henson] diff --git a/apps/crl.c b/apps/crl.c index 8ee88af46cf14dcb71f722ff5e6e7e10dbdf87bf..745469d1b8084544b1e4d26f68545dd7da0cfde7 100644 --- a/apps/crl.c +++ b/apps/crl.c @@ -104,8 +104,8 @@ int MAIN(int argc, char **argv) char *CAfile = NULL, *CApath = NULL; int ret=1,i,num,badops=0,badsig=0; BIO *out=NULL; - int informat,outformat; - char *infile=NULL,*outfile=NULL; + int informat,outformat, keyformat; + char *infile=NULL,*outfile=NULL, *crldiff = NULL, *keyfile = NULL; int hash=0,issuer=0,lastupdate=0,nextupdate=0,noout=0,text=0; int fingerprint = 0, crlnumber = 0; const char **pp; @@ -140,6 +140,7 @@ int MAIN(int argc, char **argv) informat=FORMAT_PEM; outformat=FORMAT_PEM; + keyformat=FORMAT_PEM; argc--; argv++; @@ -168,6 +169,21 @@ int MAIN(int argc, char **argv) if (--argc < 1) goto bad; infile= *(++argv); } + else if (strcmp(*argv,"-gendelta") == 0) + { + if (--argc < 1) goto bad; + crldiff= *(++argv); + } + else if (strcmp(*argv,"-key") == 0) + { + if (--argc < 1) goto bad; + keyfile= *(++argv); + } + else if (strcmp(*argv,"-keyform") == 0) + { + if (--argc < 1) goto bad; + keyformat=str2fmt(*(++argv)); + } else if (strcmp(*argv,"-out") == 0) { if (--argc < 1) goto bad; @@ -277,6 +293,39 @@ bad: else BIO_printf(bio_err, "verify OK\n"); } + if (crldiff) + { + X509_CRL *newcrl, *delta; + if (!keyfile) + { + BIO_puts(bio_err, "Missing CRL signing key\n"); + goto end; + } + newcrl = load_crl(crldiff,informat); + if (!newcrl) + goto end; + pkey = load_key(bio_err, keyfile, keyformat, 0, NULL, NULL, + "CRL signing key"); + if (!pkey) + { + X509_CRL_free(newcrl); + goto end; + } + delta = X509_CRL_diff(x, newcrl, pkey, digest, 0); + X509_CRL_free(newcrl); + EVP_PKEY_free(pkey); + if (delta) + { + X509_CRL_free(x); + x = delta; + } + else + { + BIO_puts(bio_err, "Error creating delta CRL\n"); + goto end; + } + } + if (num) { for (i=1; i<=num; i++) @@ -394,6 +443,8 @@ bad: if (!i) { BIO_printf(bio_err,"unable to write CRL\n"); goto end; } ret=0; end: + if (ret != 0) + ERR_print_errors(bio_err); BIO_free_all(out); BIO_free_all(bio_out); bio_out=NULL; diff --git a/crypto/asn1/x_crl.c b/crypto/asn1/x_crl.c index 137aa2180f61b14158904c7434aadf4eeca4d8b6..a4dfee8473880123c0c38b7b61b4386ae586fb63 100644 --- a/crypto/asn1/x_crl.c +++ b/crypto/asn1/x_crl.c @@ -356,6 +356,7 @@ ASN1_SEQUENCE_ref(X509_CRL, crl_cb, CRYPTO_LOCK_X509_CRL) = { } ASN1_SEQUENCE_END_ref(X509_CRL, X509_CRL) IMPLEMENT_ASN1_FUNCTIONS(X509_REVOKED) +IMPLEMENT_ASN1_DUP_FUNCTION(X509_REVOKED) IMPLEMENT_ASN1_FUNCTIONS(X509_CRL_INFO) IMPLEMENT_ASN1_FUNCTIONS(X509_CRL) IMPLEMENT_ASN1_DUP_FUNCTION(X509_CRL) diff --git a/crypto/x509/x509.h b/crypto/x509/x509.h index c913e3c39d2862bea4713aa41022d1217c907eec..ee560d19bf587debf1f8643a43b43aa142a0dff5 100644 --- a/crypto/x509/x509.h +++ b/crypto/x509/x509.h @@ -765,6 +765,7 @@ X509 *X509_dup(X509 *x509); X509_ATTRIBUTE *X509_ATTRIBUTE_dup(X509_ATTRIBUTE *xa); X509_EXTENSION *X509_EXTENSION_dup(X509_EXTENSION *ex); X509_CRL *X509_CRL_dup(X509_CRL *crl); +X509_REVOKED *X509_REVOKED_dup(X509_REVOKED *rev); X509_REQ *X509_REQ_dup(X509_REQ *req); X509_ALGOR *X509_ALGOR_dup(X509_ALGOR *xn); int X509_ALGOR_set0(X509_ALGOR *alg, ASN1_OBJECT *aobj, int ptype, void *pval); @@ -965,6 +966,9 @@ int X509_CRL_sort(X509_CRL *crl); int X509_REVOKED_set_serialNumber(X509_REVOKED *x, ASN1_INTEGER *serial); int X509_REVOKED_set_revocationDate(X509_REVOKED *r, ASN1_TIME *tm); +X509_CRL *X509_CRL_diff(X509_CRL *base, X509_CRL *newer, + EVP_PKEY *skey, const EVP_MD *md, unsigned int flags); + int X509_REQ_check_private_key(X509_REQ *x509,EVP_PKEY *pkey); int X509_check_private_key(X509 *x509,EVP_PKEY *pkey); @@ -1245,6 +1249,7 @@ void ERR_load_X509_strings(void); #define X509_F_X509_ATTRIBUTE_GET0_DATA 139 #define X509_F_X509_ATTRIBUTE_SET1_DATA 138 #define X509_F_X509_CHECK_PRIVATE_KEY 128 +#define X509_F_X509_CRL_DIFF 105 #define X509_F_X509_CRL_PRINT_FP 147 #define X509_F_X509_EXTENSION_CREATE_BY_NID 108 #define X509_F_X509_EXTENSION_CREATE_BY_OBJ 109 @@ -1277,20 +1282,27 @@ void ERR_load_X509_strings(void); #define X509_F_X509_VERIFY_CERT 127 /* Reason codes. */ +#define X509_R_AKID_MISMATCH 110 #define X509_R_BAD_X509_FILETYPE 100 #define X509_R_BASE64_DECODE_ERROR 118 #define X509_R_CANT_CHECK_DH_KEY 114 #define X509_R_CERT_ALREADY_IN_HASH_TABLE 101 +#define X509_R_CRL_ALREADY_DELTA 127 +#define X509_R_CRL_VERIFY_FAILURE 131 #define X509_R_ERR_ASN1_LIB 102 +#define X509_R_IDP_MISMATCH 128 #define X509_R_INVALID_DIRECTORY 113 #define X509_R_INVALID_FIELD_NAME 119 #define X509_R_INVALID_TRUST 123 +#define X509_R_ISSUER_MISMATCH 129 #define X509_R_KEY_TYPE_MISMATCH 115 #define X509_R_KEY_VALUES_MISMATCH 116 #define X509_R_LOADING_CERT_DIR 103 #define X509_R_LOADING_DEFAULTS 104 #define X509_R_METHOD_NOT_SUPPORTED 124 +#define X509_R_NEWER_CRL_NOT_NEWER 132 #define X509_R_NO_CERT_SET_FOR_US_TO_VERIFY 105 +#define X509_R_NO_CRL_NUMBER 130 #define X509_R_PUBLIC_KEY_DECODE_ERROR 125 #define X509_R_PUBLIC_KEY_ENCODE_ERROR 126 #define X509_R_SHOULD_RETRY 106 diff --git a/crypto/x509/x509_err.c b/crypto/x509/x509_err.c index a01402f41673083bf39174650020bab54ac43571..1731254d48abdcfee9b5e76f6002c406a0b5c376 100644 --- a/crypto/x509/x509_err.c +++ b/crypto/x509/x509_err.c @@ -1,6 +1,6 @@ /* crypto/x509/x509_err.c */ /* ==================================================================== - * Copyright (c) 1999-2006 The OpenSSL Project. All rights reserved. + * Copyright (c) 1999-2012 The OpenSSL Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -85,6 +85,7 @@ static ERR_STRING_DATA X509_str_functs[]= {ERR_FUNC(X509_F_X509_ATTRIBUTE_GET0_DATA), "X509_ATTRIBUTE_get0_data"}, {ERR_FUNC(X509_F_X509_ATTRIBUTE_SET1_DATA), "X509_ATTRIBUTE_set1_data"}, {ERR_FUNC(X509_F_X509_CHECK_PRIVATE_KEY), "X509_check_private_key"}, +{ERR_FUNC(X509_F_X509_CRL_DIFF), "X509_CRL_diff"}, {ERR_FUNC(X509_F_X509_CRL_PRINT_FP), "X509_CRL_print_fp"}, {ERR_FUNC(X509_F_X509_EXTENSION_CREATE_BY_NID), "X509_EXTENSION_create_by_NID"}, {ERR_FUNC(X509_F_X509_EXTENSION_CREATE_BY_OBJ), "X509_EXTENSION_create_by_OBJ"}, @@ -120,20 +121,27 @@ static ERR_STRING_DATA X509_str_functs[]= static ERR_STRING_DATA X509_str_reasons[]= { +{ERR_REASON(X509_R_AKID_MISMATCH) ,"akid mismatch"}, {ERR_REASON(X509_R_BAD_X509_FILETYPE) ,"bad x509 filetype"}, {ERR_REASON(X509_R_BASE64_DECODE_ERROR) ,"base64 decode error"}, {ERR_REASON(X509_R_CANT_CHECK_DH_KEY) ,"cant check dh key"}, {ERR_REASON(X509_R_CERT_ALREADY_IN_HASH_TABLE),"cert already in hash table"}, +{ERR_REASON(X509_R_CRL_ALREADY_DELTA) ,"crl already delta"}, +{ERR_REASON(X509_R_CRL_VERIFY_FAILURE) ,"crl verify failure"}, {ERR_REASON(X509_R_ERR_ASN1_LIB) ,"err asn1 lib"}, +{ERR_REASON(X509_R_IDP_MISMATCH) ,"idp mismatch"}, {ERR_REASON(X509_R_INVALID_DIRECTORY) ,"invalid directory"}, {ERR_REASON(X509_R_INVALID_FIELD_NAME) ,"invalid field name"}, {ERR_REASON(X509_R_INVALID_TRUST) ,"invalid trust"}, +{ERR_REASON(X509_R_ISSUER_MISMATCH) ,"issuer mismatch"}, {ERR_REASON(X509_R_KEY_TYPE_MISMATCH) ,"key type mismatch"}, {ERR_REASON(X509_R_KEY_VALUES_MISMATCH) ,"key values mismatch"}, {ERR_REASON(X509_R_LOADING_CERT_DIR) ,"loading cert dir"}, {ERR_REASON(X509_R_LOADING_DEFAULTS) ,"loading defaults"}, {ERR_REASON(X509_R_METHOD_NOT_SUPPORTED) ,"method not supported"}, +{ERR_REASON(X509_R_NEWER_CRL_NOT_NEWER) ,"newer crl not newer"}, {ERR_REASON(X509_R_NO_CERT_SET_FOR_US_TO_VERIFY),"no cert set for us to verify"}, +{ERR_REASON(X509_R_NO_CRL_NUMBER) ,"no crl number"}, {ERR_REASON(X509_R_PUBLIC_KEY_DECODE_ERROR),"public key decode error"}, {ERR_REASON(X509_R_PUBLIC_KEY_ENCODE_ERROR),"public key encode error"}, {ERR_REASON(X509_R_SHOULD_RETRY) ,"should retry"}, diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c index a4e31f71e7c7e690224cde768d445286d2274052..a21fa39c327b59fa9f2941c20dfb64eb2da8d3ce 100644 --- a/crypto/x509/x509_vfy.c +++ b/crypto/x509/x509_vfy.c @@ -1888,6 +1888,125 @@ int X509_get_pubkey_parameters(EVP_PKEY *pkey, STACK_OF(X509) *chain) return 1; } +/* Make a delta CRL as the diff between two full CRLs */ + +X509_CRL *X509_CRL_diff(X509_CRL *base, X509_CRL *newer, + EVP_PKEY *skey, const EVP_MD *md, unsigned int flags) + { + X509_CRL *crl = NULL; + int i; + STACK_OF(X509_REVOKED) *revs = NULL; + /* CRLs can't be delta already */ + if (base->base_crl_number || newer->base_crl_number) + { + X509err(X509_F_X509_CRL_DIFF, X509_R_CRL_ALREADY_DELTA); + return NULL; + } + /* Base and new CRL must have a CRL number */ + if (!base->crl_number || !newer->crl_number) + { + X509err(X509_F_X509_CRL_DIFF, X509_R_NO_CRL_NUMBER); + return NULL; + } + /* Issuer names must match */ + if (X509_NAME_cmp(X509_CRL_get_issuer(base), + X509_CRL_get_issuer(newer))) + { + X509err(X509_F_X509_CRL_DIFF, X509_R_ISSUER_MISMATCH); + return NULL; + } + /* AKID and IDP must match */ + if (!crl_extension_match(base, newer, NID_authority_key_identifier)) + { + X509err(X509_F_X509_CRL_DIFF, X509_R_AKID_MISMATCH); + return NULL; + } + if (!crl_extension_match(base, newer, NID_issuing_distribution_point)) + { + X509err(X509_F_X509_CRL_DIFF, X509_R_IDP_MISMATCH); + return NULL; + } + /* Newer CRL number must exceed full CRL number */ + if (ASN1_INTEGER_cmp(newer->crl_number, base->crl_number) <= 0) + { + X509err(X509_F_X509_CRL_DIFF, X509_R_NEWER_CRL_NOT_NEWER); + return NULL; + } + /* CRLs must verify */ + if (skey && (X509_CRL_verify(base, skey) <= 0 || + X509_CRL_verify(newer, skey) <= 0)) + { + X509err(X509_F_X509_CRL_DIFF, X509_R_CRL_VERIFY_FAILURE); + return NULL; + } + /* Create new CRL */ + crl = X509_CRL_new(); + if (!crl || !X509_CRL_set_version(crl, 1)) + goto memerr; + /* Set issuer name */ + if (!X509_CRL_set_issuer_name(crl, X509_CRL_get_issuer(newer))) + goto memerr; + + if (!X509_CRL_set_lastUpdate(crl, X509_CRL_get_lastUpdate(newer))) + goto memerr; + if (!X509_CRL_set_nextUpdate(crl, X509_CRL_get_nextUpdate(newer))) + goto memerr; + + /* Set base CRL number: must be critical */ + + if (!X509_CRL_add1_ext_i2d(crl, NID_delta_crl, base->crl_number, 1, 0)) + goto memerr; + + /* Copy extensions across from newest CRL to delta: this will set + * CRL number to correct value too. + */ + + for (i = 0; i < X509_CRL_get_ext_count(newer); i++) + { + X509_EXTENSION *ext; + ext = X509_CRL_get_ext(newer, i); + if (!X509_CRL_add_ext(crl, ext, -1)) + goto memerr; + } + + /* Go through revoked entries, copying as needed */ + + revs = X509_CRL_get_REVOKED(newer); + + for (i = 0; i < sk_X509_REVOKED_num(revs); i++) + { + X509_REVOKED *rvn, *rvtmp; + rvn = sk_X509_REVOKED_value(revs, i); + /* Add only if not also in base. + * TODO: need something cleverer here for some more complex + * CRLs covering multiple CAs. + */ + if (!X509_CRL_get0_by_serial(base, &rvtmp, rvn->serialNumber)) + { + rvtmp = X509_REVOKED_dup(rvn); + if (!rvtmp) + goto memerr; + if (!X509_CRL_add0_revoked(crl, rvtmp)) + { + X509_REVOKED_free(rvtmp); + goto memerr; + } + } + } + /* TODO: optionally prune deleted entries */ + + if (skey && md && !X509_CRL_sign(crl, skey, md)) + goto memerr; + + return crl; + + memerr: + X509err(X509_F_X509_CRL_DIFF, ERR_R_MALLOC_FAILURE); + if (crl) + X509_CRL_free(crl); + return NULL; + } + int X509_STORE_CTX_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func) {