diff options
author | Andrew Bartlett <abartlet@samba.org> | 2008-03-19 10:17:42 +1100 |
---|---|---|
committer | Andrew Bartlett <abartlet@samba.org> | 2008-03-19 10:17:42 +1100 |
commit | 9e6b0c28712ee77ce878809c8576826a3ba08d95 (patch) | |
tree | 1a325e474fbc22b1a1cadaf53a3af2c36e8d5ad2 /source4/heimdal/lib/hx509 | |
parent | 3530099cf226d591b687715b63b144d243e52083 (diff) | |
download | samba-9e6b0c28712ee77ce878809c8576826a3ba08d95.tar.gz samba-9e6b0c28712ee77ce878809c8576826a3ba08d95.tar.bz2 samba-9e6b0c28712ee77ce878809c8576826a3ba08d95.zip |
Merge lorikeet-heimdal -r 787 into Samba4 tree.
Andrew Bartlett
(This used to be commit d88b530522d3cef67c24422bd5182fb875d87ee2)
Diffstat (limited to 'source4/heimdal/lib/hx509')
-rw-r--r-- | source4/heimdal/lib/hx509/ca.c | 334 | ||||
-rw-r--r-- | source4/heimdal/lib/hx509/cert.c | 878 | ||||
-rw-r--r-- | source4/heimdal/lib/hx509/cms.c | 173 | ||||
-rw-r--r-- | source4/heimdal/lib/hx509/crypto.c | 194 | ||||
-rw-r--r-- | source4/heimdal/lib/hx509/env.c | 52 | ||||
-rw-r--r-- | source4/heimdal/lib/hx509/error.c | 81 | ||||
-rw-r--r-- | source4/heimdal/lib/hx509/hx509-private.h | 52 | ||||
-rw-r--r-- | source4/heimdal/lib/hx509/hx509-protos.h | 47 | ||||
-rw-r--r-- | source4/heimdal/lib/hx509/hx509.h | 7 | ||||
-rw-r--r-- | source4/heimdal/lib/hx509/hx509_err.et | 4 | ||||
-rw-r--r-- | source4/heimdal/lib/hx509/hx_locl.h | 6 | ||||
-rw-r--r-- | source4/heimdal/lib/hx509/keyset.c | 237 | ||||
-rw-r--r-- | source4/heimdal/lib/hx509/ks_file.c | 38 | ||||
-rw-r--r-- | source4/heimdal/lib/hx509/ks_keychain.c | 10 | ||||
-rw-r--r-- | source4/heimdal/lib/hx509/ks_p11.c | 4 | ||||
-rw-r--r-- | source4/heimdal/lib/hx509/lock.c | 8 | ||||
-rw-r--r-- | source4/heimdal/lib/hx509/name.c | 367 | ||||
-rw-r--r-- | source4/heimdal/lib/hx509/peer.c | 54 | ||||
-rw-r--r-- | source4/heimdal/lib/hx509/print.c | 200 | ||||
-rw-r--r-- | source4/heimdal/lib/hx509/revoke.c | 398 |
20 files changed, 2735 insertions, 409 deletions
diff --git a/source4/heimdal/lib/hx509/ca.c b/source4/heimdal/lib/hx509/ca.c index bf8fe1be1a..40260700b3 100644 --- a/source4/heimdal/lib/hx509/ca.c +++ b/source4/heimdal/lib/hx509/ca.c @@ -33,7 +33,13 @@ #include "hx_locl.h" #include <pkinit_asn1.h> -RCSID("$Id: ca.c 21379 2007-06-28 07:38:17Z lha $"); +RCSID("$Id: ca.c 22456 2008-01-15 20:22:53Z lha $"); + +/** + * @page page_ca Hx509 CA functions + * + * See the library functions here: @ref hx509_ca + */ struct hx509_ca_tbs { hx509_name subject; @@ -55,6 +61,19 @@ struct hx509_ca_tbs { CRLDistributionPoints crldp; }; +/** + * Allocate an to-be-signed certificate object that will be converted + * into an certificate. + * + * @param context A hx509 context. + * @param tbs returned to-be-signed certicate object, free with + * hx509_ca_tbs_free(). + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_ca + */ + int hx509_ca_tbs_init(hx509_context context, hx509_ca_tbs *tbs) { @@ -74,6 +93,14 @@ hx509_ca_tbs_init(hx509_context context, hx509_ca_tbs *tbs) return 0; } +/** + * Free an To Be Signed object. + * + * @param tbs object to free. + * + * @ingroup hx509_ca + */ + void hx509_ca_tbs_free(hx509_ca_tbs *tbs) { @@ -93,6 +120,19 @@ hx509_ca_tbs_free(hx509_ca_tbs *tbs) *tbs = NULL; } +/** + * Set the absolute time when the certificate is valid from. If not + * set the current time will be used. + * + * @param context A hx509 context. + * @param tbs object to be signed. + * @param t time the certificated will start to be valid + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_ca + */ + int hx509_ca_tbs_set_notBefore(hx509_context context, hx509_ca_tbs tbs, @@ -102,6 +142,18 @@ hx509_ca_tbs_set_notBefore(hx509_context context, return 0; } +/** + * Set the absolute time when the certificate is valid to. + * + * @param context A hx509 context. + * @param tbs object to be signed. + * @param t time when the certificate will expire + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_ca + */ + int hx509_ca_tbs_set_notAfter(hx509_context context, hx509_ca_tbs tbs, @@ -111,6 +163,18 @@ hx509_ca_tbs_set_notAfter(hx509_context context, return 0; } +/** + * Set the relative time when the certificiate is going to expire. + * + * @param context A hx509 context. + * @param tbs object to be signed. + * @param delta seconds to the certificate is going to expire. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_ca + */ + int hx509_ca_tbs_set_notAfter_lifetime(hx509_context context, hx509_ca_tbs tbs, @@ -130,12 +194,35 @@ static const struct units templatebits[] = { { NULL, 0 } }; +/** + * Make of template units, use to build flags argument to + * hx509_ca_tbs_set_template() with parse_units(). + * + * @return an units structure. + * + * @ingroup hx509_ca + */ + const struct units * hx509_ca_tbs_template_units(void) { return templatebits; } +/** + * Initialize the to-be-signed certificate object from a template certifiate. + * + * @param context A hx509 context. + * @param tbs object to be signed. + * @param flags bit field selecting what to copy from the template + * certifiate. + * @param cert template certificate. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_ca + */ + int hx509_ca_tbs_set_template(hx509_context context, hx509_ca_tbs tbs, @@ -170,12 +257,10 @@ hx509_ca_tbs_set_template(hx509_context context, tbs->notAfter = hx509_cert_get_notAfter(cert); if (flags & HX509_CA_TEMPLATE_SPKI) { free_SubjectPublicKeyInfo(&tbs->spki); - ret = hx509_cert_get_SPKI(cert, &tbs->spki); + ret = hx509_cert_get_SPKI(context, cert, &tbs->spki); tbs->flags.key = !ret; - if (ret) { - hx509_set_error_string(context, 0, ret, "Failed to copy SPKI"); + if (ret) return ret; - } } if (flags & HX509_CA_TEMPLATE_KU) { KeyUsage ku; @@ -202,6 +287,20 @@ hx509_ca_tbs_set_template(hx509_context context, return 0; } +/** + * Make the to-be-signed certificate object a CA certificate. If the + * pathLenConstraint is negative path length constraint is used. + * + * @param context A hx509 context. + * @param tbs object to be signed. + * @param pathLenConstraint path length constraint, negative, no + * constraint. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_ca + */ + int hx509_ca_tbs_set_ca(hx509_context context, hx509_ca_tbs tbs, @@ -212,6 +311,20 @@ hx509_ca_tbs_set_ca(hx509_context context, return 0; } +/** + * Make the to-be-signed certificate object a proxy certificate. If the + * pathLenConstraint is negative path length constraint is used. + * + * @param context A hx509 context. + * @param tbs object to be signed. + * @param pathLenConstraint path length constraint, negative, no + * constraint. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_ca + */ + int hx509_ca_tbs_set_proxy(hx509_context context, hx509_ca_tbs tbs, @@ -223,6 +336,17 @@ hx509_ca_tbs_set_proxy(hx509_context context, } +/** + * Make the to-be-signed certificate object a windows domain controller certificate. + * + * @param context A hx509 context. + * @param tbs object to be signed. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_ca + */ + int hx509_ca_tbs_set_domaincontroller(hx509_context context, hx509_ca_tbs tbs) @@ -231,6 +355,20 @@ hx509_ca_tbs_set_domaincontroller(hx509_context context, return 0; } +/** + * Set the subject public key info (SPKI) in the to-be-signed certificate + * object. SPKI is the public key and key related parameters in the + * certificate. + * + * @param context A hx509 context. + * @param tbs object to be signed. + * @param spki subject public key info to use for the to-be-signed certificate object. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_ca + */ + int hx509_ca_tbs_set_spki(hx509_context context, hx509_ca_tbs tbs, @@ -243,6 +381,19 @@ hx509_ca_tbs_set_spki(hx509_context context, return ret; } +/** + * Set the serial number to use for to-be-signed certificate object. + * + * @param context A hx509 context. + * @param tbs object to be signed. + * @param serialNumber serial number to use for the to-be-signed + * certificate object. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_ca + */ + int hx509_ca_tbs_set_serialnumber(hx509_context context, hx509_ca_tbs tbs, @@ -255,6 +406,19 @@ hx509_ca_tbs_set_serialnumber(hx509_context context, return ret; } +/** + * An an extended key usage to the to-be-signed certificate object. + * Duplicates will detected and not added. + * + * @param context A hx509 context. + * @param tbs object to be signed. + * @param oid extended key usage to add. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_ca + */ + int hx509_ca_tbs_add_eku(hx509_context context, hx509_ca_tbs tbs, @@ -285,6 +449,20 @@ hx509_ca_tbs_add_eku(hx509_context context, return 0; } +/** + * Add CRL distribution point URI to the to-be-signed certificate + * object. + * + * @param context A hx509 context. + * @param tbs object to be signed. + * @param uri uri to the CRL. + * @param issuername name of the issuer. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_ca + */ + int hx509_ca_tbs_add_crl_dp_uri(hx509_context context, hx509_ca_tbs tbs, @@ -325,6 +503,9 @@ hx509_ca_tbs_add_crl_dp_uri(hx509_context context, if (issuername) { #if 1 + /** + * issuername not supported + */ hx509_set_error_string(context, 0, EINVAL, "CRLDistributionPoints.name.issuername not yet supported"); return EINVAL; @@ -372,6 +553,20 @@ out: return ret; } +/** + * Add Subject Alternative Name otherName to the to-be-signed + * certificate object. + * + * @param context A hx509 context. + * @param tbs object to be signed. + * @param oid the oid of the OtherName. + * @param os data in the other name. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_ca + */ + int hx509_ca_tbs_add_san_otherName(hx509_context context, hx509_ca_tbs tbs, @@ -388,6 +583,18 @@ hx509_ca_tbs_add_san_otherName(hx509_context context, return add_GeneralNames(&tbs->san, &gn); } +/** + * Add Kerberos Subject Alternative Name to the to-be-signed + * certificate object. The principal string is a UTF8 string. + * + * @param context A hx509 context. + * @param tbs object to be signed. + * @param principal Kerberos principal to add to the certificate. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_ca + */ int hx509_ca_tbs_add_san_pkinit(hx509_context context, @@ -511,6 +718,19 @@ out: return ret; } +/** + * Add Microsoft UPN Subject Alternative Name to the to-be-signed + * certificate object. The principal string is a UTF8 string. + * + * @param context A hx509 context. + * @param tbs object to be signed. + * @param principal Microsoft UPN string. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_ca + */ + int hx509_ca_tbs_add_san_ms_upn(hx509_context context, hx509_ca_tbs tbs, @@ -519,6 +739,19 @@ hx509_ca_tbs_add_san_ms_upn(hx509_context context, return add_utf8_san(context, tbs, oid_id_pkinit_ms_san(), principal); } +/** + * Add a Jabber/XMPP jid Subject Alternative Name to the to-be-signed + * certificate object. The jid is an UTF8 string. + * + * @param context A hx509 context. + * @param tbs object to be signed. + * @param jid string of an a jabber id in UTF8. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_ca + */ + int hx509_ca_tbs_add_san_jid(hx509_context context, hx509_ca_tbs tbs, @@ -528,6 +761,22 @@ hx509_ca_tbs_add_san_jid(hx509_context context, } +/** + * Add a Subject Alternative Name hostname to to-be-signed certificate + * object. A domain match starts with ., an exact match does not. + * + * Example of a an domain match: .domain.se matches the hostname + * host.domain.se. + * + * @param context A hx509 context. + * @param tbs object to be signed. + * @param dnsname a hostame. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_ca + */ + int hx509_ca_tbs_add_san_hostname(hx509_context context, hx509_ca_tbs tbs, @@ -542,6 +791,19 @@ hx509_ca_tbs_add_san_hostname(hx509_context context, return add_GeneralNames(&tbs->san, &gn); } +/** + * Add a Subject Alternative Name rfc822 (email address) to + * to-be-signed certificate object. + * + * @param context A hx509 context. + * @param tbs object to be signed. + * @param rfc822Name a string to a email address. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_ca + */ + int hx509_ca_tbs_add_san_rfc822name(hx509_context context, hx509_ca_tbs tbs, @@ -556,6 +818,17 @@ hx509_ca_tbs_add_san_rfc822name(hx509_context context, return add_GeneralNames(&tbs->san, &gn); } +/** + * Set the subject name of a to-be-signed certificate object. + * + * @param context A hx509 context. + * @param tbs object to be signed. + * @param subject the name to set a subject. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_ca + */ int hx509_ca_tbs_set_subject(hx509_context context, @@ -567,6 +840,20 @@ hx509_ca_tbs_set_subject(hx509_context context, return hx509_name_copy(context, subject, &tbs->subject); } +/** + * Expand the the subject name in the to-be-signed certificate object + * using hx509_name_expand(). + * + * @param context A hx509 context. + * @param tbs object to be signed. + * @param env enviroment variable to expand variables in the subject + * name, see hx509_env_init(). + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_ca + */ + int hx509_ca_tbs_subject_expand(hx509_context context, hx509_ca_tbs tbs, @@ -1148,6 +1435,30 @@ out: } +/** + * Sign a to-be-signed certificate object with a issuer certificate. + * + * The caller needs to at least have called the following functions on the + * to-be-signed certificate object: + * - hx509_ca_tbs_init() + * - hx509_ca_tbs_set_subject() + * - hx509_ca_tbs_set_spki() + * + * When done the to-be-signed certificate object should be freed with + * hx509_ca_tbs_free(). + * + * When creating self-signed certificate use hx509_ca_sign_self() instead. + * + * @param context A hx509 context. + * @param tbs object to be signed. + * @param signer the CA certificate object to sign with (need private key). + * @param certificate return cerificate, free with hx509_cert_free(). + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_ca + */ + int hx509_ca_sign(hx509_context context, hx509_ca_tbs tbs, @@ -1179,6 +1490,19 @@ out: return ret; } +/** + * Work just like hx509_ca_sign() but signs it-self. + * + * @param context A hx509 context. + * @param tbs object to be signed. + * @param signer private key to sign with. + * @param certificate return cerificate, free with hx509_cert_free(). + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_ca + */ + int hx509_ca_sign_self(hx509_context context, hx509_ca_tbs tbs, diff --git a/source4/heimdal/lib/hx509/cert.c b/source4/heimdal/lib/hx509/cert.c index b7f19d152a..09c85bc084 100644 --- a/source4/heimdal/lib/hx509/cert.c +++ b/source4/heimdal/lib/hx509/cert.c @@ -32,10 +32,25 @@ */ #include "hx_locl.h" -RCSID("$Id: cert.c 21380 2007-06-28 07:38:38Z lha $"); +RCSID("$Id: cert.c 22583 2008-02-11 20:46:21Z lha $"); #include "crypto-headers.h" #include <rtbl.h> +/** + * @page page_cert The basic certificate + * + * The basic hx509 cerificate object in hx509 is hx509_cert. The + * hx509_cert object is representing one X509/PKIX certificate and + * associated attributes; like private key, friendly name, etc. + * + * A hx509_cert object is usully found via the keyset interfaces (@ref + * page_keyset), but its also possible to create a certificate + * directly from a parsed object with hx509_cert_init() and + * hx509_cert_init_data(). + * + * See the library functions here: @ref hx509_cert + */ + struct hx509_verify_ctx_data { hx509_certs trust_anchors; int flags; @@ -78,8 +93,16 @@ typedef struct hx509_name_constraints { #define GeneralSubtrees_SET(g,var) \ (g)->len = (var)->len, (g)->val = (var)->val; -/* +/** + * Creates a hx509 context that most functions in the library + * uses. The context is only allowed to be used by one thread at each + * moment. Free the context with hx509_context_free(). * + * @param context Returns a pointer to new hx509 context. + * + * @return Returns an hx509 error code. + * + * @ingroup hx509 */ int @@ -113,6 +136,19 @@ hx509_context_init(hx509_context *context) return 0; } +/** + * Selects if the hx509_revoke_verify() function is going to require + * the existans of a revokation method (OSCP, CRL) or not. Note that + * hx509_verify_path(), hx509_cms_verify_signed(), and other function + * call hx509_revoke_verify(). + * + * @param context hx509 context to change the flag for. + * @param flag zero, revokation method required, non zero missing + * revokation method ok + * + * @ingroup hx509_verify + */ + void hx509_context_set_missing_revoke(hx509_context context, int flag) { @@ -122,6 +158,14 @@ hx509_context_set_missing_revoke(hx509_context context, int flag) context->flags &= ~HX509_CTX_VERIFY_MISSING_OK; } +/** + * Free the context allocated by hx509_context_init(). + * + * @param context context to be freed. + * + * @ingroup hx509 + */ + void hx509_context_free(hx509_context *context) { @@ -139,7 +183,6 @@ hx509_context_free(hx509_context *context) *context = NULL; } - /* * */ @@ -154,39 +197,25 @@ _hx509_get_cert(hx509_cert cert) * */ -#if 0 -void -_hx509_print_cert_subject(hx509_cert cert) -{ - char *subject_name; - hx509_name name; - int ret; - - ret = hx509_cert_get_subject(cert, &name); - if (ret) - abort(); - - ret = hx509_name_to_string(name, &subject_name); - hx509_name_free(&name); - if (ret) - abort(); - - printf("name: %s\n", subject_name); - - free(subject_name); -} -#endif - -/* - * - */ - int _hx509_cert_get_version(const Certificate *t) { return t->tbsCertificate.version ? *t->tbsCertificate.version + 1 : 1; } +/** + * Allocate and init an hx509 certificate object from the decoded + * certificate `cī. + * + * @param context A hx509 context. + * @param c + * @param cert + * + * @return Returns an hx509 error code. + * + * @ingroup hx509_cert + */ + int hx509_cert_init(hx509_context context, const Certificate *c, hx509_cert *cert) { @@ -218,9 +247,29 @@ hx509_cert_init(hx509_context context, const Certificate *c, hx509_cert *cert) return ret; } +/** + * Just like hx509_cert_init(), but instead of a decode certificate + * takes an pointer and length to a memory region that contains a + * DER/BER encoded certificate. + * + * If the memory region doesn't contain just the certificate and + * nothing more the function will fail with + * HX509_EXTRA_DATA_AFTER_STRUCTURE. + * + * @param context A hx509 context. + * @param ptr pointer to memory region containing encoded certificate. + * @param len length of memory region. + * @param cert a return pointer to a hx509 certificate object, will + * contain NULL on error. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_cert + */ + int hx509_cert_init_data(hx509_context context, - const void *ptr, + const void *ptr, size_t len, hx509_cert *cert) { @@ -265,6 +314,15 @@ _hx509_cert_assign_key(hx509_cert cert, hx509_private_key private_key) return 0; } +/** + * Free reference to the hx509 certificate object, if the refcounter + * reaches 0, the object if freed. Its allowed to pass in NULL. + * + * @param cert the cert to free. + * + * @ingroup hx509_cert + */ + void hx509_cert_free(hx509_cert cert) { @@ -274,7 +332,7 @@ hx509_cert_free(hx509_cert cert) return; if (cert->ref <= 0) - _hx509_abort("refcount <= 0"); + _hx509_abort("cert refcount <= 0 on free"); if (--cert->ref > 0) return; @@ -300,9 +358,21 @@ hx509_cert_free(hx509_cert cert) free(cert); } +/** + * Add a reference to a hx509 certificate object. + * + * @param cert a pointer to an hx509 certificate object. + * + * @return the same object as is passed in. + * + * @ingroup hx509_cert + */ + hx509_cert hx509_cert_ref(hx509_cert cert) { + if (cert == NULL) + return NULL; if (cert->ref <= 0) _hx509_abort("cert refcount <= 0"); cert->ref++; @@ -311,6 +381,18 @@ hx509_cert_ref(hx509_cert cert) return cert; } +/** + * Allocate an verification context that is used fo control the + * verification process. + * + * @param context A hx509 context. + * @param ctx returns a pointer to a hx509_verify_ctx object. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_verify + */ + int hx509_verify_init_ctx(hx509_context context, hx509_verify_ctx *ctx) { @@ -327,26 +409,75 @@ hx509_verify_init_ctx(hx509_context context, hx509_verify_ctx *ctx) return 0; } +/** + * Free an hx509 verification context. + * + * @param ctx the context to be freed. + * + * @ingroup hx509_verify + */ + void hx509_verify_destroy_ctx(hx509_verify_ctx ctx) { - if (ctx) + if (ctx) { + hx509_certs_free(&ctx->trust_anchors); + hx509_revoke_free(&ctx->revoke_ctx); memset(ctx, 0, sizeof(*ctx)); + } free(ctx); } +/** + * Set the trust anchors in the verification context, makes an + * reference to the keyset, so the consumer can free the keyset + * independent of the destruction of the verification context (ctx). + * + * @param ctx a verification context + * @param set a keyset containing the trust anchors. + * + * @ingroup hx509_verify + */ + void hx509_verify_attach_anchors(hx509_verify_ctx ctx, hx509_certs set) { - ctx->trust_anchors = set; + ctx->trust_anchors = _hx509_certs_ref(set); } +/** + * Attach an revocation context to the verfication context, , makes an + * reference to the revoke context, so the consumer can free the + * revoke context independent of the destruction of the verification + * context. If there is no revoke context, the verification process is + * NOT going to check any verification status. + * + * @param ctx a verification context. + * @param revoke_ctx a revoke context. + * + * @ingroup hx509_verify + */ + void hx509_verify_attach_revoke(hx509_verify_ctx ctx, hx509_revoke_ctx revoke_ctx) { - ctx->revoke_ctx = revoke_ctx; + if (ctx->revoke_ctx) + hx509_revoke_free(&ctx->revoke_ctx); + ctx->revoke_ctx = _hx509_revoke_ref(revoke_ctx); } +/** + * Set the clock time the the verification process is going to + * use. Used to check certificate in the past and future time. If not + * set the current time will be used. + * + * @param ctx a verification context. + * @param t the time the verifiation is using. + * + * + * @ingroup hx509_verify + */ + void hx509_verify_set_time(hx509_verify_ctx ctx, time_t t) { @@ -354,12 +485,32 @@ hx509_verify_set_time(hx509_verify_ctx ctx, time_t t) ctx->time_now = t; } +/** + * Set the maximum depth of the certificate chain that the path + * builder is going to try. + * + * @param ctx a verification context + * @param max_depth maxium depth of the certificate chain, include + * trust anchor. + * + * @ingroup hx509_verify + */ + void hx509_verify_set_max_depth(hx509_verify_ctx ctx, unsigned int max_depth) { ctx->max_depth = max_depth; } +/** + * Allow or deny the use of proxy certificates + * + * @param ctx a verification context + * @param boolean if non zero, allow proxy certificates. + * + * @ingroup hx509_verify + */ + void hx509_verify_set_proxy_certificate(hx509_verify_ctx ctx, int boolean) { @@ -369,6 +520,17 @@ hx509_verify_set_proxy_certificate(hx509_verify_ctx ctx, int boolean) ctx->flags &= ~HX509_VERIFY_CTX_F_ALLOW_PROXY_CERTIFICATE; } +/** + * Select strict RFC3280 verification of certificiates. This means + * checking key usage on CA certificates, this will make version 1 + * certificiates unuseable. + * + * @param ctx a verification context + * @param boolean if non zero, use strict verification. + * + * @ingroup hx509_verify + */ + void hx509_verify_set_strict_rfc3280_verification(hx509_verify_ctx ctx, int boolean) { @@ -378,6 +540,20 @@ hx509_verify_set_strict_rfc3280_verification(hx509_verify_ctx ctx, int boolean) ctx->flags &= ~HX509_VERIFY_CTX_F_REQUIRE_RFC3280; } +/** + * Allow using the operating system builtin trust anchors if no other + * trust anchors are configured. + * + * @param ctx a verification context + * @param boolean if non zero, useing the operating systems builtin + * trust anchors. + * + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_cert + */ + void hx509_verify_ctx_f_allow_default_trustanchors(hx509_verify_ctx ctx, int boolean) { @@ -512,6 +688,15 @@ add_to_list(hx509_octet_string_list *list, const heim_octet_string *entry) return 0; } +/** + * Free a list of octet strings returned by another hx509 library + * function. + * + * @param list list to be freed. + * + * @ingroup hx509_misc + */ + void hx509_free_octet_string_list(hx509_octet_string_list *list) { @@ -523,8 +708,26 @@ hx509_free_octet_string_list(hx509_octet_string_list *list) list->len = 0; } +/** + * Return a list of subjectAltNames specified by oid in the + * certificate. On error the + * + * The returned list of octet string should be freed with + * hx509_free_octet_string_list(). + * + * @param context A hx509 context. + * @param cert a hx509 certificate object. + * @param oid an oid to for SubjectAltName. + * @param list list of matching SubjectAltName. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_cert + */ + int -hx509_cert_find_subjectAltName_otherName(hx509_cert cert, +hx509_cert_find_subjectAltName_otherName(hx509_context context, + hx509_cert cert, const heim_oid *oid, hx509_octet_string_list *list) { @@ -541,9 +744,11 @@ hx509_cert_find_subjectAltName_otherName(hx509_cert cert, if (ret == HX509_EXTENSION_NOT_FOUND) { ret = 0; break; - } else if (ret != 0) - break; - + } else if (ret != 0) { + hx509_set_error_string(context, 0, ret, "Error searching for SAN"); + hx509_free_octet_string_list(list); + return ret; + } for (j = 0; j < sa.len; j++) { if (sa.val[j].element == choice_GeneralName_otherName && @@ -551,6 +756,10 @@ hx509_cert_find_subjectAltName_otherName(hx509_cert cert, { ret = add_to_list(list, &sa.val[j].u.otherName.value); if (ret) { + hx509_set_error_string(context, 0, ret, + "Error adding an exra SAN to " + "return list"); + hx509_free_octet_string_list(list); free_GeneralNames(&sa); return ret; } @@ -558,7 +767,7 @@ hx509_cert_find_subjectAltName_otherName(hx509_cert cert, } free_GeneralNames(&sa); } - return ret; + return 0; } @@ -605,6 +814,12 @@ check_key_usage(hx509_context context, const Certificate *cert, return 0; } +/* + * Return 0 on matching key usage 'flags' for 'cert', otherwise return + * an error code. If 'req_present' the existance is required of the + * KeyUsage extension. + */ + int _hx509_check_key_usage(hx509_context context, hx509_cert cert, unsigned flags, int req_present) @@ -678,10 +893,13 @@ _hx509_cert_is_parent_cmp(const Certificate *subject, int diff; AuthorityKeyIdentifier ai; SubjectKeyIdentifier si; - int ret_ai, ret_si; + int ret_ai, ret_si, ret; - diff = _hx509_name_cmp(&issuer->tbsCertificate.subject, - &subject->tbsCertificate.issuer); + ret = _hx509_name_cmp(&issuer->tbsCertificate.subject, + &subject->tbsCertificate.issuer, + &diff); + if (ret) + return ret; if (diff) return diff; @@ -689,7 +907,7 @@ _hx509_cert_is_parent_cmp(const Certificate *subject, memset(&si, 0, sizeof(si)); /* - * Try to find AuthorityKeyIdentifier, if its not present in the + * Try to find AuthorityKeyIdentifier, if it's not present in the * subject certificate nor the parent. */ @@ -736,8 +954,11 @@ _hx509_cert_is_parent_cmp(const Certificate *subject, name.u.rdnSequence = ai.authorityCertIssuer->val[0].u.directoryName.u.rdnSequence; - diff = _hx509_name_cmp(&issuer->tbsCertificate.subject, - &name); + ret = _hx509_name_cmp(&issuer->tbsCertificate.subject, + &name, + &diff); + if (ret) + return ret; if (diff) return diff; diff = 0; @@ -776,13 +997,22 @@ certificate_is_anchor(hx509_context context, } static int -certificate_is_self_signed(const Certificate *cert) -{ - return _hx509_cert_is_parent_cmp(cert, cert, 1) == 0; +certificate_is_self_signed(hx509_context context, + const Certificate *cert, + int *self_signed) +{ + int ret, diff; + ret = _hx509_name_cmp(&cert->tbsCertificate.subject, + &cert->tbsCertificate.issuer, &diff); + *self_signed = (diff == 0); + if (ret) + hx509_set_error_string(context, 0, ret, + "Failed to check if self signed"); + return ret; } /* - * The subjectName is "null" when its empty set of relative DBs. + * The subjectName is "null" when it's empty set of relative DBs. */ static int @@ -1032,9 +1262,9 @@ _hx509_calculate_path(hx509_context context, return 0; } -static int -AlgorithmIdentifier_cmp(const AlgorithmIdentifier *p, - const AlgorithmIdentifier *q) +int +_hx509_AlgorithmIdentifier_cmp(const AlgorithmIdentifier *p, + const AlgorithmIdentifier *q) { int diff; diff = der_heim_oid_cmp(&p->algorithm, &q->algorithm); @@ -1061,8 +1291,8 @@ _hx509_Certificate_cmp(const Certificate *p, const Certificate *q) diff = der_heim_bit_string_cmp(&p->signatureValue, &q->signatureValue); if (diff) return diff; - diff = AlgorithmIdentifier_cmp(&p->signatureAlgorithm, - &q->signatureAlgorithm); + diff = _hx509_AlgorithmIdentifier_cmp(&p->signatureAlgorithm, + &q->signatureAlgorithm); if (diff) return diff; diff = der_heim_octet_string_cmp(&p->tbsCertificate._save, @@ -1070,24 +1300,77 @@ _hx509_Certificate_cmp(const Certificate *p, const Certificate *q) return diff; } +/** + * Compare to hx509 certificate object, useful for sorting. + * + * @param p a hx509 certificate object. + * @param q a hx509 certificate object. + * + * @return 0 the objects are the same, returns > 0 is p is "larger" + * then q, < 0 if p is "smaller" then q. + * + * @ingroup hx509_cert + */ + int hx509_cert_cmp(hx509_cert p, hx509_cert q) { return _hx509_Certificate_cmp(p->data, q->data); } +/** + * Return the name of the issuer of the hx509 certificate. + * + * @param p a hx509 certificate object. + * @param name a pointer to a hx509 name, should be freed by + * hx509_name_free(). + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_cert + */ + int hx509_cert_get_issuer(hx509_cert p, hx509_name *name) { return _hx509_name_from_Name(&p->data->tbsCertificate.issuer, name); } +/** + * Return the name of the subject of the hx509 certificate. + * + * @param p a hx509 certificate object. + * @param name a pointer to a hx509 name, should be freed by + * hx509_name_free(). See also hx509_cert_get_base_subject(). + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_cert + */ + int hx509_cert_get_subject(hx509_cert p, hx509_name *name) { return _hx509_name_from_Name(&p->data->tbsCertificate.subject, name); } +/** + * Return the name of the base subject of the hx509 certificate. If + * the certiicate is a verified proxy certificate, the this function + * return the base certificate (root of the proxy chain). If the proxy + * certificate is not verified with the base certificate + * HX509_PROXY_CERTIFICATE_NOT_CANONICALIZED is returned. + * + * @param context a hx509 context. + * @param c a hx509 certificate object. + * @param name a pointer to a hx509 name, should be freed by + * hx509_name_free(). See also hx509_cert_get_subject(). + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_cert + */ + int hx509_cert_get_base_subject(hx509_context context, hx509_cert c, hx509_name *name) @@ -1104,31 +1387,107 @@ hx509_cert_get_base_subject(hx509_context context, hx509_cert c, return _hx509_name_from_Name(&c->data->tbsCertificate.subject, name); } +/** + * Get serial number of the certificate. + * + * @param p a hx509 certificate object. + * @param i serial number, should be freed ith der_free_heim_integer(). + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_cert + */ + int hx509_cert_get_serialnumber(hx509_cert p, heim_integer *i) { return der_copy_heim_integer(&p->data->tbsCertificate.serialNumber, i); } +/** + * Get notBefore time of the certificate. + * + * @param p a hx509 certificate object. + * + * @return return not before time + * + * @ingroup hx509_cert + */ + time_t hx509_cert_get_notBefore(hx509_cert p) { return _hx509_Time2time_t(&p->data->tbsCertificate.validity.notBefore); } +/** + * Get notAfter time of the certificate. + * + * @param p a hx509 certificate object. + * + * @return return not after time. + * + * @ingroup hx509_cert + */ + time_t hx509_cert_get_notAfter(hx509_cert p) { return _hx509_Time2time_t(&p->data->tbsCertificate.validity.notAfter); } +/** + * Get the SubjectPublicKeyInfo structure from the hx509 certificate. + * + * @param context a hx509 context. + * @param p a hx509 certificate object. + * @param spki SubjectPublicKeyInfo, should be freed with + * free_SubjectPublicKeyInfo(). + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_cert + */ + +int +hx509_cert_get_SPKI(hx509_context context, hx509_cert p, SubjectPublicKeyInfo *spki) +{ + int ret; + + ret = copy_SubjectPublicKeyInfo(&p->data->tbsCertificate.subjectPublicKeyInfo, spki); + if (ret) + hx509_set_error_string(context, 0, ret, "Failed to copy SPKI"); + return ret; +} + +/** + * Get the AlgorithmIdentifier from the hx509 certificate. + * + * @param context a hx509 context. + * @param p a hx509 certificate object. + * @param alg AlgorithmIdentifier, should be freed with + * free_AlgorithmIdentifier(). + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_cert + */ + int -hx509_cert_get_SPKI(hx509_cert p, SubjectPublicKeyInfo *spki) +hx509_cert_get_SPKI_AlgorithmIdentifier(hx509_context context, + hx509_cert p, + AlgorithmIdentifier *alg) { - return copy_SubjectPublicKeyInfo(&p->data->tbsCertificate.subjectPublicKeyInfo, - spki); + int ret; + + ret = copy_AlgorithmIdentifier(&p->data->tbsCertificate.subjectPublicKeyInfo.algorithm, alg); + if (ret) + hx509_set_error_string(context, 0, ret, + "Failed to copy SPKI AlgorithmIdentifier"); + return ret; } + hx509_private_key _hx509_cert_private_key(hx509_cert p) { @@ -1136,6 +1495,13 @@ _hx509_cert_private_key(hx509_cert p) } int +hx509_cert_have_private_key(hx509_cert p) +{ + return p->private_key ? 1 : 0; +} + + +int _hx509_cert_private_key_exportable(hx509_cert p) { if (p->private_key == NULL) @@ -1253,9 +1619,14 @@ match_RDN(const RelativeDistinguishedName *c, return HX509_NAME_CONSTRAINT_ERROR; for (i = 0; i < n->len; i++) { + int diff, ret; + if (der_heim_oid_cmp(&c->val[i].type, &n->val[i].type) != 0) return HX509_NAME_CONSTRAINT_ERROR; - if (_hx509_name_ds_cmp(&c->val[i].value, &n->val[i].value) != 0) + ret = _hx509_name_ds_cmp(&c->val[i].value, &n->val[i].value, &diff); + if (ret) + return ret; + if (diff != 0) return HX509_NAME_CONSTRAINT_ERROR; } return 0; @@ -1316,7 +1687,7 @@ match_general_name(const GeneralName *c, const GeneralName *n, int *match) return HX509_NAME_CONSTRAINT_ERROR; if (strcasecmp(s + 1 + len2 - len1, c->u.rfc822Name) != 0) return HX509_NAME_CONSTRAINT_ERROR; - if (len1 < len2 && s[len2 - len1] != '.') + if (len1 < len2 && s[len2 - len1 + 1] != '.') return HX509_NAME_CONSTRAINT_ERROR; } *match = 1; @@ -1387,7 +1758,6 @@ match_alt_name(const GeneralName *n, const Certificate *c, } free_GeneralNames(&sa); } while (1); - return ret; } @@ -1457,7 +1827,10 @@ check_name_constraints(hx509_context context, } /* allow null subjectNames, they wont matches anything */ if (match == 0 && !subject_null_p(c)) { - hx509_clear_error_string(context); + hx509_set_error_string(context, 0, HX509_VERIFY_CONSTRAINTS, + "Error verify constraints, " + "certificate didn't match any " + "permitted subtree"); return HX509_VERIFY_CONSTRAINTS; } } @@ -1469,7 +1842,10 @@ check_name_constraints(hx509_context context, return ret; } if (match) { - hx509_clear_error_string(context); + hx509_set_error_string(context, 0, HX509_VERIFY_CONSTRAINTS, + "Error verify constraints, " + "certificate included in excluded " + "subtree"); return HX509_VERIFY_CONSTRAINTS; } } @@ -1487,6 +1863,21 @@ free_name_constraints(hx509_name_constraints *nc) free(nc->val); } +/** + * Build and verify the path for the certificate to the trust anchor + * specified in the verify context. The path is constructed from the + * certificate, the pool and the trust anchors. + * + * @param context A hx509 context. + * @param ctx A hx509 verification context. + * @param cert the certificate to build the path from. + * @param pool A keyset of certificates to build the chain from. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_verify + */ + int hx509_verify_path(hx509_context context, hx509_verify_ctx ctx, @@ -1495,10 +1886,7 @@ hx509_verify_path(hx509_context context, { hx509_name_constraints nc; hx509_path path; -#if 0 - const AlgorithmIdentifier *alg_id; -#endif - int ret, i, proxy_cert_depth; + int ret, i, proxy_cert_depth, selfsigned_depth, diff; enum certtype type; Name proxy_issuer; hx509_certs anchors = NULL; @@ -1538,10 +1926,6 @@ hx509_verify_path(hx509_context context, if (ret) goto out; -#if 0 - alg_id = path.val[path->len - 1]->data->tbsCertificate.signature; -#endif - /* * Check CA and proxy certificate chain from the top of the * certificate chain. Also check certificate is valid with respect @@ -1550,6 +1934,7 @@ hx509_verify_path(hx509_context context, */ proxy_cert_depth = 0; + selfsigned_depth = 0; if (ctx->flags & HX509_VERIFY_CTX_F_ALLOW_PROXY_CERTIFICATE) type = PROXY_CERT; @@ -1570,6 +1955,7 @@ hx509_verify_path(hx509_context context, switch (type) { case CA_CERT: + /* XXX make constants for keyusage */ ret = check_key_usage(context, c, 1 << 5, REQUIRE_RFC3280(ctx) ? TRUE : FALSE); @@ -1578,6 +1964,18 @@ hx509_verify_path(hx509_context context, "Key usage missing from CA certificate"); goto out; } + + /* self signed cert doesn't add to path length */ + if (i + 1 != path.len) { + int selfsigned; + + ret = certificate_is_self_signed(context, c, &selfsigned); + if (ret) + goto out; + if (selfsigned) + selfsigned_depth++; + } + break; case PROXY_CERT: { ProxyCertInfo info; @@ -1624,8 +2022,12 @@ hx509_verify_path(hx509_context context, */ if (proxy_cert_depth) { - ret = _hx509_name_cmp(&proxy_issuer, &c->tbsCertificate.subject); + ret = _hx509_name_cmp(&proxy_issuer, &c->tbsCertificate.subject, &diff); if (ret) { + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + if (diff) { ret = HX509_PROXY_CERT_NAME_WRONG; hx509_set_error_string(context, 0, ret, "Base proxy name not right"); @@ -1658,8 +2060,12 @@ hx509_verify_path(hx509_context context, free_RelativeDistinguishedName(&proxy_issuer.u.rdnSequence.val[j - 1]); proxy_issuer.u.rdnSequence.len -= 1; - ret = _hx509_name_cmp(&proxy_issuer, &c->tbsCertificate.issuer); - if (ret != 0) { + ret = _hx509_name_cmp(&proxy_issuer, &c->tbsCertificate.issuer, &diff); + if (ret) { + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + if (diff != 0) { ret = HX509_PROXY_CERT_NAME_WRONG; hx509_set_error_string(context, 0, ret, "Proxy issuer name not as expected"); @@ -1685,9 +2091,13 @@ hx509_verify_path(hx509_context context, */ if (proxy_cert_depth) { - ret = _hx509_name_cmp(&proxy_issuer, - &c->tbsCertificate.subject); + ret = _hx509_name_cmp(&proxy_issuer, + &c->tbsCertificate.subject, &diff); if (ret) { + hx509_set_error_string(context, 0, ret, "out of memory"); + goto out; + } + if (diff) { ret = HX509_PROXY_CERT_NAME_WRONG; hx509_clear_error_string(context); goto out; @@ -1705,7 +2115,8 @@ hx509_verify_path(hx509_context context, break; } - ret = check_basic_constraints(context, c, type, i - proxy_cert_depth); + ret = check_basic_constraints(context, c, type, + i - proxy_cert_depth - selfsigned_depth); if (ret) goto out; @@ -1742,22 +2153,16 @@ hx509_verify_path(hx509_context context, for (ret = 0, i = path.len - 1; i >= 0; i--) { Certificate *c; + int selfsigned; c = _hx509_get_cert(path.val[i]); -#if 0 - /* check that algorithm and parameters is the same */ - /* XXX this is wrong */ - ret = alg_cmp(&c->tbsCertificate.signature, alg_id); - if (ret) { - hx509_clear_error_string(context); - ret = HX509_PATH_ALGORITHM_CHANGED; + ret = certificate_is_self_signed(context, c, &selfsigned); + if (ret) goto out; - } -#endif /* verify name constraints, not for selfsigned and anchor */ - if (!certificate_is_self_signed(c) || i == path.len - 1) { + if (!selfsigned || i + 1 != path.len) { ret = check_name_constraints(context, &nc, c); if (ret) { goto out; @@ -1813,12 +2218,6 @@ hx509_verify_path(hx509_context context, hx509_certs_free(&certs); } -#if 0 - for (i = path.len - 1; i >= 0; i--) { - _hx509_print_cert_subject(path.val[i]); - } -#endif - /* * Verify signatures, do this backward so public key working * parameter is passed up from the anchor up though the chain. @@ -1830,11 +2229,17 @@ hx509_verify_path(hx509_context context, c = _hx509_get_cert(path.val[i]); /* is last in chain (trust anchor) */ - if (i == path.len - 1) { + if (i + 1 == path.len) { + int selfsigned; + signer = path.val[i]->data; + ret = certificate_is_self_signed(context, signer, &selfsigned); + if (ret) + goto out; + /* if trust anchor is not self signed, don't check sig */ - if (!certificate_is_self_signed(signer)) + if (!selfsigned) continue; } else { /* take next certificate in chain */ @@ -1863,6 +2268,20 @@ out: return ret; } +/** + * Verify a signature made using the private key of an certificate. + * + * @param context A hx509 context. + * @param signer the certificate that made the signature. + * @param alg algorthm that was used to sign the data. + * @param data the data that was signed. + * @param sig the sigature to verify. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_crypto + */ + int hx509_verify_signature(hx509_context context, const hx509_cert signer, @@ -1873,7 +2292,26 @@ hx509_verify_signature(hx509_context context, return _hx509_verify_signature(context, signer->data, alg, data, sig); } -#define HX509_VHN_F_ALLOW_NO_MATCH 1 + +/** + * Verify that the certificate is allowed to be used for the hostname + * and address. + * + * @param context A hx509 context. + * @param cert the certificate to match with + * @param flags Flags to modify the behavior: + * - HX509_VHN_F_ALLOW_NO_MATCH no match is ok + * @param type type of hostname: + * - HX509_HN_HOSTNAME for plain hostname. + * - HX509_HN_DNSSRV for DNS SRV names. + * @param hostname the hostname to check + * @param sa address of the host + * @param sa_size length of address + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_cert + */ int hx509_verify_hostname(hx509_context context, @@ -1984,6 +2422,19 @@ _hx509_set_cert_attribute(hx509_context context, return 0; } +/** + * Get an external attribute for the certificate, examples are + * friendly name and id. + * + * @param cert hx509 certificate object to search + * @param oid an oid to search for. + * + * @return an hx509_cert_attribute, only valid as long as the + * certificate is referenced. + * + * @ingroup hx509_cert + */ + hx509_cert_attribute hx509_cert_get_attribute(hx509_cert cert, const heim_oid *oid) { @@ -1994,6 +2445,17 @@ hx509_cert_get_attribute(hx509_cert cert, const heim_oid *oid) return NULL; } +/** + * Set the friendly name on the certificate. + * + * @param cert The certificate to set the friendly name on + * @param name Friendly name. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_cert + */ + int hx509_cert_set_friendly_name(hx509_cert cert, const char *name) { @@ -2005,6 +2467,16 @@ hx509_cert_set_friendly_name(hx509_cert cert, const char *name) return 0; } +/** + * Get friendly name of the certificate. + * + * @param cert cert to get the friendly name from. + * + * @return an friendly name or NULL if there is. The friendly name is + * only valid as long as the certificate is referenced. + * + * @ingroup hx509_cert + */ const char * hx509_cert_get_friendly_name(hx509_cert cert) @@ -2056,6 +2528,17 @@ _hx509_query_clear(hx509_query *q) memset(q, 0, sizeof(*q)); } +/** + * Allocate an query controller. Free using hx509_query_free(). + * + * @param context A hx509 context. + * @param q return pointer to a hx509_query. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_cert + */ + int hx509_query_alloc(hx509_context context, hx509_query **q) { @@ -2065,6 +2548,17 @@ hx509_query_alloc(hx509_context context, hx509_query **q) return 0; } +/** + * Set match options for the hx509 query controller. + * + * @param q query controller. + * @param option options to control the query controller. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_cert + */ + void hx509_query_match_option(hx509_query *q, hx509_query_option option) { @@ -2087,6 +2581,19 @@ hx509_query_match_option(hx509_query *q, hx509_query_option option) } } +/** + * Set the issuer and serial number of match in the query + * controller. The function make copies of the isser and serial number. + * + * @param q a hx509 query controller + * @param issuer issuer to search for + * @param serialNumber the serialNumber of the issuer. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_cert + */ + int hx509_query_match_issuer_serial(hx509_query *q, const Name *issuer, @@ -2123,6 +2630,16 @@ hx509_query_match_issuer_serial(hx509_query *q, return 0; } +/** + * Set the query controller to match on a friendly name + * + * @param q a hx509 query controller. + * @param name a friendly name to match on + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_cert + */ int hx509_query_match_friendly_name(hx509_query *q, const char *name) @@ -2136,6 +2653,63 @@ hx509_query_match_friendly_name(hx509_query *q, const char *name) return 0; } +/** + * Set the query controller to require an one specific EKU (extended + * key usage). Any previous EKU matching is overwitten. If NULL is + * passed in as the eku, the EKU requirement is reset. + * + * @param q a hx509 query controller. + * @param eku an EKU to match on. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_cert + */ + +int +hx509_query_match_eku(hx509_query *q, const heim_oid *eku) +{ + int ret; + + if (eku == NULL) { + if (q->eku) { + der_free_oid(q->eku); + free(q->eku); + q->eku = NULL; + } + q->match &= ~HX509_QUERY_MATCH_EKU; + } else { + if (q->eku) { + der_free_oid(q->eku); + } else { + q->eku = calloc(1, sizeof(*q->eku)); + if (q->eku == NULL) + return ENOMEM; + } + ret = der_copy_oid(eku, q->eku); + if (ret) { + free(q->eku); + q->eku = NULL; + return ret; + } + q->match |= HX509_QUERY_MATCH_EKU; + } + return 0; +} + +/** + * Set the query controller to match using a specific match function. + * + * @param q a hx509 query controller. + * @param func function to use for matching, if the argument is NULL, + * the match function is removed. + * @param ctx context passed to the function. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_cert + */ + int hx509_query_match_cmp_func(hx509_query *q, int (*func)(void *, hx509_cert), @@ -2150,24 +2724,36 @@ hx509_query_match_cmp_func(hx509_query *q, return 0; } +/** + * Free the query controller. + * + * @param context A hx509 context. + * @param q a pointer to the query controller. + * + * @ingroup hx509_cert + */ void hx509_query_free(hx509_context context, hx509_query *q) { + if (q == NULL) + return; + if (q->serial) { der_free_heim_integer(q->serial); free(q->serial); - q->serial = NULL; } if (q->issuer_name) { free_Name(q->issuer_name); free(q->issuer_name); - q->issuer_name = NULL; } - if (q) { - free(q->friendlyname); - memset(q, 0, sizeof(*q)); + if (q->eku) { + der_free_oid(q->eku); + free(q->eku); } + if (q->friendlyname) + free(q->friendlyname); + memset(q, 0, sizeof(*q)); free(q); } @@ -2175,6 +2761,7 @@ int _hx509_query_match_cert(hx509_context context, const hx509_query *q, hx509_cert cert) { Certificate *c = _hx509_get_cert(cert); + int ret, diff; _hx509_query_statistic(context, 1, q); @@ -2190,17 +2777,20 @@ _hx509_query_match_cert(hx509_context context, const hx509_query *q, hx509_cert && der_heim_integer_cmp(&c->tbsCertificate.serialNumber, q->serial) != 0) return 0; - if ((q->match & HX509_QUERY_MATCH_ISSUER_NAME) - && _hx509_name_cmp(&c->tbsCertificate.issuer, q->issuer_name) != 0) - return 0; + if (q->match & HX509_QUERY_MATCH_ISSUER_NAME) { + ret = _hx509_name_cmp(&c->tbsCertificate.issuer, q->issuer_name, &diff); + if (ret || diff) + return 0; + } - if ((q->match & HX509_QUERY_MATCH_SUBJECT_NAME) - && _hx509_name_cmp(&c->tbsCertificate.subject, q->subject_name) != 0) - return 0; + if (q->match & HX509_QUERY_MATCH_SUBJECT_NAME) { + ret = _hx509_name_cmp(&c->tbsCertificate.subject, q->subject_name, &diff); + if (ret || diff) + return 0; + } if (q->match & HX509_QUERY_MATCH_SUBJECT_KEY_ID) { SubjectKeyIdentifier si; - int ret; ret = _hx509_find_extension_subject_key_id(c, &si); if (ret == 0) { @@ -2264,14 +2854,13 @@ _hx509_query_match_cert(hx509_context context, const hx509_query *q, hx509_cert return 0; } if (q->match & HX509_QUERY_MATCH_FUNCTION) { - int ret = (*q->cmp_func)(q->cmp_func_ctx, cert); + ret = (*q->cmp_func)(q->cmp_func_ctx, cert); if (ret != 0) return 0; } if (q->match & HX509_QUERY_MATCH_KEY_HASH_SHA1) { heim_octet_string os; - int ret; os.data = c->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data; os.length = @@ -2296,12 +2885,26 @@ _hx509_query_match_cert(hx509_context context, const hx509_query *q, hx509_cert return 0; } + /* If an EKU is required, check the cert for it. */ + if ((q->match & HX509_QUERY_MATCH_EKU) && + hx509_cert_check_eku(context, cert, q->eku, 0)) + return 0; + if (q->match & ~HX509_QUERY_MASK) return 0; return 1; } +/** + * Set a statistic file for the query statistics. + * + * @param context A hx509 context. + * @param fn statistics file name + * + * @ingroup hx509_cert + */ + void hx509_query_statistic_file(hx509_context context, const char *fn) { @@ -2362,6 +2965,16 @@ stat_sort(const void *a, const void *b) return be->stats - ae->stats; } +/** + * Unparse the statistics file and print the result on a FILE descriptor. + * + * @param context A hx509 context. + * @param printtype tyep to print + * @param out the FILE to write the data on. + * + * @ingroup hx509_cert + */ + void hx509_query_unparse_stats(hx509_context context, int printtype, FILE *out) { @@ -2435,6 +3048,20 @@ hx509_query_unparse_stats(hx509_context context, int printtype, FILE *out) multiqueries, totalqueries); } +/** + * Check the extended key usage on the hx509 certificate. + * + * @param context A hx509 context. + * @param cert A hx509 context. + * @param eku the EKU to check for + * @param allow_any_eku if the any EKU is set, allow that to be a + * substitute. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_cert + */ + int hx509_cert_check_eku(hx509_context context, hx509_cert cert, const heim_oid *eku, int allow_any_eku) @@ -2511,6 +3138,19 @@ _hx509_cert_get_eku(hx509_context context, return 0; } +/** + * Encodes the hx509 certificate as a DER encode binary. + * + * @param context A hx509 context. + * @param c the certificate to encode. + * @param os the encode certificate, set to NULL, 0 on case of + * error. Free the returned structure with hx509_xfree(). + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_cert + */ + int hx509_cert_binary(hx509_context context, hx509_cert c, heim_octet_string *os) { @@ -2522,8 +3162,11 @@ hx509_cert_binary(hx509_context context, hx509_cert c, heim_octet_string *os) ASN1_MALLOC_ENCODE(Certificate, os->data, os->length, _hx509_get_cert(c), &size, ret); - if (ret) + if (ret) { + os->data = NULL; + os->length = 0; return ret; + } if (os->length != size) _hx509_abort("internal ASN.1 encoder error"); @@ -2550,3 +3193,16 @@ _hx509_abort(const char *fmt, ...) abort(); } +/** + * Free a data element allocated in the library. + * + * @param ptr data to be freed. + * + * @ingroup hx509_misc + */ + +void +hx509_xfree(void *ptr) +{ + free(ptr); +} diff --git a/source4/heimdal/lib/hx509/cms.c b/source4/heimdal/lib/hx509/cms.c index 30f364060d..80bcaac6c9 100644 --- a/source4/heimdal/lib/hx509/cms.c +++ b/source4/heimdal/lib/hx509/cms.c @@ -32,11 +32,46 @@ */ #include "hx_locl.h" -RCSID("$Id: cms.c 21319 2007-06-25 19:46:52Z lha $"); +RCSID("$Id: cms.c 22327 2007-12-15 04:49:37Z lha $"); + +/** + * @page page_cms CMS/PKCS7 message functions. + * + * CMS is defined in RFC 3369 and is an continuation of the RSA Labs + * standard PKCS7. The basic messages in CMS is + * + * - SignedData + * Data signed with private key (RSA, DSA, ECDSA) or secret + * (symmetric) key + * - EnvelopedData + * Data encrypted with private key (RSA) + * - EncryptedData + * Data encrypted with secret (symmetric) key. + * - ContentInfo + * Wrapper structure including type and data. + * + * + * See the library functions here: @ref hx509_cms + */ #define ALLOC(X, N) (X) = calloc((N), sizeof(*(X))) #define ALLOC_SEQ(X, N) do { (X)->len = (N); ALLOC((X)->val, (N)); } while(0) +/** + * Wrap data and oid in a ContentInfo and encode it. + * + * @param oid type of the content. + * @param buf data to be wrapped. If a NULL pointer is passed in, the + * optional content field in the ContentInfo is not going be filled + * in. + * @param res the encoded buffer, the result should be freed with + * der_free_octet_string(). + * + * @return Returns an hx509 error code. + * + * @ingroup hx509_cms + */ + int hx509_cms_wrap_ContentInfo(const heim_oid *oid, const heim_octet_string *buf, @@ -52,18 +87,20 @@ hx509_cms_wrap_ContentInfo(const heim_oid *oid, ret = der_copy_oid(oid, &ci.contentType); if (ret) return ret; - ALLOC(ci.content, 1); - if (ci.content == NULL) { - free_ContentInfo(&ci); - return ENOMEM; - } - ci.content->data = malloc(buf->length); - if (ci.content->data == NULL) { - free_ContentInfo(&ci); - return ENOMEM; + if (buf) { + ALLOC(ci.content, 1); + if (ci.content == NULL) { + free_ContentInfo(&ci); + return ENOMEM; + } + ci.content->data = malloc(buf->length); + if (ci.content->data == NULL) { + free_ContentInfo(&ci); + return ENOMEM; + } + memcpy(ci.content->data, buf->data, buf->length); + ci.content->length = buf->length; } - memcpy(ci.content->data, buf->data, buf->length); - ci.content->length = buf->length; ASN1_MALLOC_ENCODE(ContentInfo, res->data, res->length, &ci, &size, ret); free_ContentInfo(&ci); @@ -75,6 +112,20 @@ hx509_cms_wrap_ContentInfo(const heim_oid *oid, return 0; } +/** + * Decode an ContentInfo and unwrap data and oid it. + * + * @param in the encoded buffer. + * @param oid type of the content. + * @param out data to be wrapped. + * @param have_data since the data is optional, this flags show dthe + * diffrence between no data and the zero length data. + * + * @return Returns an hx509 error code. + * + * @ingroup hx509_cms + */ + int hx509_cms_unwrap_ContentInfo(const heim_octet_string *in, heim_oid *oid, @@ -267,6 +318,27 @@ find_CMSIdentifier(hx509_context context, return 0; } +/** + * Decode and unencrypt EnvelopedData. + * + * Extract data and parameteres from from the EnvelopedData. Also + * supports using detached EnvelopedData. + * + * @param context A hx509 context. + * @param certs Certificate that can decrypt the EnvelopedData + * encryption key. + * @param flags HX509_CMS_UE flags to control the behavior. + * @param data pointer the structure the contains the DER/BER encoded + * EnvelopedData stucture. + * @param length length of the data that data point to. + * @param encryptedContent in case of detached signature, this + * contains the actual encrypted data, othersize its should be NULL. + * @param contentType output type oid, should be freed with der_free_oid(). + * @param content the data, free with der_free_octet_string(). + * + * @ingroup hx509_cms + */ + int hx509_cms_unenvelope(hx509_context context, hx509_certs certs, @@ -335,11 +407,6 @@ hx509_cms_unenvelope(hx509_context context, ri = &ed.recipientInfos.val[i]; - /* ret = search_keyset(ri, - * PRIVATE_KEY, - * ki->keyEncryptionAlgorithm.algorithm); - */ - ret = find_CMSIdentifier(context, &ri->rid, certs, &cert, HX509_QUERY_PRIVATE_KEY|findflags); if (ret) @@ -444,6 +511,29 @@ out: return ret; } +/** + * Encrypt end encode EnvelopedData. + * + * Encrypt and encode EnvelopedData. The data is encrypted with a + * random key and the the random key is encrypted with the + * certificates private key. This limits what private key type can be + * used to RSA. + * + * @param context A hx509 context. + * @param flags flags to control the behavior, no flags today + * @param cert Certificate to encrypt the EnvelopedData encryption key + * with. + * @param data pointer the data to encrypt. + * @param length length of the data that data point to. + * @param encryption_type Encryption cipher to use for the bulk data, + * use NULL to get default. + * @param contentType type of the data that is encrypted + * @param content the output of the function, + * free with der_free_octet_string(). + * + * @ingroup hx509_cms + */ + int hx509_cms_envelope_1(hx509_context context, int flags, @@ -637,13 +727,31 @@ find_attribute(const CMSAttributes *attr, const heim_oid *oid) return NULL; } +/** + * Decode SignedData and verify that the signature is correct. + * + * @param context A hx509 context. + * @param ctx a hx509 version context + * @param data + * @param length length of the data that data point to. + * @param signedContent + * @param pool certificate pool to build certificates paths. + * @param contentType free with der_free_oid() + * @param content the output of the function, free with + * der_free_octet_string(). + * @param signer_certs list of the cerficates used to sign this + * request, free with hx509_certs_free(). + * + * @ingroup hx509_cms + */ + int hx509_cms_verify_signed(hx509_context context, hx509_verify_ctx ctx, const void *data, size_t length, const heim_octet_string *signedContent, - hx509_certs store, + hx509_certs pool, heim_oid *contentType, heim_octet_string *content, hx509_certs *signer_certs) @@ -701,8 +809,8 @@ hx509_cms_verify_signed(hx509_context context, if (ret) goto out; - if (store) { - ret = hx509_certs_merge(context, certs, store); + if (pool) { + ret = hx509_certs_merge(context, certs, pool); if (ret) goto out; } @@ -946,6 +1054,29 @@ add_one_attribute(Attribute **attr, return 0; } +/** + * Decode SignedData and verify that the signature is correct. + * + * @param context A hx509 context. + * @param flags + * @param eContentType the type of the data. + * @param data data to sign + * @param length length of the data that data point to. + * @param digest_alg digest algorithm to use, use NULL to get the + * default or the peer determined algorithm. + * @param cert certificate to use for sign the data. + * @param peer info about the peer the message to send the message to, + * like what digest algorithm to use. + * @param anchors trust anchors that the client will use, used to + * polulate the certificates included in the message + * @param pool certificates to use in try to build the path to the + * trust anchors. + * @param signed_data the output of the function, free with + * der_free_octet_string(). + * + * @ingroup hx509_cms + */ + int hx509_cms_create_signed_1(hx509_context context, int flags, @@ -1050,7 +1181,7 @@ hx509_cms_create_signed_1(hx509_context context, } /* - * If its not pkcs7-data send signedAttributes + * If it isn't pkcs7-data send signedAttributes */ if (der_heim_oid_cmp(eContentType, oid_id_pkcs7_data()) != 0) { diff --git a/source4/heimdal/lib/hx509/crypto.c b/source4/heimdal/lib/hx509/crypto.c index d86300bd58..e0f00ad7b4 100644 --- a/source4/heimdal/lib/hx509/crypto.c +++ b/source4/heimdal/lib/hx509/crypto.c @@ -32,7 +32,7 @@ */ #include "hx_locl.h" -RCSID("$Id: crypto.c 21318 2007-06-25 19:46:32Z lha $"); +RCSID("$Id: crypto.c 22435 2008-01-14 20:53:56Z lha $"); struct hx509_crypto; @@ -64,6 +64,7 @@ struct hx509_private_key_ops { int (*generate_private_key)(hx509_context, struct hx509_generate_private_context *, hx509_private_key); + BIGNUM *(*get_internal)(hx509_context, hx509_private_key, const char *); int (*handle_alg)(const hx509_private_key, const AlgorithmIdentifier *, enum crypto_op_type); @@ -115,6 +116,9 @@ struct signature_alg { #define SIG_PUBLIC_SIG 0x200 #define SIG_SECRET 0x400 +#define RA_RSA_USES_DIGEST_INFO 0x1000000 + + int (*verify_signature)(hx509_context context, const struct signature_alg *, const Certificate *, @@ -248,43 +252,57 @@ rsa_verify_signature(hx509_context context, } if (retsize > tosize) _hx509_abort("internal rsa decryption failure: ret > tosize"); - ret = decode_DigestInfo(to, retsize, &di, &size); - free(to); - if (ret) { - goto out; - } - /* Check for extra data inside the sigature */ - if (size != retsize) { - ret = HX509_CRYPTO_SIG_INVALID_FORMAT; - hx509_set_error_string(context, 0, ret, "size from decryption mismatch"); - goto out; - } + if (sig_alg->flags & RA_RSA_USES_DIGEST_INFO) { - if (sig_alg->digest_oid && - der_heim_oid_cmp(&di.digestAlgorithm.algorithm, - (*sig_alg->digest_oid)()) != 0) - { - ret = HX509_CRYPTO_OID_MISMATCH; - hx509_set_error_string(context, 0, ret, "object identifier in RSA sig mismatch"); - goto out; - } + ret = decode_DigestInfo(to, retsize, &di, &size); + free(to); + if (ret) { + goto out; + } + + /* Check for extra data inside the sigature */ + if (size != retsize) { + ret = HX509_CRYPTO_SIG_INVALID_FORMAT; + hx509_set_error_string(context, 0, ret, "size from decryption mismatch"); + goto out; + } + + if (sig_alg->digest_oid && + der_heim_oid_cmp(&di.digestAlgorithm.algorithm, + (*sig_alg->digest_oid)()) != 0) + { + ret = HX509_CRYPTO_OID_MISMATCH; + hx509_set_error_string(context, 0, ret, "object identifier in RSA sig mismatch"); + goto out; + } + + /* verify that the parameters are NULL or the NULL-type */ + if (di.digestAlgorithm.parameters != NULL && + (di.digestAlgorithm.parameters->length != 2 || + memcmp(di.digestAlgorithm.parameters->data, "\x05\x00", 2) != 0)) + { + ret = HX509_CRYPTO_SIG_INVALID_FORMAT; + hx509_set_error_string(context, 0, ret, "Extra parameters inside RSA signature"); + goto out; + } - /* verify that the parameters are NULL or the NULL-type */ - if (di.digestAlgorithm.parameters != NULL && - (di.digestAlgorithm.parameters->length != 2 || - memcmp(di.digestAlgorithm.parameters->data, "\x05\x00", 2) != 0)) - { - ret = HX509_CRYPTO_SIG_INVALID_FORMAT; - hx509_set_error_string(context, 0, ret, "Extra parameters inside RSA signature"); - goto out; + ret = _hx509_verify_signature(context, + NULL, + &di.digestAlgorithm, + data, + &di.digest); + } else { + if (retsize != data->length || + memcmp(to, data->data, retsize) != 0) + { + ret = HX509_CRYPTO_SIG_INVALID_FORMAT; + hx509_set_error_string(context, 0, ret, "RSA Signature incorrect"); + goto out; + } + free(to); } - ret = _hx509_verify_signature(context, - NULL, - &di.digestAlgorithm, - data, - &di.digest); out: free_DigestInfo(&di); RSA_free(rsa); @@ -303,7 +321,6 @@ rsa_create_signature(hx509_context context, const AlgorithmIdentifier *digest_alg; heim_octet_string indata; const heim_oid *sig_oid; - DigestInfo di; size_t size; int ret; @@ -324,6 +341,8 @@ rsa_create_signature(hx509_context context, digest_alg = hx509_signature_sha1(); } else if (der_heim_oid_cmp(sig_oid, oid_id_pkcs1_rsaEncryption()) == 0) { digest_alg = hx509_signature_sha1(); + } else if (der_heim_oid_cmp(sig_oid, oid_id_heim_rsa_pkcs1_x509()) == 0) { + digest_alg = NULL; } else return HX509_ALG_NOT_SUPP; @@ -335,29 +354,34 @@ rsa_create_signature(hx509_context context, } } - memset(&di, 0, sizeof(di)); + if (digest_alg) { + DigestInfo di; + memset(&di, 0, sizeof(di)); - ret = _hx509_create_signature(context, - NULL, - digest_alg, - data, - &di.digestAlgorithm, - &di.digest); - if (ret) - return ret; - ASN1_MALLOC_ENCODE(DigestInfo, - indata.data, - indata.length, - &di, - &size, - ret); - free_DigestInfo(&di); - if (ret) { - hx509_set_error_string(context, 0, ret, "out of memory"); - return ret; + ret = _hx509_create_signature(context, + NULL, + digest_alg, + data, + &di.digestAlgorithm, + &di.digest); + if (ret) + return ret; + ASN1_MALLOC_ENCODE(DigestInfo, + indata.data, + indata.length, + &di, + &size, + ret); + free_DigestInfo(&di); + if (ret) { + hx509_set_error_string(context, 0, ret, "out of memory"); + return ret; + } + if (indata.length != size) + _hx509_abort("internal ASN.1 encoder error"); + } else { + indata = *data; } - if (indata.length != size) - _hx509_abort("internal ASN.1 encoder error"); sig->length = RSA_size(signer->private_key.rsa); sig->data = malloc(sig->length); @@ -371,7 +395,8 @@ rsa_create_signature(hx509_context context, sig->data, signer->private_key.rsa, RSA_PKCS1_PADDING); - der_free_octet_string(&indata); + if (indata.data != data->data) + der_free_octet_string(&indata); if (ret <= 0) { ret = HX509_CMS_FAILED_CREATE_SIGATURE; hx509_set_error_string(context, 0, ret, @@ -517,6 +542,18 @@ rsa_private_key_export(hx509_context context, return 0; } +static BIGNUM * +rsa_get_internal(hx509_context context, hx509_private_key key, const char *type) +{ + if (strcasecmp(type, "rsa-modulus") == 0) { + return BN_dup(key->private_key.rsa->n); + } else if (strcasecmp(type, "rsa-exponent") == 0) { + return BN_dup(key->private_key.rsa->e); + } else + return NULL; +} + + static hx509_private_key_ops rsa_private_key_ops = { "RSA PRIVATE KEY", @@ -524,7 +561,8 @@ static hx509_private_key_ops rsa_private_key_ops = { rsa_private_key2SPKI, rsa_private_key_export, rsa_private_key_import, - rsa_generate_private_key + rsa_generate_private_key, + rsa_get_internal }; @@ -833,13 +871,24 @@ md2_verify_signature(hx509_context context, return 0; } +static const struct signature_alg heim_rsa_pkcs1_x509 = { + "rsa-pkcs1-x509", + oid_id_heim_rsa_pkcs1_x509, + hx509_signature_rsa_pkcs1_x509, + oid_id_pkcs1_rsaEncryption, + NULL, + PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG, + rsa_verify_signature, + rsa_create_signature +}; + static const struct signature_alg pkcs1_rsa_sha1_alg = { "rsa", oid_id_pkcs1_rsaEncryption, hx509_signature_rsa_with_sha1, oid_id_pkcs1_rsaEncryption, NULL, - PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG, + PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG, rsa_verify_signature, rsa_create_signature }; @@ -850,7 +899,7 @@ static const struct signature_alg rsa_with_sha256_alg = { hx509_signature_rsa_with_sha256, oid_id_pkcs1_rsaEncryption, oid_id_sha256, - PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG, + PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG, rsa_verify_signature, rsa_create_signature }; @@ -861,7 +910,7 @@ static const struct signature_alg rsa_with_sha1_alg = { hx509_signature_rsa_with_sha1, oid_id_pkcs1_rsaEncryption, oid_id_secsig_sha_1, - PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG, + PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG, rsa_verify_signature, rsa_create_signature }; @@ -872,7 +921,7 @@ static const struct signature_alg rsa_with_md5_alg = { hx509_signature_rsa_with_md5, oid_id_pkcs1_rsaEncryption, oid_id_rsa_digest_md5, - PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG, + PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG, rsa_verify_signature, rsa_create_signature }; @@ -883,7 +932,7 @@ static const struct signature_alg rsa_with_md2_alg = { hx509_signature_rsa_with_md2, oid_id_pkcs1_rsaEncryption, oid_id_rsa_digest_md2, - PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG, + PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG, rsa_verify_signature, rsa_create_signature }; @@ -952,7 +1001,7 @@ static const struct signature_alg *sig_algs[] = { &pkcs1_rsa_sha1_alg, &rsa_with_md5_alg, &rsa_with_md2_alg, - &pkcs1_rsa_sha1_alg, + &heim_rsa_pkcs1_x509, &dsa_sha1_alg, &sha256_alg, &sha1_alg, @@ -1423,6 +1472,11 @@ const AlgorithmIdentifier _hx509_signature_rsa_data = { { 7, rk_UNCONST(rsa_oid) }, NULL }; +static const unsigned rsa_pkcs1_x509_oid[] ={ 1, 2, 752, 43, 16, 1 }; +const AlgorithmIdentifier _hx509_signature_rsa_pkcs1_x509_data = { + { 6, rk_UNCONST(rsa_pkcs1_x509_oid) }, NULL +}; + static const unsigned des_rsdi_ede3_cbc_oid[] ={ 1, 2, 840, 113549, 3, 7 }; const AlgorithmIdentifier _hx509_des_rsdi_ede3_cbc_oid = { { 6, rk_UNCONST(des_rsdi_ede3_cbc_oid) }, NULL @@ -1491,6 +1545,10 @@ hx509_signature_rsa(void) { return &_hx509_signature_rsa_data; } const AlgorithmIdentifier * +hx509_signature_rsa_pkcs1_x509(void) +{ return &_hx509_signature_rsa_pkcs1_x509_data; } + +const AlgorithmIdentifier * hx509_crypto_des_rsdi_ede3_cbc(void) { return &_hx509_des_rsdi_ede3_cbc_oid; } @@ -1597,6 +1655,16 @@ _hx509_private_key_exportable(hx509_private_key key) return 1; } +BIGNUM * +_hx509_private_key_get_internal(hx509_context context, + hx509_private_key key, + const char *type) +{ + if (key->ops->get_internal == NULL) + return NULL; + return (*key->ops->get_internal)(context, key, type); +} + int _hx509_private_key_export(hx509_context context, const hx509_private_key key, diff --git a/source4/heimdal/lib/hx509/env.c b/source4/heimdal/lib/hx509/env.c index 4cb2f9f4b1..f868c22488 100644 --- a/source4/heimdal/lib/hx509/env.c +++ b/source4/heimdal/lib/hx509/env.c @@ -32,7 +32,13 @@ */ #include "hx_locl.h" -RCSID("$Id: env.c 19878 2007-01-13 00:58:39Z lha $"); +RCSID("$Id: env.c 22349 2007-12-26 19:32:49Z lha $"); + +/** + * @page page_env Hx509 enviroment functions + * + * See the library functions here: @ref hx509_env + */ struct hx509_env { struct { @@ -42,6 +48,17 @@ struct hx509_env { size_t len; }; +/** + * Allocate a new hx509_env container object. + * + * @param context A hx509 context. + * @param env return a hx509_env structure, free with hx509_env_free(). + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_env + */ + int hx509_env_init(hx509_context context, hx509_env *env) { @@ -53,6 +70,19 @@ hx509_env_init(hx509_context context, hx509_env *env) return 0; } +/** + * Add a new key/value pair to the hx509_env. + * + * @param context A hx509 context. + * @param env enviroment to add the enviroment variable too. + * @param key key to add + * @param value value to add + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_env + */ + int hx509_env_add(hx509_context context, hx509_env env, const char *key, const char *value) @@ -80,6 +110,19 @@ hx509_env_add(hx509_context context, hx509_env env, return 0; } +/** + * Search the hx509_env for a key. + * + * @param context A hx509 context. + * @param env enviroment to add the enviroment variable too. + * @param key key to search for. + * @param len length of key. + * + * @return the value if the key is found, NULL otherwise. + * + * @ingroup hx509_env + */ + const char * hx509_env_lfind(hx509_context context, hx509_env env, const char *key, size_t len) @@ -94,6 +137,13 @@ hx509_env_lfind(hx509_context context, hx509_env env, return NULL; } +/** + * Free an hx509_env enviroment context. + * + * @param env the enviroment to free. + * + * @ingroup hx509_env + */ void hx509_env_free(hx509_env *env) diff --git a/source4/heimdal/lib/hx509/error.c b/source4/heimdal/lib/hx509/error.c index 9f3a014873..25119ed288 100644 --- a/source4/heimdal/lib/hx509/error.c +++ b/source4/heimdal/lib/hx509/error.c @@ -32,7 +32,13 @@ */ #include "hx_locl.h" -RCSID("$Id: error.c 20912 2007-06-05 03:53:52Z lha $"); +RCSID("$Id: error.c 22332 2007-12-17 01:03:22Z lha $"); + +/** + * @page page_error Hx509 error reporting functions + * + * See the library functions here: @ref hx509_error + */ struct hx509_error_data { hx509_error next; @@ -51,6 +57,14 @@ free_error_string(hx509_error msg) } } +/** + * Resets the error strings the hx509 context. + * + * @param context A hx509 context. + * + * @ingroup hx509_error + */ + void hx509_clear_error_string(hx509_context context) { @@ -58,6 +72,20 @@ hx509_clear_error_string(hx509_context context) context->error = NULL; } +/** + * Add an error message to the hx509 context. + * + * @param context A hx509 context. + * @param flags + * - HX509_ERROR_APPEND appends the error string to the old messages + (code is updated). + * @param code error code related to error message + * @param fmt error message format + * @param ap arguments to error message format + * + * @ingroup hx509_error + */ + void hx509_set_error_stringv(hx509_context context, int flags, int code, const char *fmt, va_list ap) @@ -86,6 +114,20 @@ hx509_set_error_stringv(hx509_context context, int flags, int code, } } +/** + * See hx509_set_error_stringv(). + * + * @param context A hx509 context. + * @param flags + * - HX509_ERROR_APPEND appends the error string to the old messages + (code is updated). + * @param code error code related to error message + * @param fmt error message format + * @param ... arguments to error message format + * + * @ingroup hx509_error + */ + void hx509_set_error_string(hx509_context context, int flags, int code, const char *fmt, ...) @@ -97,6 +139,17 @@ hx509_set_error_string(hx509_context context, int flags, int code, va_end(ap); } +/** + * Get an error string from context associated with error_code. + * + * @param context A hx509 context. + * @param error_code Get error message for this error code. + * + * @return error string, free with hx509_free_error_string(). + * + * @ingroup hx509_error + */ + char * hx509_get_error_string(hx509_context context, int error_code) { @@ -125,6 +178,32 @@ hx509_get_error_string(hx509_context context, int error_code) return rk_strpoolcollect(p); } +/** + * Free error string returned by hx509_get_error_string(). + * + * @param str error string to free. + * + * @ingroup hx509_error + */ + +void +hx509_free_error_string(char *str) +{ + free(str); +} + +/** + * Print error message and fatally exit from error code + * + * @param context A hx509 context. + * @param exit_code exit() code from process. + * @param error_code Error code for the reason to exit. + * @param fmt format string with the exit message. + * @param ... argument to format string. + * + * @ingroup hx509_error + */ + void hx509_err(hx509_context context, int exit_code, int error_code, const char *fmt, ...) diff --git a/source4/heimdal/lib/hx509/hx509-private.h b/source4/heimdal/lib/hx509/hx509-private.h index acbc3218c6..be36c07421 100644 --- a/source4/heimdal/lib/hx509/hx509-private.h +++ b/source4/heimdal/lib/hx509/hx509-private.h @@ -9,6 +9,11 @@ #endif int +_hx509_AlgorithmIdentifier_cmp ( + const AlgorithmIdentifier */*p*/, + const AlgorithmIdentifier */*q*/); + +int _hx509_Certificate_cmp ( const Certificate */*p*/, const Certificate */*q*/); @@ -269,12 +274,14 @@ _hx509_match_keys ( int _hx509_name_cmp ( const Name */*n1*/, - const Name */*n2*/); + const Name */*n2*/, + int */*c*/); int _hx509_name_ds_cmp ( const DirectoryString */*ds1*/, - const DirectoryString */*ds2*/); + const DirectoryString */*ds2*/, + int */*diff*/); int _hx509_name_from_Name ( @@ -314,6 +321,14 @@ _hx509_pbe_decrypt ( const heim_octet_string */*econtent*/, heim_octet_string */*content*/); +int +_hx509_pbe_encrypt ( + hx509_context /*context*/, + hx509_lock /*lock*/, + const AlgorithmIdentifier */*ai*/, + const heim_octet_string */*content*/, + heim_octet_string */*econtent*/); + void _hx509_pi_printf ( int (*/*func*/)(void *, const char *), @@ -344,6 +359,12 @@ _hx509_private_key_exportable (hx509_private_key /*key*/); int _hx509_private_key_free (hx509_private_key */*key*/); +BIGNUM * +_hx509_private_key_get_internal ( + hx509_context /*context*/, + hx509_private_key /*key*/, + const char */*type*/); + int _hx509_private_key_init ( hx509_private_key */*key*/, @@ -415,11 +436,35 @@ void _hx509_request_free (hx509_request */*req*/); int +_hx509_request_get_SubjectPublicKeyInfo ( + hx509_context /*context*/, + hx509_request /*req*/, + SubjectPublicKeyInfo */*key*/); + +int +_hx509_request_get_name ( + hx509_context /*context*/, + hx509_request /*req*/, + hx509_name */*name*/); + +int _hx509_request_init ( hx509_context /*context*/, hx509_request */*req*/); int +_hx509_request_parse ( + hx509_context /*context*/, + const char */*path*/, + hx509_request */*req*/); + +int +_hx509_request_print ( + hx509_context /*context*/, + hx509_request /*req*/, + FILE */*f*/); + +int _hx509_request_set_SubjectPublicKeyInfo ( hx509_context /*context*/, hx509_request /*req*/, @@ -438,6 +483,9 @@ _hx509_request_to_pkcs10 ( const hx509_private_key /*signer*/, heim_octet_string */*request*/); +hx509_revoke_ctx +_hx509_revoke_ref (hx509_revoke_ctx /*ctx*/); + int _hx509_set_cert_attribute ( hx509_context /*context*/, diff --git a/source4/heimdal/lib/hx509/hx509-protos.h b/source4/heimdal/lib/hx509/hx509-protos.h index 71fb29d59d..3e297424cc 100644 --- a/source4/heimdal/lib/hx509/hx509-protos.h +++ b/source4/heimdal/lib/hx509/hx509-protos.h @@ -183,6 +183,7 @@ hx509_cert_cmp ( int hx509_cert_find_subjectAltName_otherName ( + hx509_context /*context*/, hx509_cert /*cert*/, const heim_oid */*oid*/, hx509_octet_string_list */*list*/); @@ -192,9 +193,16 @@ hx509_cert_free (hx509_cert /*cert*/); int hx509_cert_get_SPKI ( + hx509_context /*context*/, hx509_cert /*p*/, SubjectPublicKeyInfo */*spki*/); +int +hx509_cert_get_SPKI_AlgorithmIdentifier ( + hx509_context /*context*/, + hx509_cert /*p*/, + AlgorithmIdentifier */*alg*/); + hx509_cert_attribute hx509_cert_get_attribute ( hx509_cert /*cert*/, @@ -231,6 +239,9 @@ hx509_cert_get_subject ( hx509_name */*name*/); int +hx509_cert_have_private_key (hx509_cert /*p*/); + +int hx509_cert_init ( hx509_context /*context*/, const Certificate */*c*/, @@ -305,7 +316,7 @@ int hx509_certs_iter ( hx509_context /*context*/, hx509_certs /*certs*/, - int (*/*fn*/)(hx509_context, void *, hx509_cert), + int (*/*func*/)(hx509_context, void *, hx509_cert), void */*ctx*/); int @@ -402,7 +413,7 @@ hx509_cms_verify_signed ( const void */*data*/, size_t /*length*/, const heim_octet_string */*signedContent*/, - hx509_certs /*store*/, + hx509_certs /*pool*/, heim_oid */*contentType*/, heim_octet_string */*content*/, hx509_certs */*signer_certs*/); @@ -581,6 +592,9 @@ hx509_err ( ...); void +hx509_free_error_string (char */*str*/); + +void hx509_free_octet_string_list (hx509_octet_string_list */*list*/); int @@ -652,6 +666,11 @@ hx509_lock_set_prompter ( void */*data*/); int +hx509_name_binary ( + const hx509_name /*name*/, + heim_octet_string */*os*/); + +int hx509_name_cmp ( hx509_name /*n1*/, hx509_name /*n2*/); @@ -685,12 +704,6 @@ hx509_name_to_Name ( Name */*to*/); int -hx509_name_to_der_name ( - const hx509_name /*name*/, - void **/*data*/, - size_t */*length*/); - -int hx509_name_to_string ( const hx509_name /*name*/, char **/*str*/); @@ -783,13 +796,6 @@ hx509_pem_write ( size_t /*size*/); void -hx509_print_func ( - hx509_vprint_func /*func*/, - void */*ctx*/, - const char */*fmt*/, - ...); - -void hx509_print_stdout ( void */*ctx*/, const char */*fmt*/, @@ -815,6 +821,11 @@ hx509_query_match_cmp_func ( void */*ctx*/); int +hx509_query_match_eku ( + hx509_query */*q*/, + const heim_oid */*eku*/); + +int hx509_query_match_friendly_name ( hx509_query */*q*/, const char */*name*/); @@ -902,6 +913,9 @@ const AlgorithmIdentifier * hx509_signature_rsa (void); const AlgorithmIdentifier * +hx509_signature_rsa_pkcs1_x509 (void); + +const AlgorithmIdentifier * hx509_signature_rsa_with_md2 (void); const AlgorithmIdentifier * @@ -1030,6 +1044,9 @@ hx509_verify_signature ( const heim_octet_string */*data*/, const heim_octet_string */*sig*/); +void +hx509_xfree (void */*ptr*/); + #ifdef __cplusplus } #endif diff --git a/source4/heimdal/lib/hx509/hx509.h b/source4/heimdal/lib/hx509/hx509.h index 2f22cedfbc..be02f63474 100644 --- a/source4/heimdal/lib/hx509/hx509.h +++ b/source4/heimdal/lib/hx509/hx509.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. */ -/* $Id: hx509.h 21310 2007-06-25 18:26:06Z lha $ */ +/* $Id: hx509.h 22464 2008-01-16 14:24:50Z lha $ */ typedef struct hx509_cert_attribute_data *hx509_cert_attribute; typedef struct hx509_cert_data *hx509_cert; @@ -56,6 +56,10 @@ typedef struct hx509_crl *hx509_crl; typedef void (*hx509_vprint_func)(void *, const char *, va_list); enum { + HX509_VHN_F_ALLOW_NO_MATCH = 1 +}; + +enum { HX509_VALIDATE_F_VALIDATE = 1, HX509_VALIDATE_F_VERBOSE = 2 }; @@ -107,6 +111,7 @@ typedef enum { /* flags to hx509_certs_init */ #define HX509_CERTS_CREATE 0x01 +#define HX509_CERTS_UNPROTECT_ALL 0x02 /* flags to hx509_set_error_string */ #define HX509_ERROR_APPEND 0x01 diff --git a/source4/heimdal/lib/hx509/hx509_err.et b/source4/heimdal/lib/hx509/hx509_err.et index 90f3b3d907..8fc5cb8f2f 100644 --- a/source4/heimdal/lib/hx509/hx509_err.et +++ b/source4/heimdal/lib/hx509/hx509_err.et @@ -3,7 +3,7 @@ # # This might look like a com_err file, but is not # -id "$Id: hx509_err.et 20807 2007-06-03 03:11:20Z lha $" +id "$Id: hx509_err.et 22329 2007-12-15 05:13:14Z lha $" error_table hx prefix HX509 @@ -72,7 +72,7 @@ prefix HX509 error_code CRL_USED_BEFORE_TIME, "CRL used before it became valid" error_code CRL_USED_AFTER_TIME, "CRL used after it became invalid" error_code CRL_INVALID_FORMAT, "CRL have invalid format" -error_code CRL_CERT_REVOKED, "Certificate is included in CRL" +error_code CERT_REVOKED, "Certificate is revoked" error_code REVOKE_STATUS_MISSING, "No revoke status found for certificates" error_code CRL_UNKNOWN_EXTENSION, "Unknown extension" error_code REVOKE_WRONG_DATA, "Got wrong CRL/OCSP data from server" diff --git a/source4/heimdal/lib/hx509/hx_locl.h b/source4/heimdal/lib/hx509/hx_locl.h index 145bfcc006..6d89167bfc 100644 --- a/source4/heimdal/lib/hx509/hx_locl.h +++ b/source4/heimdal/lib/hx509/hx_locl.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. */ -/* $Id: hx_locl.h 21083 2007-06-13 02:11:19Z lha $ */ +/* $Id: hx_locl.h 22538 2008-01-27 13:05:47Z lha $ */ #ifdef HAVE_CONFIG_H #include <config.h> @@ -128,7 +128,8 @@ struct hx509_query_data { #define HX509_QUERY_MATCH_FUNCTION 0x080000 #define HX509_QUERY_MATCH_KEY_HASH_SHA1 0x100000 #define HX509_QUERY_MATCH_TIME 0x200000 -#define HX509_QUERY_MASK 0x3fffff +#define HX509_QUERY_MATCH_EKU 0x400000 +#define HX509_QUERY_MASK 0x7fffff Certificate *subject; Certificate *certificate; heim_integer *serial; @@ -142,6 +143,7 @@ struct hx509_query_data { void *cmp_func_ctx; heim_octet_string *keyhash_sha1; time_t timenow; + heim_oid *eku; }; struct hx509_keyset_ops { diff --git a/source4/heimdal/lib/hx509/keyset.c b/source4/heimdal/lib/hx509/keyset.c index 7da5705a80..2fcff7b03b 100644 --- a/source4/heimdal/lib/hx509/keyset.c +++ b/source4/heimdal/lib/hx509/keyset.c @@ -32,7 +32,31 @@ */ #include "hx_locl.h" -RCSID("$Id: keyset.c 21140 2007-06-18 21:24:19Z lha $"); +RCSID("$Id: keyset.c 22466 2008-01-16 14:26:35Z lha $"); + +/** + * @page page_keyset Certificate store operations + * + * Type of certificates store: + * - MEMORY + * In memory based format. Doesnt support storing. + * - FILE + * FILE supports raw DER certicates and PEM certicates. When PEM is + * used the file can contain may certificates and match private + * keys. Support storing the certificates. DER format only supports + * on certificate and no private key. + * - PEM-FILE + * Same as FILE, defaulting to PEM encoded certificates. + * - PEM-FILE + * Same as FILE, defaulting to DER encoded certificates. + * - PKCS11 + * - PKCS12 + * - DIR + * - KEYCHAIN + * Apple Mac OS X KeyChain backed keychain object. + * + * See the library functions here: @ref hx509_keyset + */ struct hx509_certs_data { int ref; @@ -69,6 +93,22 @@ _hx509_ks_register(hx509_context context, struct hx509_keyset_ops *ops) context->ks_num_ops++; } +/** + * Open or creates a new hx509 certificate store. + * + * @param context A hx509 context + * @param name name of the store, format is TYPE:type-specific-string, + * if NULL is used the MEMORY store is used. + * @param flags list of flags: + * - HX509_CERTS_CREATE create a new keystore of the specific TYPE. + * - HX509_CERTS_UNPROTECT_ALL fails if any private key failed to be extracted. + * @param lock a lock that unlocks the certificates store, use NULL to + * select no password/certifictes/prompt lock (see @ref page_lock). + * @param certs return pointer, free with hx509_certs_free(). + * + * @ingroup hx509_keyset + */ + int hx509_certs_init(hx509_context context, const char *name, int flags, @@ -125,6 +165,21 @@ hx509_certs_init(hx509_context context, return 0; } +/** + * Write the certificate store to stable storage. + * + * @param context A hx509 context. + * @param certs a certificate store to store. + * @param flags currently unused, use 0. + * @param lock a lock that unlocks the certificates store, use NULL to + * select no password/certifictes/prompt lock (see @ref page_lock). + * + * @return Returns an hx509 error code. HX509_UNSUPPORTED_OPERATION if + * the certificate store doesn't support the store operation. + * + * @ingroup hx509_keyset + */ + int hx509_certs_store(hx509_context context, hx509_certs certs, @@ -132,11 +187,11 @@ hx509_certs_store(hx509_context context, hx509_lock lock) { if (certs->ops->store == NULL) { - hx509_set_error_string(context, 0, EINVAL, + hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION, "keystore if type %s doesn't support " "store operation", certs->ops->name); - return EINVAL; + return HX509_UNSUPPORTED_OPERATION; } return (*certs->ops->store)(context, certs, certs->ops_data, flags, lock); @@ -146,6 +201,8 @@ hx509_certs_store(hx509_context context, hx509_certs _hx509_certs_ref(hx509_certs certs) { + if (certs == NULL) + return NULL; if (certs->ref <= 0) _hx509_abort("certs refcount <= 0"); certs->ref++; @@ -154,6 +211,14 @@ _hx509_certs_ref(hx509_certs certs) return certs; } +/** + * Free a certificate store. + * + * @param certs certificate store to free. + * + * @ingroup hx509_keyset + */ + void hx509_certs_free(hx509_certs *certs) { @@ -169,6 +234,21 @@ hx509_certs_free(hx509_certs *certs) } } +/** + * Start the integration + * + * @param context a hx509 context. + * @param certs certificate store to iterate over + * @param cursor cursor that will keep track of progress, free with + * hx509_certs_end_seq(). + * + * @return Returns an hx509 error code. HX509_UNSUPPORTED_OPERATION is + * returned if the certificate store doesn't support the iteration + * operation. + * + * @ingroup hx509_keyset + */ + int hx509_certs_start_seq(hx509_context context, hx509_certs certs, @@ -177,10 +257,10 @@ hx509_certs_start_seq(hx509_context context, int ret; if (certs->ops->iter_start == NULL) { - hx509_set_error_string(context, 0, ENOENT, + hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION, "Keyset type %s doesn't support iteration", certs->ops->name); - return ENOENT; + return HX509_UNSUPPORTED_OPERATION; } ret = (*certs->ops->iter_start)(context, certs, certs->ops_data, cursor); @@ -190,6 +270,21 @@ hx509_certs_start_seq(hx509_context context, return 0; } +/** + * Get next ceritificate from the certificate keystore pointed out by + * cursor. + * + * @param context a hx509 context. + * @param certs certificate store to iterate over. + * @param cursor cursor that keeps track of progress. + * @param cert return certificate next in store, NULL if the store + * contains no more certificates. Free with hx509_cert_free(). + * + * @return Returns an hx509 error code. + * + * @ingroup hx509_keyset + */ + int hx509_certs_next_cert(hx509_context context, hx509_certs certs, @@ -200,6 +295,18 @@ hx509_certs_next_cert(hx509_context context, return (*certs->ops->iter)(context, certs, certs->ops_data, cursor, cert); } +/** + * End the iteration over certificates. + * + * @param context a hx509 context. + * @param certs certificate store to iterate over. + * @param cursor cursor that will keep track of progress, freed. + * + * @return Returns an hx509 error code. + * + * @ingroup hx509_keyset + */ + int hx509_certs_end_seq(hx509_context context, hx509_certs certs, @@ -209,11 +316,26 @@ hx509_certs_end_seq(hx509_context context, return 0; } +/** + * Iterate over all certificates in a keystore and call an function + * for each fo them. + * + * @param context a hx509 context. + * @param certs certificate store to iterate over. + * @param func function to call for each certificate. The function + * should return non-zero to abort the iteration, that value is passed + * back to te caller of hx509_certs_iter(). + * @param ctx context variable that will passed to the function. + * + * @return Returns an hx509 error code. + * + * @ingroup hx509_keyset + */ int hx509_certs_iter(hx509_context context, hx509_certs certs, - int (*fn)(hx509_context, void *, hx509_cert), + int (*func)(hx509_context, void *, hx509_cert), void *ctx) { hx509_cursor cursor; @@ -232,7 +354,7 @@ hx509_certs_iter(hx509_context context, ret = 0; break; } - ret = (*fn)(context, ctx, c); + ret = (*func)(context, ctx, c); hx509_cert_free(c); if (ret) break; @@ -243,6 +365,20 @@ hx509_certs_iter(hx509_context context, return ret; } + +/** + * Function to use to hx509_certs_iter() as a function argument, the + * ctx variable to hx509_certs_iter() should be a FILE file descriptor. + * + * @param context a hx509 context. + * @param ctx used by hx509_certs_iter(). + * @param c a certificate + * + * @return Returns an hx509 error code. + * + * @ingroup hx509_keyset + */ + int hx509_ci_print_names(hx509_context context, void *ctx, hx509_cert c) { @@ -264,10 +400,20 @@ hx509_ci_print_names(hx509_context context, void *ctx, hx509_cert c) return 0; } -/* - * The receiving keyset `certsī will either increase reference counter - * of the `certī or make a deep copy, either way, the caller needs to - * free the `certī itself. +/** + * Add a certificate to the certificiate store. + * + * The receiving keyset certs will either increase reference counter + * of the cert or make a deep copy, either way, the caller needs to + * free the cert itself. + * + * @param context a hx509 context. + * @param certs certificate store to add the certificate to. + * @param cert certificate to add. + * + * @return Returns an hx509 error code. + * + * @ingroup hx509_keyset */ int @@ -283,6 +429,20 @@ hx509_certs_add(hx509_context context, hx509_certs certs, hx509_cert cert) return (*certs->ops->add)(context, certs, certs->ops_data, cert); } +/** + * Find a certificate matching the query. + * + * @param context a hx509 context. + * @param certs certificate store to search. + * @param q query allocated with @ref hx509_query functions. + * @param r return certificate (or NULL on error), should be freed + * with hx509_cert_free(). + * + * @return Returns an hx509 error code. + * + * @ingroup hx509_keyset + */ + int hx509_certs_find(hx509_context context, hx509_certs certs, @@ -335,6 +495,19 @@ certs_merge_func(hx509_context context, void *ctx, hx509_cert c) return hx509_certs_add(context, (hx509_certs)ctx, c); } +/** + * Merge a certificate store into another. The from store is keep + * intact. + * + * @param context a hx509 context. + * @param to the store to merge into. + * @param from the store to copy the object from. + * + * @return Returns an hx509 error code. + * + * @ingroup hx509_keyset + */ + int hx509_certs_merge(hx509_context context, hx509_certs to, hx509_certs from) { @@ -343,6 +516,21 @@ hx509_certs_merge(hx509_context context, hx509_certs to, hx509_certs from) return hx509_certs_iter(context, from, certs_merge_func, to); } +/** + * Same a hx509_certs_merge() but use a lock and name to describe the + * from source. + * + * @param context a hx509 context. + * @param to the store to merge into. + * @param lock a lock that unlocks the certificates store, use NULL to + * select no password/certifictes/prompt lock (see @ref page_lock). + * @param name name of the source store + * + * @return Returns an hx509 error code. + * + * @ingroup hx509_keyset + */ + int hx509_certs_append(hx509_context context, hx509_certs to, @@ -360,6 +548,18 @@ hx509_certs_append(hx509_context context, return ret; } +/** + * Get one random certificate from the certificate store. + * + * @param context a hx509 context. + * @param certs a certificate store to get the certificate from. + * @param c return certificate, should be freed with hx509_cert_free(). + * + * @return Returns an hx509 error code. + * + * @ingroup hx509_keyset + */ + int hx509_get_one_cert(hx509_context context, hx509_certs certs, hx509_cert *c) { @@ -388,6 +588,21 @@ certs_info_stdio(void *ctx, const char *str) return 0; } +/** + * Print some info about the certificate store. + * + * @param context a hx509 context. + * @param certs certificate store to print information about. + * @param func function that will get each line of the information, if + * NULL is used the data is printed on a FILE descriptor that should + * be passed in ctx, if ctx also is NULL, stdout is used. + * @param ctx parameter to func. + * + * @return Returns an hx509 error code. + * + * @ingroup hx509_keyset + */ + int hx509_certs_info(hx509_context context, hx509_certs certs, diff --git a/source4/heimdal/lib/hx509/ks_file.c b/source4/heimdal/lib/hx509/ks_file.c index 269afd03b1..87b97af401 100644 --- a/source4/heimdal/lib/hx509/ks_file.c +++ b/source4/heimdal/lib/hx509/ks_file.c @@ -32,7 +32,7 @@ */ #include "hx_locl.h" -RCSID("$Id: ks_file.c 21314 2007-06-25 18:45:07Z lha $"); +RCSID("$Id: ks_file.c 22465 2008-01-16 14:25:24Z lha $"); typedef enum { USE_PEM, USE_DER } outformat; @@ -289,19 +289,25 @@ struct pem_formats { }; +struct pem_ctx { + int flags; + struct hx509_collector *c; +}; + static int pem_func(hx509_context context, const char *type, const hx509_pem_header *header, const void *data, size_t len, void *ctx) { - struct hx509_collector *c = ctx; - int ret, j; + struct pem_ctx *pem_ctx = (struct pem_ctx*)ctx; + int ret = 0, j; for (j = 0; j < sizeof(formats)/sizeof(formats[0]); j++) { const char *q = formats[j].name; if (strcasecmp(type, q) == 0) { - ret = (*formats[j].func)(context, NULL, c, header, data, len); - break; + ret = (*formats[j].func)(context, NULL, pem_ctx->c, header, data, len); + if (ret == 0) + break; } } if (j == sizeof(formats)/sizeof(formats[0])) { @@ -310,6 +316,8 @@ pem_func(hx509_context context, const char *type, "Found no matching PEM format for %s", type); return ret; } + if (ret && (pem_ctx->flags & HX509_CERTS_UNPROTECT_ALL)) + return ret; return 0; } @@ -324,9 +332,12 @@ file_init_common(hx509_context context, { char *p, *pnext; struct ks_file *f = NULL; - struct hx509_collector *c = NULL; hx509_private_key *keys = NULL; int ret; + struct pem_ctx pem_ctx; + + pem_ctx.flags = flags; + pem_ctx.c = NULL; *data = NULL; @@ -361,7 +372,7 @@ file_init_common(hx509_context context, return 0; } - ret = _hx509_collector_alloc(context, lock, &c); + ret = _hx509_collector_alloc(context, lock, &pem_ctx.c); if (ret) goto out; @@ -381,7 +392,7 @@ file_init_common(hx509_context context, goto out; } - ret = hx509_pem_read(context, f, pem_func, c); + ret = hx509_pem_read(context, f, pem_func, &pem_ctx); fclose(f); if (ret != 0 && ret != HX509_PARSING_KEY_FAILED) goto out; @@ -397,7 +408,7 @@ file_init_common(hx509_context context, } for (i = 0; i < sizeof(formats)/sizeof(formats[0]); i++) { - ret = (*formats[i].func)(context, p, c, NULL, ptr, length); + ret = (*formats[i].func)(context, p, pem_ctx.c, NULL, ptr, length); if (ret == 0) break; } @@ -407,11 +418,11 @@ file_init_common(hx509_context context, } } - ret = _hx509_collector_collect_certs(context, c, &f->certs); + ret = _hx509_collector_collect_certs(context, pem_ctx.c, &f->certs); if (ret) goto out; - ret = _hx509_collector_collect_private_keys(context, c, &keys); + ret = _hx509_collector_collect_private_keys(context, pem_ctx.c, &keys); if (ret == 0) { int i; @@ -428,8 +439,9 @@ out: free(f->fn); free(f); } - if (c) - _hx509_collector_free(c); + if (pem_ctx.c) + _hx509_collector_free(pem_ctx.c); + return ret; } diff --git a/source4/heimdal/lib/hx509/ks_keychain.c b/source4/heimdal/lib/hx509/ks_keychain.c index 33c4d6774b..f8181975d9 100644 --- a/source4/heimdal/lib/hx509/ks_keychain.c +++ b/source4/heimdal/lib/hx509/ks_keychain.c @@ -32,17 +32,19 @@ */ #include "hx_locl.h" -RCSID("$Id: ks_keychain.c 21097 2007-06-16 07:00:49Z lha $"); +RCSID("$Id: ks_keychain.c 22084 2007-11-16 20:12:30Z lha $"); #ifdef HAVE_FRAMEWORK_SECURITY #include <Security/Security.h> -/* Missing function decls */ +/* Missing function decls in pre Leopard */ +#ifdef NEED_SECKEYGETCSPHANDLE_PROTO OSStatus SecKeyGetCSPHandle(SecKeyRef, CSSM_CSP_HANDLE *); OSStatus SecKeyGetCredentials(SecKeyRef, CSSM_ACL_AUTHORIZATION_TAG, int, const CSSM_ACCESS_CREDENTIALS **); #define kSecCredentialTypeDefault 0 +#endif static int @@ -50,7 +52,7 @@ getAttribute(SecKeychainItemRef itemRef, SecItemAttr item, SecKeychainAttributeList **attrs) { SecKeychainAttributeInfo attrInfo; - uint32 attrFormat = 0; + UInt32 attrFormat = 0; OSStatus ret; *attrs = NULL; @@ -408,7 +410,7 @@ keychain_iter(hx509_context context, { SecKeychainAttributeList *attrs = NULL; SecKeychainAttributeInfo attrInfo; - uint32 attrFormat[1] = { 0 }; + UInt32 attrFormat[1] = { 0 }; SecKeychainItemRef itemRef; SecItemAttr item[1]; struct iter *iter = cursor; diff --git a/source4/heimdal/lib/hx509/ks_p11.c b/source4/heimdal/lib/hx509/ks_p11.c index e3066bbcfa..0d7c312c72 100644 --- a/source4/heimdal/lib/hx509/ks_p11.c +++ b/source4/heimdal/lib/hx509/ks_p11.c @@ -32,7 +32,7 @@ */ #include "hx_locl.h" -RCSID("$Id: ks_p11.c 21387 2007-06-28 08:53:45Z lha $"); +RCSID("$Id: ks_p11.c 22071 2007-11-14 20:04:50Z lha $"); #ifdef HAVE_DLFCN_H #include <dlfcn.h> #endif @@ -403,7 +403,7 @@ p11_get_session(hx509_context context, * prompter or known to work pin code. * * This code is very conversative and only uses the prompter in - * the hx509_lock, the reason is that its bad to try many + * the hx509_lock, the reason is that it's bad to try many * passwords on a pkcs11 token, it might lock up and have to be * unlocked by a administrator. * diff --git a/source4/heimdal/lib/hx509/lock.c b/source4/heimdal/lib/hx509/lock.c index de326f2e2d..e835aee35a 100644 --- a/source4/heimdal/lib/hx509/lock.c +++ b/source4/heimdal/lib/hx509/lock.c @@ -32,7 +32,13 @@ */ #include "hx_locl.h" -RCSID("$Id: lock.c 18452 2006-10-14 09:41:05Z lha $"); +RCSID("$Id: lock.c 22327 2007-12-15 04:49:37Z lha $"); + +/** + * @page page_lock Locking and unlocking certificates and encrypted data. + * + * See the library functions here: @ref hx509_lock + */ struct hx509_lock_data { struct _hx509_password password; diff --git a/source4/heimdal/lib/hx509/name.c b/source4/heimdal/lib/hx509/name.c index 5198633b1e..3f0806ddc0 100644 --- a/source4/heimdal/lib/hx509/name.c +++ b/source4/heimdal/lib/hx509/name.c @@ -32,17 +32,39 @@ */ #include "hx_locl.h" -RCSID("$Id: name.c 20891 2007-06-04 22:51:41Z lha $"); +#include <wind.h> +RCSID("$Id: name.c 22583 2008-02-11 20:46:21Z lha $"); -/* - * name parsing from rfc2253 - * fix so parsing rfc1779 works too - * rfc3280 +/** + * @page page_name PKIX/X.509 Names + * + * There are several names in PKIX/X.509, GeneralName and Name. + * + * A Name consists of an ordered list of Relative Distinguished Names + * (RDN). Each RDN consists of an unordered list of typed strings. The + * types are defined by OID and have long and short description. For + * example id-at-commonName (2.5.4.3) have the long name CommonName + * and short name CN. The string itself can be of serveral encoding, + * UTF8, UTF16, Teltex string, etc. The type limit what encoding + * should be used. + * + * GeneralName is a broader nametype that can contains al kind of + * stuff like Name, IP addresses, partial Name, etc. + * + * Name is mapped into a hx509_name object. + * + * Parse and string name into a hx509_name object with hx509_parse_name(), + * make it back into string representation with hx509_name_to_string(). + * + * Name string are defined rfc2253, rfc1779 and X.501. + * + * See the library functions here: @ref hx509_name */ static const struct { const char *n; const heim_oid *(*o)(void); + wind_profile_flags flags; } no[] = { { "C", oid_id_at_countryName }, { "CN", oid_id_at_commonName }, @@ -153,6 +175,18 @@ stringtooid(const char *name, size_t len, heim_oid *oid) return ret; } +/** + * Convert the hx509 name object into a printable string. + * The resulting string should be freed with free(). + * + * @param name name to print + * @param str the string to return + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_name + */ + int hx509_name_to_string(const hx509_name name, char **str) { @@ -247,82 +281,185 @@ _hx509_Name_to_string(const Name *n, char **str) return 0; } -/* - * XXX this function is broken, it needs to compare code points, not - * bytes. - */ +#define COPYCHARARRAY(_ds,_el,_l,_n) \ + (_l) = strlen(_ds->u._el); \ + (_n) = malloc((_l) * sizeof((_n)[0])); \ + if ((_n) == NULL) \ + return ENOMEM; \ + for (i = 0; i < (_l); i++) \ + (_n)[i] = _ds->u._el[i] -int -_hx509_name_ds_cmp(const DirectoryString *ds1, const DirectoryString *ds2) + +#define COPYVALARRAY(_ds,_el,_l,_n) \ + (_l) = _ds->u._el.length; \ + (_n) = malloc((_l) * sizeof((_n)[0])); \ + if ((_n) == NULL) \ + return ENOMEM; \ + for (i = 0; i < (_l); i++) \ + (_n)[i] = _ds->u._el.data[i] + +#define COPYVOIDARRAY(_ds,_el,_l,_n) \ + (_l) = _ds->u._el.length; \ + (_n) = malloc((_l) * sizeof((_n)[0])); \ + if ((_n) == NULL) \ + return ENOMEM; \ + for (i = 0; i < (_l); i++) \ + (_n)[i] = ((unsigned char *)_ds->u._el.data)[i] + + + +static int +dsstringprep(const DirectoryString *ds, uint32_t **rname, size_t *rlen) { - int c; + wind_profile_flags flags = 0; + size_t i, len; + int ret; + uint32_t *name; - c = ds1->element - ds2->element; - if (c) - return c; + *rname = NULL; + *rlen = 0; - switch(ds1->element) { + switch(ds->element) { case choice_DirectoryString_ia5String: - c = strcmp(ds1->u.ia5String, ds2->u.ia5String); - break; - case choice_DirectoryString_teletexString: - c = der_heim_octet_string_cmp(&ds1->u.teletexString, - &ds2->u.teletexString); + COPYCHARARRAY(ds, ia5String, len, name); break; case choice_DirectoryString_printableString: - c = strcasecmp(ds1->u.printableString, ds2->u.printableString); + flags = WIND_PROFILE_LDAP_CASE_EXACT_ATTRIBUTE; + COPYCHARARRAY(ds, printableString, len, name); break; - case choice_DirectoryString_utf8String: - c = strcmp(ds1->u.utf8String, ds2->u.utf8String); + case choice_DirectoryString_teletexString: + COPYVOIDARRAY(ds, teletexString, len, name); + break; + case choice_DirectoryString_bmpString: + COPYVALARRAY(ds, bmpString, len, name); break; case choice_DirectoryString_universalString: - c = der_heim_universal_string_cmp(&ds1->u.universalString, - &ds2->u.universalString); + COPYVALARRAY(ds, universalString, len, name); break; - case choice_DirectoryString_bmpString: - c = der_heim_bmp_string_cmp(&ds1->u.bmpString, - &ds2->u.bmpString); + case choice_DirectoryString_utf8String: + ret = wind_utf8ucs4_length(ds->u.utf8String, &len); + if (ret) + return ret; + name = malloc(len * sizeof(name[0])); + if (name == NULL) + return ENOMEM; + ret = wind_utf8ucs4(ds->u.utf8String, name, &len); + if (ret) + return ret; break; default: - c = 1; - break; + _hx509_abort("unknown directory type: %d", ds->element); + } + + *rlen = len; + /* try a couple of times to get the length right, XXX gross */ + for (i = 0; i < 4; i++) { + *rlen = *rlen * 2; + *rname = malloc(*rlen * sizeof((*rname)[0])); + + ret = wind_stringprep(name, len, *rname, rlen, + WIND_PROFILE_LDAP|flags); + if (ret == WIND_ERR_OVERRUN) { + free(*rname); + *rname = NULL; + continue; + } else + break; + } + free(name); + if (ret) { + if (*rname) + free(*rname); + *rname = NULL; + *rlen = 0; + return ret; + } + + return 0; +} + +int +_hx509_name_ds_cmp(const DirectoryString *ds1, + const DirectoryString *ds2, + int *diff) +{ + uint32_t *ds1lp, *ds2lp; + size_t ds1len, ds2len; + int ret; + + ret = dsstringprep(ds1, &ds1lp, &ds1len); + if (ret) + return ret; + ret = dsstringprep(ds2, &ds2lp, &ds2len); + if (ret) { + free(ds1lp); + return ret; } - return c; + + if (ds1len != ds2len) + *diff = ds1len - ds2len; + else + *diff = memcmp(ds1lp, ds2lp, ds1len * sizeof(ds1lp[0])); + + free(ds1lp); + free(ds2lp); + + return 0; } int -_hx509_name_cmp(const Name *n1, const Name *n2) +_hx509_name_cmp(const Name *n1, const Name *n2, int *c) { - int i, j, c; + int ret, i, j; - c = n1->u.rdnSequence.len - n2->u.rdnSequence.len; - if (c) - return c; + *c = n1->u.rdnSequence.len - n2->u.rdnSequence.len; + if (*c) + return 0; for (i = 0 ; i < n1->u.rdnSequence.len; i++) { - c = n1->u.rdnSequence.val[i].len - n2->u.rdnSequence.val[i].len; - if (c) - return c; + *c = n1->u.rdnSequence.val[i].len - n2->u.rdnSequence.val[i].len; + if (*c) + return 0; for (j = 0; j < n1->u.rdnSequence.val[i].len; j++) { - c = der_heim_oid_cmp(&n1->u.rdnSequence.val[i].val[j].type, - &n1->u.rdnSequence.val[i].val[j].type); - if (c) - return c; + *c = der_heim_oid_cmp(&n1->u.rdnSequence.val[i].val[j].type, + &n1->u.rdnSequence.val[i].val[j].type); + if (*c) + return 0; - c = _hx509_name_ds_cmp(&n1->u.rdnSequence.val[i].val[j].value, - &n2->u.rdnSequence.val[i].val[j].value); - if (c) - return c; + ret = _hx509_name_ds_cmp(&n1->u.rdnSequence.val[i].val[j].value, + &n2->u.rdnSequence.val[i].val[j].value, + c); + if (ret) + return ret; + if (*c) + return 0; } } + *c = 0; return 0; } +/** + * Compare to hx509 name object, useful for sorting. + * + * @param n1 a hx509 name object. + * @param n2 a hx509 name object. + * + * @return 0 the objects are the same, returns > 0 is n2 is "larger" + * then n2, < 0 if n1 is "smaller" then n2. + * + * @ingroup hx509_name + */ + int hx509_name_cmp(hx509_name n1, hx509_name n2) { - return _hx509_name_cmp(&n1->der_name, &n2->der_name); + int ret, diff; + ret = _hx509_name_cmp(&n1->der_name, &n2->der_name, &diff); + if (ret) + return ret; + return diff; } @@ -341,19 +478,6 @@ _hx509_name_from_Name(const Name *n, hx509_name *name) return ret; } -static int -hx509_der_parse_name(const void *data, size_t length, hx509_name *name) -{ - int ret; - Name n; - - *name = NULL; - ret = decode_Name(data, length, &n, NULL); - if (ret) - return ret; - return _hx509_name_from_Name(&n, name); -} - int _hx509_name_modify(hx509_context context, Name *name, @@ -400,6 +524,18 @@ _hx509_name_modify(hx509_context context, return 0; } +/** + * Parse a string into a hx509 name object. + * + * @param context A hx509 context. + * @param str a string to parse. + * @param name the resulting object, NULL in case of error. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_name + */ + int hx509_parse_name(hx509_context context, const char *str, hx509_name *name) { @@ -492,6 +628,18 @@ out: return HX509_NAME_MALFORMED; } +/** + * Copy a hx509 name object. + * + * @param context A hx509 cotext. + * @param from the name to copy from + * @param to the name to copy to + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_name + */ + int hx509_name_copy(hx509_context context, const hx509_name from, hx509_name *to) { @@ -509,6 +657,17 @@ hx509_name_copy(hx509_context context, const hx509_name from, hx509_name *to) return 0; } +/** + * Convert a hx509_name into a Name. + * + * @param from the name to copy from + * @param to the name to copy to + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_name + */ + int hx509_name_to_Name(const hx509_name from, Name *to) { @@ -521,6 +680,19 @@ hx509_name_normalize(hx509_context context, hx509_name name) return 0; } +/** + * Expands variables in the name using env. Variables are on the form + * ${name}. Useful when dealing with certificate templates. + * + * @param context A hx509 cotext. + * @param name the name to expand. + * @param env environment variable to expand. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_name + */ + int hx509_name_expand(hx509_context context, hx509_name name, @@ -539,6 +711,7 @@ hx509_name_expand(hx509_context context, for (i = 0 ; i < n->u.rdnSequence.len; i++) { for (j = 0; j < n->u.rdnSequence.val[i].len; j++) { + /** Only UTF8String rdnSequence names are allowed */ /* THIS SHOULD REALLY BE: COMP = n->u.rdnSequence.val[i].val[j]; @@ -615,6 +788,13 @@ hx509_name_expand(hx509_context context, return 0; } +/** + * Free a hx509 name object, upond return *name will be NULL. + * + * @param name a hx509 name object to be freed. + * + * @ingroup hx509_name + */ void hx509_name_free(hx509_name *name) @@ -625,37 +805,61 @@ hx509_name_free(hx509_name *name) *name = NULL; } +/** + * Convert a DER encoded name info a string. + * + * @param data data to a DER/BER encoded name + * @param length length of data + * @param str the resulting string, is NULL on failure. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_name + */ + int hx509_unparse_der_name(const void *data, size_t length, char **str) { - hx509_name name; + Name name; int ret; - ret = hx509_der_parse_name(data, length, &name); + *str = NULL; + + ret = decode_Name(data, length, &name, NULL); if (ret) return ret; - - ret = hx509_name_to_string(name, str); - hx509_name_free(&name); + ret = _hx509_Name_to_string(&name, str); + free_Name(&name); return ret; } +/** + * Convert a hx509_name object to DER encoded name. + * + * @param name name to concert + * @param os data to a DER encoded name, free the resulting octet + * string with hx509_xfree(os->data). + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_name + */ + int -hx509_name_to_der_name(const hx509_name name, void **data, size_t *length) +hx509_name_binary(const hx509_name name, heim_octet_string *os) { size_t size; int ret; - ASN1_MALLOC_ENCODE(Name, *data, *length, &name->der_name, &size, ret); + ASN1_MALLOC_ENCODE(Name, os->data, os->length, &name->der_name, &size, ret); if (ret) return ret; - if (*length != size) + if (os->length != size) _hx509_abort("internal ASN.1 encoder error"); return 0; } - int _hx509_unparse_Name(const Name *aname, char **str) { @@ -671,12 +875,33 @@ _hx509_unparse_Name(const Name *aname, char **str) return ret; } +/** + * Unparse the hx509 name in name into a string. + * + * @param name the name to check if its empty/null. + * + * @return non zero if the name is empty/null. + * + * @ingroup hx509_name + */ + int hx509_name_is_null_p(const hx509_name name) { return name->der_name.u.rdnSequence.len == 0; } +/** + * Unparse the hx509 name in name into a string. + * + * @param name the name to print + * @param str an allocated string returns the name in string form + * + * @return An hx509 error code, see krb5_get_error_string(). + * + * @ingroup hx509_name + */ + int hx509_general_name_unparse(GeneralName *name, char **str) { diff --git a/source4/heimdal/lib/hx509/peer.c b/source4/heimdal/lib/hx509/peer.c index e90f8f34b0..eb0ecd2bde 100644 --- a/source4/heimdal/lib/hx509/peer.c +++ b/source4/heimdal/lib/hx509/peer.c @@ -32,7 +32,27 @@ */ #include "hx_locl.h" -RCSID("$Id: peer.c 21481 2007-07-10 16:33:23Z lha $"); +RCSID("$Id: peer.c 22345 2007-12-26 19:03:51Z lha $"); + +/** + * @page page_peer Hx509 crypto selecting functions + * + * Peer info structures are used togeter with hx509_crypto_select() to + * select the best avaible crypto algorithm to use. + * + * See the library functions here: @ref hx509_peer + */ + +/** + * Allocate a new peer info structure an init it to default values. + * + * @param context A hx509 context. + * @param peer return an allocated peer, free with hx509_peer_info_free(). + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_peer + */ int hx509_peer_info_alloc(hx509_context context, hx509_peer_info *peer) @@ -59,6 +79,14 @@ free_cms_alg(hx509_peer_info peer) } } +/** + * Free a peer info structure. + * + * @param peer peer info to be freed. + * + * @ingroup hx509_peer + */ + void hx509_peer_info_free(hx509_peer_info peer) { @@ -71,6 +99,17 @@ hx509_peer_info_free(hx509_peer_info peer) free(peer); } +/** + * Set the certificate that remote peer is using. + * + * @param peer peer info to update + * @param cert cerificate of the remote peer. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_peer + */ + int hx509_peer_info_set_cert(hx509_peer_info peer, hx509_cert cert) @@ -81,6 +120,19 @@ hx509_peer_info_set_cert(hx509_peer_info peer, return 0; } +/** + * Set the algorithms that the peer supports. + * + * @param context A hx509 context. + * @param peer the peer to set the new algorithms for + * @param val array of supported AlgorithmsIdentiers + * @param len length of array val. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_peer + */ + int hx509_peer_info_set_cms_algs(hx509_context context, hx509_peer_info peer, diff --git a/source4/heimdal/lib/hx509/print.c b/source4/heimdal/lib/hx509/print.c index e6f71ea2ce..c1594ff047 100644 --- a/source4/heimdal/lib/hx509/print.c +++ b/source4/heimdal/lib/hx509/print.c @@ -32,8 +32,13 @@ */ #include "hx_locl.h" -RCSID("$Id: print.c 21381 2007-06-28 08:29:22Z lha $"); +RCSID("$Id: print.c 22538 2008-01-27 13:05:47Z lha $"); +/** + * @page page_print Hx509 printing functions + * + * See the library functions here: @ref hx509_print + */ struct hx509_validate_ctx_data { int flags; @@ -75,15 +80,31 @@ Time2string(const Time *T, char **str) return 0; } +/** + * Helper function to print on stdout for: + * - hx509_oid_print(), + * - hx509_bitstring_print(), + * - hx509_validate_ctx_set_print(). + * + * @param ctx the context to the print function. If the ctx is NULL, + * stdout is used. + * @param fmt the printing format. + * @param va the argumet list. + * + * @ingroup hx509_print + */ + void hx509_print_stdout(void *ctx, const char *fmt, va_list va) { FILE *f = ctx; + if (f == NULL) + f = stdout; vfprintf(f, fmt, va); } -void -hx509_print_func(hx509_vprint_func func, void *ctx, const char *fmt, ...) +static void +print_func(hx509_vprint_func func, void *ctx, const char *fmt, ...) { va_list va; va_start(va, fmt); @@ -91,36 +112,82 @@ hx509_print_func(hx509_vprint_func func, void *ctx, const char *fmt, ...) va_end(va); } +/** + * Print a oid to a string. + * + * @param oid oid to print + * @param str allocated string, free with hx509_xfree(). + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_print + */ + int hx509_oid_sprint(const heim_oid *oid, char **str) { return der_print_heim_oid(oid, '.', str); } +/** + * Print a oid using a hx509_vprint_func function. To print to stdout + * use hx509_print_stdout(). + * + * @param oid oid to print + * @param func hx509_vprint_func to print with. + * @param ctx context variable to hx509_vprint_func function. + * + * @ingroup hx509_print + */ + void hx509_oid_print(const heim_oid *oid, hx509_vprint_func func, void *ctx) { char *str; hx509_oid_sprint(oid, &str); - hx509_print_func(func, ctx, "%s", str); + print_func(func, ctx, "%s", str); free(str); } +/** + * Print a bitstring using a hx509_vprint_func function. To print to + * stdout use hx509_print_stdout(). + * + * @param b bit string to print. + * @param func hx509_vprint_func to print with. + * @param ctx context variable to hx509_vprint_func function. + * + * @ingroup hx509_print + */ + void hx509_bitstring_print(const heim_bit_string *b, hx509_vprint_func func, void *ctx) { int i; - hx509_print_func(func, ctx, "\tlength: %d\n\t", b->length); + print_func(func, ctx, "\tlength: %d\n\t", b->length); for (i = 0; i < (b->length + 7) / 8; i++) - hx509_print_func(func, ctx, "%02x%s%s", - ((unsigned char *)b->data)[i], - i < (b->length - 7) / 8 - && (i == 0 || (i % 16) != 15) ? ":" : "", - i != 0 && (i % 16) == 15 ? - (i <= ((b->length + 7) / 8 - 2) ? "\n\t" : "\n"):""); + print_func(func, ctx, "%02x%s%s", + ((unsigned char *)b->data)[i], + i < (b->length - 7) / 8 + && (i == 0 || (i % 16) != 15) ? ":" : "", + i != 0 && (i % 16) == 15 ? + (i <= ((b->length + 7) / 8 - 2) ? "\n\t" : "\n"):""); } +/** + * Print certificate usage for a certificate to a string. + * + * @param context A hx509 context. + * @param c a certificate print the keyusage for. + * @param s the return string with the keysage printed in to, free + * with hx509_xfree(). + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_print + */ + int hx509_cert_keyusage_print(hx509_context context, hx509_cert c, char **s) { @@ -268,9 +335,6 @@ check_authorityKeyIdentifier(hx509_validate_ctx ctx, status->haveAKI = 1; check_Null(ctx, status, cf, e); - status->haveSKI = 1; - check_Null(ctx, status, cf, e); - ret = decode_AuthorityKeyIdentifier(e->extnValue.data, e->extnValue.length, &ai, &size); @@ -298,6 +362,56 @@ check_authorityKeyIdentifier(hx509_validate_ctx ctx, return 0; } +static int +check_extKeyUsage(hx509_validate_ctx ctx, + struct cert_status *status, + enum critical_flag cf, + const Extension *e) +{ + ExtKeyUsage eku; + size_t size, i; + int ret; + + check_Null(ctx, status, cf, e); + + ret = decode_ExtKeyUsage(e->extnValue.data, + e->extnValue.length, + &eku, &size); + if (ret) { + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "Decoding ExtKeyUsage failed: %d", ret); + return 1; + } + if (size != e->extnValue.length) { + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "Padding data in EKU"); + free_ExtKeyUsage(&eku); + return 1; + } + if (eku.len == 0) { + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "ExtKeyUsage length is 0"); + return 1; + } + + for (i = 0; i < eku.len; i++) { + char *str; + ret = der_print_heim_oid (&eku.val[i], '.', &str); + if (ret) { + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "\tEKU: failed to print oid %d", i); + free_ExtKeyUsage(&eku); + return 1; + } + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, + "\teku-%d: %s\n", i, str);; + free(str); + } + + free_ExtKeyUsage(&eku); + + return 0; +} static int check_pkinit_san(hx509_validate_ctx ctx, heim_any *a) @@ -664,7 +778,7 @@ struct { { ext(policyMappings, Null), M_N_C }, { ext(authorityKeyIdentifier, authorityKeyIdentifier), M_N_C }, { ext(policyConstraints, Null), D_C }, - { ext(extKeyUsage, Null), D_C }, + { ext(extKeyUsage, extKeyUsage), D_C }, { ext(freshestCRL, Null), M_N_C }, { ext(inhibitAnyPolicy, Null), M_C }, #undef ext @@ -679,6 +793,18 @@ struct { { NULL } }; +/** + * Allocate a hx509 validation/printing context. + * + * @param context A hx509 context. + * @param ctx a new allocated hx509 validation context, free with + * hx509_validate_ctx_free(). + + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_print + */ + int hx509_validate_ctx_init(hx509_context context, hx509_validate_ctx *ctx) { @@ -689,6 +815,18 @@ hx509_validate_ctx_init(hx509_context context, hx509_validate_ctx *ctx) return 0; } +/** + * Set the printing functions for the validation context. + * + * @param ctx a hx509 valication context. + * @param func the printing function to usea. + * @param c the context variable to the printing function. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_print + */ + void hx509_validate_ctx_set_print(hx509_validate_ctx ctx, hx509_vprint_func func, @@ -698,18 +836,50 @@ hx509_validate_ctx_set_print(hx509_validate_ctx ctx, ctx->ctx = c; } +/** + * Add flags to control the behaivor of the hx509_validate_cert() + * function. + * + * @param ctx A hx509 validation context. + * @param flags flags to add to the validation context. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_print + */ + void hx509_validate_ctx_add_flags(hx509_validate_ctx ctx, int flags) { ctx->flags |= flags; } +/** + * Free an hx509 validate context. + * + * @param ctx the hx509 validate context to free. + * + * @ingroup hx509_print + */ + void hx509_validate_ctx_free(hx509_validate_ctx ctx) { free(ctx); } +/** + * Validate/Print the status of the certificate. + * + * @param context A hx509 context. + * @param ctx A hx509 validation context. + * @param cert the cerificate to validate/print. + + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_print + */ + int hx509_validate_cert(hx509_context context, hx509_validate_ctx ctx, diff --git a/source4/heimdal/lib/hx509/revoke.c b/source4/heimdal/lib/hx509/revoke.c index ddcb17ee38..2010f945f0 100644 --- a/source4/heimdal/lib/hx509/revoke.c +++ b/source4/heimdal/lib/hx509/revoke.c @@ -31,14 +31,33 @@ * SUCH DAMAGE. */ +/** + * @page page_revoke Revocation methods + * + * There are two revocation method for PKIX/X.509: CRL and OCSP. + * Revocation is needed if the private key is lost and + * stolen. Depending on how picky you are, you might want to make + * revocation for destroyed private keys too (smartcard broken), but + * that should not be a problem. + * + * CRL is a list of certifiates that have expired. + * + * OCSP is an online checking method where the requestor sends a list + * of certificates to the OCSP server to return a signed reply if they + * are valid or not. Some services sends a OCSP reply as part of the + * hand-shake to make the revoktion decision simpler/faster for the + * client. + */ + #include "hx_locl.h" -RCSID("$Id: revoke.c 21153 2007-06-18 21:55:46Z lha $"); +RCSID("$Id: revoke.c 22583 2008-02-11 20:46:21Z lha $"); struct revoke_crl { char *path; time_t last_modfied; CRLCertificateList crl; int verified; + int failed_verify; }; struct revoke_ocsp { @@ -51,6 +70,7 @@ struct revoke_ocsp { struct hx509_revoke_ctx_data { + unsigned ref; struct { struct revoke_crl *val; size_t len; @@ -61,6 +81,17 @@ struct hx509_revoke_ctx_data { } ocsps; }; +/** + * Allocate a revokation context. Free with hx509_revoke_free(). + * + * @param context A hx509 context. + * @param ctx returns a newly allocated revokation context. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_revoke + */ + int hx509_revoke_init(hx509_context context, hx509_revoke_ctx *ctx) { @@ -68,6 +99,7 @@ hx509_revoke_init(hx509_context context, hx509_revoke_ctx *ctx) if (*ctx == NULL) return ENOMEM; + (*ctx)->ref = 1; (*ctx)->crls.len = 0; (*ctx)->crls.val = NULL; (*ctx)->ocsps.len = 0; @@ -76,6 +108,19 @@ hx509_revoke_init(hx509_context context, hx509_revoke_ctx *ctx) return 0; } +hx509_revoke_ctx +_hx509_revoke_ref(hx509_revoke_ctx ctx) +{ + if (ctx == NULL) + return NULL; + if (ctx->ref <= 0) + _hx509_abort("revoke ctx refcount <= 0"); + ctx->ref++; + if (ctx->ref == 0) + _hx509_abort("revoke ctx refcount == 0"); + return ctx; +} + static void free_ocsp(struct revoke_ocsp *ocsp) { @@ -85,6 +130,14 @@ free_ocsp(struct revoke_ocsp *ocsp) hx509_cert_free(ocsp->signer); } +/** + * Free a hx509 revokation context. + * + * @param ctx context to be freed + * + * @ingroup hx509_revoke + */ + void hx509_revoke_free(hx509_revoke_ctx *ctx) { @@ -93,6 +146,11 @@ hx509_revoke_free(hx509_revoke_ctx *ctx) if (ctx == NULL || *ctx == NULL) return; + if ((*ctx)->ref <= 0) + _hx509_abort("revoke ctx refcount <= 0 on free"); + if (--(*ctx)->ref > 0) + return; + for (i = 0; i < (*ctx)->crls.len; i++) { free((*ctx)->crls.val[i].path); free_CRLCertificateList(&(*ctx)->crls.val[i].crl); @@ -150,7 +208,7 @@ verify_ocsp(hx509_context context, /* * If signer certificate isn't the CA certificate, lets check the - * its the CA that signed the signer certificate and the OCSP EKU + * it is the CA that signed the signer certificate and the OCSP EKU * is set. */ if (hx509_cert_cmp(signer, parent) != 0) { @@ -324,6 +382,18 @@ load_ocsp(hx509_context context, struct revoke_ocsp *ocsp) return 0; } +/** + * Add a OCSP file to the revokation context. + * + * @param context hx509 context + * @param ctx hx509 revokation context + * @param path path to file that is going to be added to the context. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_revoke + */ + int hx509_revoke_add_ocsp(hx509_context context, hx509_revoke_ctx ctx, @@ -380,6 +450,7 @@ hx509_revoke_add_ocsp(hx509_context context, static int verify_crl(hx509_context context, + hx509_revoke_ctx ctx, CRLCertificateList *crl, time_t time_now, hx509_certs certs, @@ -391,52 +462,44 @@ verify_crl(hx509_context context, int ret; t = _hx509_Time2time_t(&crl->tbsCertList.thisUpdate); - if (t > time_now) + if (t > time_now) { + hx509_set_error_string(context, 0, HX509_CRL_USED_BEFORE_TIME, + "CRL used before time"); return HX509_CRL_USED_BEFORE_TIME; + } - if (crl->tbsCertList.nextUpdate == NULL) + if (crl->tbsCertList.nextUpdate == NULL) { + hx509_set_error_string(context, 0, HX509_CRL_INVALID_FORMAT, + "CRL missing nextUpdate"); return HX509_CRL_INVALID_FORMAT; + } t = _hx509_Time2time_t(crl->tbsCertList.nextUpdate); - if (t < time_now) + if (t < time_now) { + hx509_set_error_string(context, 0, HX509_CRL_USED_AFTER_TIME, + "CRL used after time"); return HX509_CRL_USED_AFTER_TIME; + } _hx509_query_clear(&q); - q.match = HX509_QUERY_MATCH_SUBJECT_NAME; - q.subject_name = &crl->tbsCertList.issuer; + /* + * If it's the signer have CRLSIGN bit set, use that as the signer + * cert for the certificate, otherwise, search for a certificate. + */ + if (_hx509_check_key_usage(context, parent, 1 << 6, FALSE) == 0) { + signer = hx509_cert_ref(parent); + } else { + q.match = HX509_QUERY_MATCH_SUBJECT_NAME; + q.match |= HX509_QUERY_KU_CRLSIGN; + q.subject_name = &crl->tbsCertList.issuer; - ret = hx509_certs_find(context, certs, &q, &signer); - if (ret) - return ret; - - /* verify is parent or CRLsigner */ - if (hx509_cert_cmp(signer, parent) != 0) { - Certificate *p = _hx509_get_cert(parent); - Certificate *s = _hx509_get_cert(signer); - - ret = _hx509_cert_is_parent_cmp(s, p, 0); - if (ret != 0) { - ret = HX509_PARENT_NOT_CA; - hx509_set_error_string(context, 0, ret, "Revoke CRL signer is " - "doesn't have CA as signer certificate"); - goto out; - } - - ret = _hx509_verify_signature_bitstring(context, - p, - &s->signatureAlgorithm, - &s->tbsCertificate._save, - &s->signatureValue); + ret = hx509_certs_find(context, certs, &q, &signer); if (ret) { hx509_set_error_string(context, HX509_ERROR_APPEND, ret, - "CRL signer signature invalid"); - goto out; + "Failed to find certificate for CRL"); + return ret; } - - ret = _hx509_check_key_usage(context, signer, 1 << 6, TRUE); /* crl */ - if (ret != 0) - goto out; } ret = _hx509_verify_signature_bitstring(context, @@ -450,6 +513,44 @@ verify_crl(hx509_context context, goto out; } + /* + * If signer is not CA cert, need to check revoke status of this + * CRL signing cert too, this include all parent CRL signer cert + * up to the root *sigh*, assume root at least hve CERTSIGN flag + * set. + */ + while (_hx509_check_key_usage(context, signer, 1 << 5, TRUE)) { + hx509_cert crl_parent; + + _hx509_query_clear(&q); + + q.match = HX509_QUERY_MATCH_SUBJECT_NAME; + q.match |= HX509_QUERY_KU_CRLSIGN; + q.subject_name = &_hx509_get_cert(signer)->tbsCertificate.issuer; + + ret = hx509_certs_find(context, certs, &q, &crl_parent); + if (ret) { + hx509_set_error_string(context, HX509_ERROR_APPEND, ret, + "Failed to find parent of CRL signer"); + goto out; + } + + ret = hx509_revoke_verify(context, + ctx, + certs, + time_now, + signer, + crl_parent); + hx509_cert_free(signer); + signer = crl_parent; + if (ret) { + hx509_set_error_string(context, HX509_ERROR_APPEND, ret, + "Failed to verify revoke " + "status of CRL signer"); + goto out; + } + } + out: hx509_cert_free(signer); @@ -485,6 +586,18 @@ load_crl(const char *path, time_t *t, CRLCertificateList *crl) return 0; } +/** + * Add a CRL file to the revokation context. + * + * @param context hx509 context + * @param ctx hx509 revokation context + * @param path path to file that is going to be added to the context. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_revoke + */ + int hx509_revoke_add_crl(hx509_context context, hx509_revoke_ctx ctx, @@ -537,6 +650,23 @@ hx509_revoke_add_crl(hx509_context context, return ret; } +/** + * Check that a certificate is not expired according to a revokation + * context. Also need the parent certificte to the check OCSP + * parent identifier. + * + * @param context hx509 context + * @param ctx hx509 revokation context + * @param certs + * @param now + * @param cert + * @param parent_cert + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_revoke + */ + int hx509_revoke_verify(hx509_context context, @@ -551,6 +681,8 @@ hx509_revoke_verify(hx509_context context, unsigned long i, j, k; int ret; + hx509_clear_error_string(context); + for (i = 0; i < ctx->ocsps.len; i++) { struct revoke_ocsp *ocsp = &ctx->ocsps.val[i]; struct stat sb; @@ -604,6 +736,10 @@ hx509_revoke_verify(hx509_context context, case choice_OCSPCertStatus_good: break; case choice_OCSPCertStatus_revoked: + hx509_set_error_string(context, 0, + HX509_CERT_REVOKED, + "Certificate revoked by issuer in OCSP"); + return HX509_CERT_REVOKED; case choice_OCSPCertStatus_unknown: continue; } @@ -613,7 +749,7 @@ hx509_revoke_verify(hx509_context context, now + context->ocsp_time_diff) continue; - /* don't allow the next updte to be in the past */ + /* don't allow the next update to be in the past */ if (ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate) { if (*ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate < now) continue; @@ -627,11 +763,12 @@ hx509_revoke_verify(hx509_context context, for (i = 0; i < ctx->crls.len; i++) { struct revoke_crl *crl = &ctx->crls.val[i]; struct stat sb; + int diff; /* check if cert.issuer == crls.val[i].crl.issuer */ ret = _hx509_name_cmp(&c->tbsCertificate.issuer, - &crl->crl.tbsCertList.issuer); - if (ret) + &crl->crl.tbsCertList.issuer, &diff); + if (ret || diff) continue; ret = stat(crl->path, &sb); @@ -643,21 +780,32 @@ hx509_revoke_verify(hx509_context context, free_CRLCertificateList(&crl->crl); crl->crl = cl; crl->verified = 0; + crl->failed_verify = 0; } } + if (crl->failed_verify) + continue; /* verify signature in crl if not already done */ if (crl->verified == 0) { - ret = verify_crl(context, &crl->crl, now, certs, parent_cert); - if (ret) - return ret; + ret = verify_crl(context, ctx, &crl->crl, now, certs, parent_cert); + if (ret) { + crl->failed_verify = 1; + continue; + } crl->verified = 1; } - - if (crl->crl.tbsCertList.crlExtensions) - for (j = 0; j < crl->crl.tbsCertList.crlExtensions->len; j++) - if (crl->crl.tbsCertList.crlExtensions->val[j].critical) + + if (crl->crl.tbsCertList.crlExtensions) { + for (j = 0; j < crl->crl.tbsCertList.crlExtensions->len; j++) { + if (crl->crl.tbsCertList.crlExtensions->val[j].critical) { + hx509_set_error_string(context, 0, + HX509_CRL_UNKNOWN_EXTENSION, + "Unknown CRL extension"); return HX509_CRL_UNKNOWN_EXTENSION; + } + } + } if (crl->crl.tbsCertList.revokedCertificates == NULL) return 0; @@ -667,7 +815,7 @@ hx509_revoke_verify(hx509_context context, time_t t; ret = der_heim_integer_cmp(&crl->crl.tbsCertList.revokedCertificates->val[j].userCertificate, - &c->tbsCertificate.serialNumber); + &c->tbsCertificate.serialNumber); if (ret != 0) continue; @@ -680,7 +828,10 @@ hx509_revoke_verify(hx509_context context, if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->val[k].critical) return HX509_CRL_UNKNOWN_EXTENSION; - return HX509_CRL_CERT_REVOKED; + hx509_set_error_string(context, 0, + HX509_CERT_REVOKED, + "Certificate revoked by issuer in CRL"); + return HX509_CERT_REVOKED; } return 0; @@ -689,6 +840,10 @@ hx509_revoke_verify(hx509_context context, if (context->flags & HX509_CTX_VERIFY_MISSING_OK) return 0; + hx509_set_error_string(context, HX509_ERROR_APPEND, + HX509_REVOKE_STATUS_MISSING, + "No revoke status found for " + "certificates"); return HX509_REVOKE_STATUS_MISSING; } @@ -785,6 +940,22 @@ out: return ret; } +/** + * Create an OCSP request for a set of certificates. + * + * @param context a hx509 context + * @param reqcerts list of certificates to request ocsp data for + * @param pool certificate pool to use when signing + * @param signer certificate to use to sign the request + * @param digest the signing algorithm in the request, if NULL use the + * default signature algorithm, + * @param request the encoded request, free with free_heim_octet_string(). + * @param nonce nonce in the request, free with free_heim_octet_string(). + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_revoke + */ int hx509_ocsp_request(hx509_context context, @@ -813,41 +984,49 @@ hx509_ocsp_request(hx509_context context, ret = hx509_certs_iter(context, reqcerts, add_to_req, &ctx); hx509_cert_free(ctx.parent); - if (ret) { - free_OCSPRequest(&req); - return ret; - } + if (ret) + goto out; if (nonce) { - req.tbsRequest.requestExtensions = calloc(1, sizeof(*req.tbsRequest.requestExtensions)); if (req.tbsRequest.requestExtensions == NULL) { - free_OCSPRequest(&req); - return ENOMEM; + ret = ENOMEM; + goto out; } es = req.tbsRequest.requestExtensions; - es->len = 1; es->val = calloc(es->len, sizeof(es->val[0])); + if (es->val == NULL) { + ret = ENOMEM; + goto out; + } + es->len = 1; ret = der_copy_oid(oid_id_pkix_ocsp_nonce(), &es->val[0].extnID); - if (ret) - abort(); - + if (ret) { + free_OCSPRequest(&req); + return ret; + } + es->val[0].extnValue.data = malloc(10); if (es->val[0].extnValue.data == NULL) { - free_OCSPRequest(&req); - return ENOMEM; + ret = ENOMEM; + goto out; } es->val[0].extnValue.length = 10; ret = RAND_bytes(es->val[0].extnValue.data, es->val[0].extnValue.length); if (ret != 1) { - free_OCSPRequest(&req); - return HX509_CRYPTO_INTERNAL_ERROR; + ret = HX509_CRYPTO_INTERNAL_ERROR; + goto out; + } + ret = der_copy_octet_string(nonce, &es->val[0].extnValue); + if (ret) { + ret = ENOMEM; + goto out; } } @@ -855,12 +1034,15 @@ hx509_ocsp_request(hx509_context context, &req, &size, ret); free_OCSPRequest(&req); if (ret) - return ret; + goto out; if (size != request->length) _hx509_abort("internal ASN.1 encoder error"); - return 0; + +out: + free_OCSPRequest(&req); + return ret; } static char * @@ -872,6 +1054,18 @@ printable_time(time_t t) return s; } +/** + * Print the OCSP reply stored in a file. + * + * @param context a hx509 context + * @param path path to a file with a OCSP reply + * @param out the out FILE descriptor to print the reply on + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_revoke + */ + int hx509_revoke_ocsp_print(hx509_context context, const char *path, FILE *out) { @@ -959,10 +1153,23 @@ hx509_revoke_ocsp_print(hx509_context context, const char *path, FILE *out) return ret; } -/* - * Verify that the `cert' is part of the OCSP reply and its not - * expired. Doesn't verify signature the OCSP reply or its done by a +/** + * Verify that the certificate is part of the OCSP reply and it's not + * expired. Doesn't verify signature the OCSP reply or it's done by a * authorized sender, that is assumed to be already done. + * + * @param context a hx509 context + * @param now the time right now, if 0, use the current time. + * @param cert the certificate to verify + * @param flags flags control the behavior + * @param data pointer to the encode ocsp reply + * @param length the length of the encode ocsp reply + * @param expiration return the time the OCSP will expire and need to + * be rechecked. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_verify */ int @@ -1062,6 +1269,17 @@ struct hx509_crl { time_t expire; }; +/** + * Create a CRL context. Use hx509_crl_free() to free the CRL context. + * + * @param context a hx509 context. + * @param crl return pointer to a newly allocated CRL context. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_verify + */ + int hx509_crl_alloc(hx509_context context, hx509_crl *crl) { @@ -1083,6 +1301,18 @@ hx509_crl_alloc(hx509_context context, hx509_crl *crl) return ret; } +/** + * Add revoked certificate to an CRL context. + * + * @param context a hx509 context. + * @param crl the CRL to add the revoked certificate to. + * @param certs keyset of certificate to revoke. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_verify + */ + int hx509_crl_add_revoked_certs(hx509_context context, hx509_crl crl, @@ -1091,6 +1321,19 @@ hx509_crl_add_revoked_certs(hx509_context context, return hx509_certs_merge(context, crl->revoked, certs); } +/** + * Set the lifetime of a CRL context. + * + * @param context a hx509 context. + * @param crl a CRL context + * @param delta delta time the certificate is valid, library adds the + * current time to this. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_verify + */ + int hx509_crl_lifetime(hx509_context context, hx509_crl crl, int delta) { @@ -1098,6 +1341,14 @@ hx509_crl_lifetime(hx509_context context, hx509_crl crl, int delta) return 0; } +/** + * Free a CRL context. + * + * @param context a hx509 context. + * @param crl a CRL context to free. + * + * @ingroup hx509_verify + */ void hx509_crl_free(hx509_context context, hx509_crl *crl) @@ -1144,6 +1395,19 @@ add_revoked(hx509_context context, void *ctx, hx509_cert cert) return 0; } +/** + * Sign a CRL and return an encode certificate. + * + * @param context a hx509 context. + * @param signer certificate to sign the CRL with + * @param crl the CRL to sign + * @param os return the signed and encoded CRL, free with + * free_heim_octet_string() + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_verify + */ int hx509_crl_sign(hx509_context context, |