diff options
Diffstat (limited to 'source4/heimdal/lib/hx509')
36 files changed, 23898 insertions, 0 deletions
diff --git a/source4/heimdal/lib/hx509/ca.c b/source4/heimdal/lib/hx509/ca.c new file mode 100644 index 0000000000..5b4d7711e7 --- /dev/null +++ b/source4/heimdal/lib/hx509/ca.c @@ -0,0 +1,1518 @@ +/* + * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hx_locl.h" +#include <pkinit_asn1.h> +RCSID("$Id$"); + +/** + * @page page_ca Hx509 CA functions + * + * See the library functions here: @ref hx509_ca + */ + +struct hx509_ca_tbs { + hx509_name subject; + SubjectPublicKeyInfo spki; + ExtKeyUsage eku; + GeneralNames san; + unsigned key_usage; + heim_integer serial; + struct { + unsigned int proxy:1; + unsigned int ca:1; + unsigned int key:1; + unsigned int serial:1; + unsigned int domaincontroller:1; + } flags; + time_t notBefore; + time_t notAfter; + int pathLenConstraint; /* both for CA and Proxy */ + 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) +{ + *tbs = calloc(1, sizeof(**tbs)); + if (*tbs == NULL) + return ENOMEM; + + (*tbs)->subject = NULL; + (*tbs)->san.len = 0; + (*tbs)->san.val = NULL; + (*tbs)->eku.len = 0; + (*tbs)->eku.val = NULL; + (*tbs)->pathLenConstraint = 0; + (*tbs)->crldp.len = 0; + (*tbs)->crldp.val = NULL; + + 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) +{ + if (tbs == NULL || *tbs == NULL) + return; + + free_SubjectPublicKeyInfo(&(*tbs)->spki); + free_GeneralNames(&(*tbs)->san); + free_ExtKeyUsage(&(*tbs)->eku); + der_free_heim_integer(&(*tbs)->serial); + free_CRLDistributionPoints(&(*tbs)->crldp); + + hx509_name_free(&(*tbs)->subject); + + memset(*tbs, 0, sizeof(**tbs)); + free(*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, + time_t t) +{ + tbs->notBefore = t; + 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, + time_t t) +{ + tbs->notAfter = t; + 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, + time_t delta) +{ + return hx509_ca_tbs_set_notAfter(context, tbs, time(NULL) + delta); +} + +static const struct units templatebits[] = { + { "ExtendedKeyUsage", HX509_CA_TEMPLATE_EKU }, + { "KeyUsage", HX509_CA_TEMPLATE_KU }, + { "SPKI", HX509_CA_TEMPLATE_SPKI }, + { "notAfter", HX509_CA_TEMPLATE_NOTAFTER }, + { "notBefore", HX509_CA_TEMPLATE_NOTBEFORE }, + { "serial", HX509_CA_TEMPLATE_SERIAL }, + { "subject", HX509_CA_TEMPLATE_SUBJECT }, + { 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, + int flags, + hx509_cert cert) +{ + int ret; + + if (flags & HX509_CA_TEMPLATE_SUBJECT) { + if (tbs->subject) + hx509_name_free(&tbs->subject); + ret = hx509_cert_get_subject(cert, &tbs->subject); + if (ret) { + hx509_set_error_string(context, 0, ret, + "Failed to get subject from template"); + return ret; + } + } + if (flags & HX509_CA_TEMPLATE_SERIAL) { + der_free_heim_integer(&tbs->serial); + ret = hx509_cert_get_serialnumber(cert, &tbs->serial); + tbs->flags.serial = !ret; + if (ret) { + hx509_set_error_string(context, 0, ret, + "Failed to copy serial number"); + return ret; + } + } + if (flags & HX509_CA_TEMPLATE_NOTBEFORE) + tbs->notBefore = hx509_cert_get_notBefore(cert); + if (flags & HX509_CA_TEMPLATE_NOTAFTER) + tbs->notAfter = hx509_cert_get_notAfter(cert); + if (flags & HX509_CA_TEMPLATE_SPKI) { + free_SubjectPublicKeyInfo(&tbs->spki); + ret = hx509_cert_get_SPKI(context, cert, &tbs->spki); + tbs->flags.key = !ret; + if (ret) + return ret; + } + if (flags & HX509_CA_TEMPLATE_KU) { + KeyUsage ku; + ret = _hx509_cert_get_keyusage(context, cert, &ku); + if (ret) + return ret; + tbs->key_usage = KeyUsage2int(ku); + } + if (flags & HX509_CA_TEMPLATE_EKU) { + ExtKeyUsage eku; + int i; + ret = _hx509_cert_get_eku(context, cert, &eku); + if (ret) + return ret; + for (i = 0; i < eku.len; i++) { + ret = hx509_ca_tbs_add_eku(context, tbs, &eku.val[i]); + if (ret) { + free_ExtKeyUsage(&eku); + return ret; + } + } + free_ExtKeyUsage(&eku); + } + 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, + int pathLenConstraint) +{ + tbs->flags.ca = 1; + tbs->pathLenConstraint = pathLenConstraint; + 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, + int pathLenConstraint) +{ + tbs->flags.proxy = 1; + tbs->pathLenConstraint = pathLenConstraint; + return 0; +} + + +/** + * 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) +{ + tbs->flags.domaincontroller = 1; + 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, + const SubjectPublicKeyInfo *spki) +{ + int ret; + free_SubjectPublicKeyInfo(&tbs->spki); + ret = copy_SubjectPublicKeyInfo(spki, &tbs->spki); + tbs->flags.key = !ret; + 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, + const heim_integer *serialNumber) +{ + int ret; + der_free_heim_integer(&tbs->serial); + ret = der_copy_heim_integer(serialNumber, &tbs->serial); + tbs->flags.serial = !ret; + 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, + const heim_oid *oid) +{ + void *ptr; + int ret; + unsigned i; + + /* search for duplicates */ + for (i = 0; i < tbs->eku.len; i++) { + if (der_heim_oid_cmp(oid, &tbs->eku.val[i]) == 0) + return 0; + } + + ptr = realloc(tbs->eku.val, sizeof(tbs->eku.val[0]) * (tbs->eku.len + 1)); + if (ptr == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + tbs->eku.val = ptr; + ret = der_copy_oid(oid, &tbs->eku.val[tbs->eku.len]); + if (ret) { + hx509_set_error_string(context, 0, ret, "out of memory"); + return ret; + } + tbs->eku.len += 1; + 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, + const char *uri, + hx509_name issuername) +{ + DistributionPoint dp; + int ret; + + memset(&dp, 0, sizeof(dp)); + + dp.distributionPoint = ecalloc(1, sizeof(*dp.distributionPoint)); + + { + DistributionPointName name; + GeneralName gn; + size_t size; + + name.element = choice_DistributionPointName_fullName; + name.u.fullName.len = 1; + name.u.fullName.val = &gn; + + gn.element = choice_GeneralName_uniformResourceIdentifier; + gn.u.uniformResourceIdentifier = rk_UNCONST(uri); + + ASN1_MALLOC_ENCODE(DistributionPointName, + dp.distributionPoint->data, + dp.distributionPoint->length, + &name, &size, ret); + if (ret) { + hx509_set_error_string(context, 0, ret, + "Failed to encoded DistributionPointName"); + goto out; + } + if (dp.distributionPoint->length != size) + _hx509_abort("internal ASN.1 encoder error"); + } + + if (issuername) { +#if 1 + /** + * issuername not supported + */ + hx509_set_error_string(context, 0, EINVAL, + "CRLDistributionPoints.name.issuername not yet supported"); + return EINVAL; +#else + GeneralNames *crlissuer; + GeneralName gn; + Name n; + + crlissuer = calloc(1, sizeof(*crlissuer)); + if (crlissuer == NULL) { + return ENOMEM; + } + memset(&gn, 0, sizeof(gn)); + + gn.element = choice_GeneralName_directoryName; + ret = hx509_name_to_Name(issuername, &n); + if (ret) { + hx509_set_error_string(context, 0, ret, "out of memory"); + goto out; + } + + gn.u.directoryName.element = n.element; + gn.u.directoryName.u.rdnSequence = n.u.rdnSequence; + + ret = add_GeneralNames(&crlissuer, &gn); + free_Name(&n); + if (ret) { + hx509_set_error_string(context, 0, ret, "out of memory"); + goto out; + } + + dp.cRLIssuer = &crlissuer; +#endif + } + + ret = add_CRLDistributionPoints(&tbs->crldp, &dp); + if (ret) { + hx509_set_error_string(context, 0, ret, "out of memory"); + goto out; + } + +out: + free_DistributionPoint(&dp); + + 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, + const heim_oid *oid, + const heim_octet_string *os) +{ + GeneralName gn; + + memset(&gn, 0, sizeof(gn)); + gn.element = choice_GeneralName_otherName; + gn.u.otherName.type_id = *oid; + gn.u.otherName.value = *os; + + 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, + hx509_ca_tbs tbs, + const char *principal) +{ + heim_octet_string os; + KRB5PrincipalName p; + size_t size; + int ret; + char *s = NULL; + + memset(&p, 0, sizeof(p)); + + /* parse principal */ + { + const char *str; + char *q; + int n; + + /* count number of component */ + n = 1; + for(str = principal; *str != '\0' && *str != '@'; str++){ + if(*str=='\\'){ + if(str[1] == '\0' || str[1] == '@') { + ret = HX509_PARSING_NAME_FAILED; + hx509_set_error_string(context, 0, ret, + "trailing \\ in principal name"); + goto out; + } + str++; + } else if(*str == '/') + n++; + } + p.principalName.name_string.val = + calloc(n, sizeof(*p.principalName.name_string.val)); + if (p.principalName.name_string.val == NULL) { + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, "malloc: out of memory"); + goto out; + } + p.principalName.name_string.len = n; + + p.principalName.name_type = KRB5_NT_PRINCIPAL; + q = s = strdup(principal); + if (q == NULL) { + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, "malloc: out of memory"); + goto out; + } + p.realm = strrchr(q, '@'); + if (p.realm == NULL) { + ret = HX509_PARSING_NAME_FAILED; + hx509_set_error_string(context, 0, ret, "Missing @ in principal"); + goto out; + }; + *p.realm++ = '\0'; + + n = 0; + while (q) { + p.principalName.name_string.val[n++] = q; + q = strchr(q, '/'); + if (q) + *q++ = '\0'; + } + } + + ASN1_MALLOC_ENCODE(KRB5PrincipalName, os.data, os.length, &p, &size, ret); + if (ret) { + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + if (size != os.length) + _hx509_abort("internal ASN.1 encoder error"); + + ret = hx509_ca_tbs_add_san_otherName(context, + tbs, + oid_id_pkinit_san(), + &os); + free(os.data); +out: + if (p.principalName.name_string.val) + free (p.principalName.name_string.val); + if (s) + free(s); + return ret; +} + +/* + * + */ + +static int +add_utf8_san(hx509_context context, + hx509_ca_tbs tbs, + const heim_oid *oid, + const char *string) +{ + const PKIXXmppAddr ustring = (const PKIXXmppAddr)string; + heim_octet_string os; + size_t size; + int ret; + + os.length = 0; + os.data = NULL; + + ASN1_MALLOC_ENCODE(PKIXXmppAddr, os.data, os.length, &ustring, &size, ret); + if (ret) { + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + if (size != os.length) + _hx509_abort("internal ASN.1 encoder error"); + + ret = hx509_ca_tbs_add_san_otherName(context, + tbs, + oid, + &os); + free(os.data); +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, + const char *principal) +{ + 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, + const char *jid) +{ + return add_utf8_san(context, tbs, oid_id_pkix_on_xmppAddr(), jid); +} + + +/** + * 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, + const char *dnsname) +{ + GeneralName gn; + + memset(&gn, 0, sizeof(gn)); + gn.element = choice_GeneralName_dNSName; + gn.u.dNSName = rk_UNCONST(dnsname); + + 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, + const char *rfc822Name) +{ + GeneralName gn; + + memset(&gn, 0, sizeof(gn)); + gn.element = choice_GeneralName_rfc822Name; + gn.u.rfc822Name = rk_UNCONST(rfc822Name); + + 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, + hx509_ca_tbs tbs, + hx509_name subject) +{ + if (tbs->subject) + hx509_name_free(&tbs->subject); + 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, + hx509_env env) +{ + return hx509_name_expand(context, tbs->subject, env); +} + +static int +add_extension(hx509_context context, + TBSCertificate *tbsc, + int critical_flag, + const heim_oid *oid, + const heim_octet_string *data) +{ + Extension ext; + int ret; + + memset(&ext, 0, sizeof(ext)); + + if (critical_flag) { + ext.critical = malloc(sizeof(*ext.critical)); + if (ext.critical == NULL) { + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + *ext.critical = TRUE; + } + + ret = der_copy_oid(oid, &ext.extnID); + if (ret) { + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + ret = der_copy_octet_string(data, &ext.extnValue); + if (ret) { + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + ret = add_Extensions(tbsc->extensions, &ext); + if (ret) { + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } +out: + free_Extension(&ext); + return ret; +} + +static int +build_proxy_prefix(hx509_context context, const Name *issuer, Name *subject) +{ + char *tstr; + time_t t; + int ret; + + ret = copy_Name(issuer, subject); + if (ret) { + hx509_set_error_string(context, 0, ret, + "Failed to copy subject name"); + return ret; + } + + t = time(NULL); + asprintf(&tstr, "ts-%lu", (unsigned long)t); + if (tstr == NULL) { + hx509_set_error_string(context, 0, ENOMEM, + "Failed to copy subject name"); + return ENOMEM; + } + /* prefix with CN=<ts>,...*/ + ret = _hx509_name_modify(context, subject, 1, oid_id_at_commonName(), tstr); + free(tstr); + if (ret) + free_Name(subject); + return ret; +} + +static int +ca_sign(hx509_context context, + hx509_ca_tbs tbs, + hx509_private_key signer, + const AuthorityKeyIdentifier *ai, + const Name *issuername, + hx509_cert *certificate) +{ + heim_octet_string data; + Certificate c; + TBSCertificate *tbsc; + size_t size; + int ret; + const AlgorithmIdentifier *sigalg; + time_t notBefore; + time_t notAfter; + unsigned key_usage; + + sigalg = _hx509_crypto_default_sig_alg; + + memset(&c, 0, sizeof(c)); + + /* + * Default values are: Valid since 24h ago, valid one year into + * the future, KeyUsage digitalSignature and keyEncipherment set, + * and keyCertSign for CA certificates. + */ + notBefore = tbs->notBefore; + if (notBefore == 0) + notBefore = time(NULL) - 3600 * 24; + notAfter = tbs->notAfter; + if (notAfter == 0) + notAfter = time(NULL) + 3600 * 24 * 365; + + key_usage = tbs->key_usage; + if (key_usage == 0) { + KeyUsage ku; + memset(&ku, 0, sizeof(ku)); + ku.digitalSignature = 1; + ku.keyEncipherment = 1; + key_usage = KeyUsage2int(ku); + } + + if (tbs->flags.ca) { + KeyUsage ku; + memset(&ku, 0, sizeof(ku)); + ku.keyCertSign = 1; + ku.cRLSign = 1; + key_usage |= KeyUsage2int(ku); + } + + /* + * + */ + + tbsc = &c.tbsCertificate; + + if (tbs->flags.key == 0) { + ret = EINVAL; + hx509_set_error_string(context, 0, ret, "No public key set"); + return ret; + } + /* + * Don't put restrictions on proxy certificate's subject name, it + * will be generated below. + */ + if (!tbs->flags.proxy) { + if (tbs->subject == NULL) { + hx509_set_error_string(context, 0, EINVAL, "No subject name set"); + return EINVAL; + } + if (hx509_name_is_null_p(tbs->subject) && tbs->san.len == 0) { + hx509_set_error_string(context, 0, EINVAL, + "NULL subject and no SubjectAltNames"); + return EINVAL; + } + } + if (tbs->flags.ca && tbs->flags.proxy) { + hx509_set_error_string(context, 0, EINVAL, "Can't be proxy and CA " + "at the same time"); + return EINVAL; + } + if (tbs->flags.proxy) { + if (tbs->san.len > 0) { + hx509_set_error_string(context, 0, EINVAL, + "Proxy certificate is not allowed " + "to have SubjectAltNames"); + return EINVAL; + } + } + + /* version [0] Version OPTIONAL, -- EXPLICIT nnn DEFAULT 1, */ + tbsc->version = calloc(1, sizeof(*tbsc->version)); + if (tbsc->version == NULL) { + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + *tbsc->version = rfc3280_version_3; + /* serialNumber CertificateSerialNumber, */ + if (tbs->flags.serial) { + ret = der_copy_heim_integer(&tbs->serial, &tbsc->serialNumber); + if (ret) { + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + } else { + tbsc->serialNumber.length = 20; + tbsc->serialNumber.data = malloc(tbsc->serialNumber.length); + if (tbsc->serialNumber.data == NULL){ + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + /* XXX diffrent */ + RAND_bytes(tbsc->serialNumber.data, tbsc->serialNumber.length); + ((unsigned char *)tbsc->serialNumber.data)[0] &= 0x7f; + } + /* signature AlgorithmIdentifier, */ + ret = copy_AlgorithmIdentifier(sigalg, &tbsc->signature); + if (ret) { + hx509_set_error_string(context, 0, ret, "Failed to copy sigature alg"); + goto out; + } + /* issuer Name, */ + if (issuername) + ret = copy_Name(issuername, &tbsc->issuer); + else + ret = hx509_name_to_Name(tbs->subject, &tbsc->issuer); + if (ret) { + hx509_set_error_string(context, 0, ret, "Failed to copy issuer name"); + goto out; + } + /* validity Validity, */ + tbsc->validity.notBefore.element = choice_Time_generalTime; + tbsc->validity.notBefore.u.generalTime = notBefore; + tbsc->validity.notAfter.element = choice_Time_generalTime; + tbsc->validity.notAfter.u.generalTime = notAfter; + /* subject Name, */ + if (tbs->flags.proxy) { + ret = build_proxy_prefix(context, &tbsc->issuer, &tbsc->subject); + if (ret) + goto out; + } else { + ret = hx509_name_to_Name(tbs->subject, &tbsc->subject); + if (ret) { + hx509_set_error_string(context, 0, ret, + "Failed to copy subject name"); + goto out; + } + } + /* subjectPublicKeyInfo SubjectPublicKeyInfo, */ + ret = copy_SubjectPublicKeyInfo(&tbs->spki, &tbsc->subjectPublicKeyInfo); + if (ret) { + hx509_set_error_string(context, 0, ret, "Failed to copy spki"); + goto out; + } + /* issuerUniqueID [1] IMPLICIT BIT STRING OPTIONAL */ + /* subjectUniqueID [2] IMPLICIT BIT STRING OPTIONAL */ + /* extensions [3] EXPLICIT Extensions OPTIONAL */ + tbsc->extensions = calloc(1, sizeof(*tbsc->extensions)); + if (tbsc->extensions == NULL) { + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + + /* Add the text BMP string Domaincontroller to the cert */ + if (tbs->flags.domaincontroller) { + data.data = rk_UNCONST("\x1e\x20\x00\x44\x00\x6f\x00\x6d" + "\x00\x61\x00\x69\x00\x6e\x00\x43" + "\x00\x6f\x00\x6e\x00\x74\x00\x72" + "\x00\x6f\x00\x6c\x00\x6c\x00\x65" + "\x00\x72"); + data.length = 34; + + ret = add_extension(context, tbsc, 0, + oid_id_ms_cert_enroll_domaincontroller(), + &data); + if (ret) + goto out; + } + + /* add KeyUsage */ + { + KeyUsage ku; + + ku = int2KeyUsage(key_usage); + ASN1_MALLOC_ENCODE(KeyUsage, data.data, data.length, &ku, &size, ret); + if (ret) { + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + if (size != data.length) + _hx509_abort("internal ASN.1 encoder error"); + ret = add_extension(context, tbsc, 1, + oid_id_x509_ce_keyUsage(), &data); + free(data.data); + if (ret) + goto out; + } + + /* add ExtendedKeyUsage */ + if (tbs->eku.len > 0) { + ASN1_MALLOC_ENCODE(ExtKeyUsage, data.data, data.length, + &tbs->eku, &size, ret); + if (ret) { + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + if (size != data.length) + _hx509_abort("internal ASN.1 encoder error"); + ret = add_extension(context, tbsc, 0, + oid_id_x509_ce_extKeyUsage(), &data); + free(data.data); + if (ret) + goto out; + } + + /* add Subject Alternative Name */ + if (tbs->san.len > 0) { + ASN1_MALLOC_ENCODE(GeneralNames, data.data, data.length, + &tbs->san, &size, ret); + if (ret) { + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + if (size != data.length) + _hx509_abort("internal ASN.1 encoder error"); + ret = add_extension(context, tbsc, 0, + oid_id_x509_ce_subjectAltName(), + &data); + free(data.data); + if (ret) + goto out; + } + + /* Add Authority Key Identifier */ + if (ai) { + ASN1_MALLOC_ENCODE(AuthorityKeyIdentifier, data.data, data.length, + ai, &size, ret); + if (ret) { + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + if (size != data.length) + _hx509_abort("internal ASN.1 encoder error"); + ret = add_extension(context, tbsc, 0, + oid_id_x509_ce_authorityKeyIdentifier(), + &data); + free(data.data); + if (ret) + goto out; + } + + /* Add Subject Key Identifier */ + { + SubjectKeyIdentifier si; + unsigned char hash[SHA_DIGEST_LENGTH]; + + { + SHA_CTX m; + + SHA1_Init(&m); + SHA1_Update(&m, tbs->spki.subjectPublicKey.data, + tbs->spki.subjectPublicKey.length / 8); + SHA1_Final (hash, &m); + } + + si.data = hash; + si.length = sizeof(hash); + + ASN1_MALLOC_ENCODE(SubjectKeyIdentifier, data.data, data.length, + &si, &size, ret); + if (ret) { + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + if (size != data.length) + _hx509_abort("internal ASN.1 encoder error"); + ret = add_extension(context, tbsc, 0, + oid_id_x509_ce_subjectKeyIdentifier(), + &data); + free(data.data); + if (ret) + goto out; + } + + /* Add BasicConstraints */ + { + BasicConstraints bc; + int aCA = 1; + unsigned int path; + + memset(&bc, 0, sizeof(bc)); + + if (tbs->flags.ca) { + bc.cA = &aCA; + if (tbs->pathLenConstraint >= 0) { + path = tbs->pathLenConstraint; + bc.pathLenConstraint = &path; + } + } + + ASN1_MALLOC_ENCODE(BasicConstraints, data.data, data.length, + &bc, &size, ret); + if (ret) { + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + if (size != data.length) + _hx509_abort("internal ASN.1 encoder error"); + /* Critical if this is a CA */ + ret = add_extension(context, tbsc, tbs->flags.ca, + oid_id_x509_ce_basicConstraints(), + &data); + free(data.data); + if (ret) + goto out; + } + + /* add Proxy */ + if (tbs->flags.proxy) { + ProxyCertInfo info; + + memset(&info, 0, sizeof(info)); + + if (tbs->pathLenConstraint >= 0) { + info.pCPathLenConstraint = + malloc(sizeof(*info.pCPathLenConstraint)); + if (info.pCPathLenConstraint == NULL) { + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + *info.pCPathLenConstraint = tbs->pathLenConstraint; + } + + ret = der_copy_oid(oid_id_pkix_ppl_inheritAll(), + &info.proxyPolicy.policyLanguage); + if (ret) { + free_ProxyCertInfo(&info); + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + + ASN1_MALLOC_ENCODE(ProxyCertInfo, data.data, data.length, + &info, &size, ret); + free_ProxyCertInfo(&info); + if (ret) { + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + if (size != data.length) + _hx509_abort("internal ASN.1 encoder error"); + ret = add_extension(context, tbsc, 0, + oid_id_pkix_pe_proxyCertInfo(), + &data); + free(data.data); + if (ret) + goto out; + } + + if (tbs->crldp.len) { + + ASN1_MALLOC_ENCODE(CRLDistributionPoints, data.data, data.length, + &tbs->crldp, &size, ret); + if (ret) { + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + if (size != data.length) + _hx509_abort("internal ASN.1 encoder error"); + ret = add_extension(context, tbsc, FALSE, + oid_id_x509_ce_cRLDistributionPoints(), + &data); + free(data.data); + if (ret) + goto out; + } + + ASN1_MALLOC_ENCODE(TBSCertificate, data.data, data.length,tbsc, &size, ret); + if (ret) { + hx509_set_error_string(context, 0, ret, "malloc out of memory"); + goto out; + } + if (data.length != size) + _hx509_abort("internal ASN.1 encoder error"); + + ret = _hx509_create_signature_bitstring(context, + signer, + sigalg, + &data, + &c.signatureAlgorithm, + &c.signatureValue); + free(data.data); + if (ret) + goto out; + + ret = hx509_cert_init(context, &c, certificate); + if (ret) + goto out; + + free_Certificate(&c); + + return 0; + +out: + free_Certificate(&c); + return ret; +} + +static int +get_AuthorityKeyIdentifier(hx509_context context, + const Certificate *certificate, + AuthorityKeyIdentifier *ai) +{ + SubjectKeyIdentifier si; + int ret; + + ret = _hx509_find_extension_subject_key_id(certificate, &si); + if (ret == 0) { + ai->keyIdentifier = calloc(1, sizeof(*ai->keyIdentifier)); + if (ai->keyIdentifier == NULL) { + free_SubjectKeyIdentifier(&si); + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + ret = der_copy_octet_string(&si, ai->keyIdentifier); + free_SubjectKeyIdentifier(&si); + if (ret) { + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + } else { + GeneralNames gns; + GeneralName gn; + Name name; + + memset(&gn, 0, sizeof(gn)); + memset(&gns, 0, sizeof(gns)); + memset(&name, 0, sizeof(name)); + + ai->authorityCertIssuer = + calloc(1, sizeof(*ai->authorityCertIssuer)); + if (ai->authorityCertIssuer == NULL) { + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + ai->authorityCertSerialNumber = + calloc(1, sizeof(*ai->authorityCertSerialNumber)); + if (ai->authorityCertSerialNumber == NULL) { + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + + /* + * XXX unbreak when asn1 compiler handle IMPLICIT + * + * This is so horrible. + */ + + ret = copy_Name(&certificate->tbsCertificate.subject, &name); + if (ai->authorityCertSerialNumber == NULL) { + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + + memset(&gn, 0, sizeof(gn)); + gn.element = choice_GeneralName_directoryName; + gn.u.directoryName.element = + choice_GeneralName_directoryName_rdnSequence; + gn.u.directoryName.u.rdnSequence = name.u.rdnSequence; + + ret = add_GeneralNames(&gns, &gn); + if (ret) { + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + + ai->authorityCertIssuer->val = gns.val; + ai->authorityCertIssuer->len = gns.len; + + ret = der_copy_heim_integer(&certificate->tbsCertificate.serialNumber, + ai->authorityCertSerialNumber); + if (ai->authorityCertSerialNumber == NULL) { + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + } +out: + if (ret) + free_AuthorityKeyIdentifier(ai); + return ret; +} + + +/** + * 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, + hx509_cert signer, + hx509_cert *certificate) +{ + const Certificate *signer_cert; + AuthorityKeyIdentifier ai; + int ret; + + memset(&ai, 0, sizeof(ai)); + + signer_cert = _hx509_get_cert(signer); + + ret = get_AuthorityKeyIdentifier(context, signer_cert, &ai); + if (ret) + goto out; + + ret = ca_sign(context, + tbs, + _hx509_cert_private_key(signer), + &ai, + &signer_cert->tbsCertificate.subject, + certificate); + +out: + free_AuthorityKeyIdentifier(&ai); + + 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, + hx509_private_key signer, + hx509_cert *certificate) +{ + return ca_sign(context, + tbs, + signer, + NULL, + NULL, + certificate); +} diff --git a/source4/heimdal/lib/hx509/cert.c b/source4/heimdal/lib/hx509/cert.c new file mode 100644 index 0000000000..3597896c0c --- /dev/null +++ b/source4/heimdal/lib/hx509/cert.c @@ -0,0 +1,3360 @@ +/* + * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hx_locl.h" +RCSID("$Id$"); +#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; +#define HX509_VERIFY_CTX_F_TIME_SET 1 +#define HX509_VERIFY_CTX_F_ALLOW_PROXY_CERTIFICATE 2 +#define HX509_VERIFY_CTX_F_REQUIRE_RFC3280 4 +#define HX509_VERIFY_CTX_F_CHECK_TRUST_ANCHORS 8 +#define HX509_VERIFY_CTX_F_NO_DEFAULT_ANCHORS 16 + time_t time_now; + unsigned int max_depth; +#define HX509_VERIFY_MAX_DEPTH 30 + hx509_revoke_ctx revoke_ctx; +}; + +#define REQUIRE_RFC3280(ctx) ((ctx)->flags & HX509_VERIFY_CTX_F_REQUIRE_RFC3280) +#define CHECK_TA(ctx) ((ctx)->flags & HX509_VERIFY_CTX_F_CHECK_TRUST_ANCHORS) +#define ALLOW_DEF_TA(ctx) (((ctx)->flags & HX509_VERIFY_CTX_F_NO_DEFAULT_ANCHORS) == 0) + +struct _hx509_cert_attrs { + size_t len; + hx509_cert_attribute *val; +}; + +struct hx509_cert_data { + unsigned int ref; + char *friendlyname; + Certificate *data; + hx509_private_key private_key; + struct _hx509_cert_attrs attrs; + hx509_name basename; + _hx509_cert_release_func release; + void *ctx; +}; + +typedef struct hx509_name_constraints { + NameConstraints *val; + size_t len; +} 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 +hx509_context_init(hx509_context *context) +{ + *context = calloc(1, sizeof(**context)); + if (*context == NULL) + return ENOMEM; + + _hx509_ks_null_register(*context); + _hx509_ks_mem_register(*context); + _hx509_ks_file_register(*context); + _hx509_ks_pkcs12_register(*context); + _hx509_ks_pkcs11_register(*context); + _hx509_ks_dir_register(*context); + _hx509_ks_keychain_register(*context); + + ENGINE_add_conf_module(); + OpenSSL_add_all_algorithms(); + + (*context)->ocsp_time_diff = HX509_DEFAULT_OCSP_TIME_DIFF; + + initialize_hx_error_table_r(&(*context)->et_list); + initialize_asn1_error_table_r(&(*context)->et_list); + +#ifdef HX509_DEFAULT_ANCHORS + (void)hx509_certs_init(*context, HX509_DEFAULT_ANCHORS, 0, + NULL, &(*context)->default_trust_anchors); +#endif + + return 0; +} + +/** + * Selects if the hx509_revoke_verify() function is going to require + * the existans of a revokation method (OCSP, 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) +{ + if (flag) + context->flags |= HX509_CTX_VERIFY_MISSING_OK; + else + 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) +{ + hx509_clear_error_string(*context); + if ((*context)->ks_ops) { + free((*context)->ks_ops); + (*context)->ks_ops = NULL; + } + (*context)->ks_num_ops = 0; + free_error_table ((*context)->et_list); + if ((*context)->querystat) + free((*context)->querystat); + memset(*context, 0, sizeof(**context)); + free(*context); + *context = NULL; +} + +/* + * + */ + +Certificate * +_hx509_get_cert(hx509_cert cert) +{ + return cert->data; +} + +/* + * + */ + +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) +{ + int ret; + + *cert = malloc(sizeof(**cert)); + if (*cert == NULL) + return ENOMEM; + (*cert)->ref = 1; + (*cert)->friendlyname = NULL; + (*cert)->attrs.len = 0; + (*cert)->attrs.val = NULL; + (*cert)->private_key = NULL; + (*cert)->basename = NULL; + (*cert)->release = NULL; + (*cert)->ctx = NULL; + + (*cert)->data = calloc(1, sizeof(*(*cert)->data)); + if ((*cert)->data == NULL) { + free(*cert); + return ENOMEM; + } + ret = copy_Certificate(c, (*cert)->data); + if (ret) { + free((*cert)->data); + free(*cert); + *cert = NULL; + } + 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, + size_t len, + hx509_cert *cert) +{ + Certificate t; + size_t size; + int ret; + + ret = decode_Certificate(ptr, len, &t, &size); + if (ret) { + hx509_set_error_string(context, 0, ret, "Failed to decode certificate"); + return ret; + } + if (size != len) { + hx509_set_error_string(context, 0, HX509_EXTRA_DATA_AFTER_STRUCTURE, + "Extra data after certificate"); + return HX509_EXTRA_DATA_AFTER_STRUCTURE; + } + + ret = hx509_cert_init(context, &t, cert); + free_Certificate(&t); + return ret; +} + +void +_hx509_cert_set_release(hx509_cert cert, + _hx509_cert_release_func release, + void *ctx) +{ + cert->release = release; + cert->ctx = ctx; +} + + +/* Doesn't make a copy of `private_key'. */ + +int +_hx509_cert_assign_key(hx509_cert cert, hx509_private_key private_key) +{ + if (cert->private_key) + _hx509_private_key_free(&cert->private_key); + cert->private_key = _hx509_private_key_ref(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) +{ + int i; + + if (cert == NULL) + return; + + if (cert->ref <= 0) + _hx509_abort("cert refcount <= 0 on free"); + if (--cert->ref > 0) + return; + + if (cert->release) + (cert->release)(cert, cert->ctx); + + if (cert->private_key) + _hx509_private_key_free(&cert->private_key); + + free_Certificate(cert->data); + free(cert->data); + + for (i = 0; i < cert->attrs.len; i++) { + der_free_octet_string(&cert->attrs.val[i]->data); + der_free_oid(&cert->attrs.val[i]->oid); + free(cert->attrs.val[i]); + } + free(cert->attrs.val); + free(cert->friendlyname); + if (cert->basename) + hx509_name_free(&cert->basename); + memset(cert, 0, sizeof(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++; + if (cert->ref == 0) + _hx509_abort("cert refcount == 0"); + 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) +{ + hx509_verify_ctx c; + + c = calloc(1, sizeof(*c)); + if (c == NULL) + return ENOMEM; + + c->max_depth = HX509_VERIFY_MAX_DEPTH; + + *ctx = c; + + 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) { + 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 = _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) +{ + 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) +{ + ctx->flags |= HX509_VERIFY_CTX_F_TIME_SET; + ctx->time_now = t; +} + +time_t +_hx509_verify_get_time(hx509_verify_ctx ctx) +{ + return ctx->time_now; +} + +/** + * 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) +{ + if (boolean) + ctx->flags |= HX509_VERIFY_CTX_F_ALLOW_PROXY_CERTIFICATE; + else + 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) +{ + if (boolean) + ctx->flags |= HX509_VERIFY_CTX_F_REQUIRE_RFC3280; + else + 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) +{ + if (boolean) + ctx->flags &= ~HX509_VERIFY_CTX_F_NO_DEFAULT_ANCHORS; + else + ctx->flags |= HX509_VERIFY_CTX_F_NO_DEFAULT_ANCHORS; +} + +static const Extension * +find_extension(const Certificate *cert, const heim_oid *oid, int *idx) +{ + const TBSCertificate *c = &cert->tbsCertificate; + + if (c->version == NULL || *c->version < 2 || c->extensions == NULL) + return NULL; + + for (;*idx < c->extensions->len; (*idx)++) { + if (der_heim_oid_cmp(&c->extensions->val[*idx].extnID, oid) == 0) + return &c->extensions->val[(*idx)++]; + } + return NULL; +} + +static int +find_extension_auth_key_id(const Certificate *subject, + AuthorityKeyIdentifier *ai) +{ + const Extension *e; + size_t size; + int i = 0; + + memset(ai, 0, sizeof(*ai)); + + e = find_extension(subject, oid_id_x509_ce_authorityKeyIdentifier(), &i); + if (e == NULL) + return HX509_EXTENSION_NOT_FOUND; + + return decode_AuthorityKeyIdentifier(e->extnValue.data, + e->extnValue.length, + ai, &size); +} + +int +_hx509_find_extension_subject_key_id(const Certificate *issuer, + SubjectKeyIdentifier *si) +{ + const Extension *e; + size_t size; + int i = 0; + + memset(si, 0, sizeof(*si)); + + e = find_extension(issuer, oid_id_x509_ce_subjectKeyIdentifier(), &i); + if (e == NULL) + return HX509_EXTENSION_NOT_FOUND; + + return decode_SubjectKeyIdentifier(e->extnValue.data, + e->extnValue.length, + si, &size); +} + +static int +find_extension_name_constraints(const Certificate *subject, + NameConstraints *nc) +{ + const Extension *e; + size_t size; + int i = 0; + + memset(nc, 0, sizeof(*nc)); + + e = find_extension(subject, oid_id_x509_ce_nameConstraints(), &i); + if (e == NULL) + return HX509_EXTENSION_NOT_FOUND; + + return decode_NameConstraints(e->extnValue.data, + e->extnValue.length, + nc, &size); +} + +static int +find_extension_subject_alt_name(const Certificate *cert, int *i, + GeneralNames *sa) +{ + const Extension *e; + size_t size; + + memset(sa, 0, sizeof(*sa)); + + e = find_extension(cert, oid_id_x509_ce_subjectAltName(), i); + if (e == NULL) + return HX509_EXTENSION_NOT_FOUND; + + return decode_GeneralNames(e->extnValue.data, + e->extnValue.length, + sa, &size); +} + +static int +find_extension_eku(const Certificate *cert, ExtKeyUsage *eku) +{ + const Extension *e; + size_t size; + int i = 0; + + memset(eku, 0, sizeof(*eku)); + + e = find_extension(cert, oid_id_x509_ce_extKeyUsage(), &i); + if (e == NULL) + return HX509_EXTENSION_NOT_FOUND; + + return decode_ExtKeyUsage(e->extnValue.data, + e->extnValue.length, + eku, &size); +} + +static int +add_to_list(hx509_octet_string_list *list, const heim_octet_string *entry) +{ + void *p; + int ret; + + p = realloc(list->val, (list->len + 1) * sizeof(list->val[0])); + if (p == NULL) + return ENOMEM; + list->val = p; + ret = der_copy_octet_string(entry, &list->val[list->len]); + if (ret) + return ret; + list->len++; + 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) +{ + int i; + for (i = 0; i < list->len; i++) + der_free_octet_string(&list->val[i]); + free(list->val); + list->val = NULL; + 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_context context, + hx509_cert cert, + const heim_oid *oid, + hx509_octet_string_list *list) +{ + GeneralNames sa; + int ret, i, j; + + list->val = NULL; + list->len = 0; + + i = 0; + while (1) { + ret = find_extension_subject_alt_name(_hx509_get_cert(cert), &i, &sa); + i++; + if (ret == HX509_EXTENSION_NOT_FOUND) { + 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 && + der_heim_oid_cmp(&sa.val[j].u.otherName.type_id, oid) == 0) + { + 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; + } + } + } + free_GeneralNames(&sa); + } + return 0; +} + + +static int +check_key_usage(hx509_context context, const Certificate *cert, + unsigned flags, int req_present) +{ + const Extension *e; + KeyUsage ku; + size_t size; + int ret, i = 0; + unsigned ku_flags; + + if (_hx509_cert_get_version(cert) < 3) + return 0; + + e = find_extension(cert, oid_id_x509_ce_keyUsage(), &i); + if (e == NULL) { + if (req_present) { + hx509_set_error_string(context, 0, HX509_KU_CERT_MISSING, + "Required extension key " + "usage missing from certifiate"); + return HX509_KU_CERT_MISSING; + } + return 0; + } + + ret = decode_KeyUsage(e->extnValue.data, e->extnValue.length, &ku, &size); + if (ret) + return ret; + ku_flags = KeyUsage2int(ku); + if ((ku_flags & flags) != flags) { + unsigned missing = (~ku_flags) & flags; + char buf[256], *name; + + unparse_flags(missing, asn1_KeyUsage_units(), buf, sizeof(buf)); + _hx509_unparse_Name(&cert->tbsCertificate.subject, &name); + hx509_set_error_string(context, 0, HX509_KU_CERT_MISSING, + "Key usage %s required but missing " + "from certifiate %s", buf, name); + free(name); + return HX509_KU_CERT_MISSING; + } + 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) +{ + return check_key_usage(context, _hx509_get_cert(cert), flags, req_present); +} + +enum certtype { PROXY_CERT, EE_CERT, CA_CERT }; + +static int +check_basic_constraints(hx509_context context, const Certificate *cert, + enum certtype type, int depth) +{ + BasicConstraints bc; + const Extension *e; + size_t size; + int ret, i = 0; + + if (_hx509_cert_get_version(cert) < 3) + return 0; + + e = find_extension(cert, oid_id_x509_ce_basicConstraints(), &i); + if (e == NULL) { + switch(type) { + case PROXY_CERT: + case EE_CERT: + return 0; + case CA_CERT: { + char *name; + ret = _hx509_unparse_Name(&cert->tbsCertificate.subject, &name); + assert(ret == 0); + hx509_set_error_string(context, 0, HX509_EXTENSION_NOT_FOUND, + "basicConstraints missing from " + "CA certifiacte %s", name); + free(name); + return HX509_EXTENSION_NOT_FOUND; + } + } + } + + ret = decode_BasicConstraints(e->extnValue.data, + e->extnValue.length, &bc, + &size); + if (ret) + return ret; + switch(type) { + case PROXY_CERT: + if (bc.cA != NULL && *bc.cA) + ret = HX509_PARENT_IS_CA; + break; + case EE_CERT: + ret = 0; + break; + case CA_CERT: + if (bc.cA == NULL || !*bc.cA) + ret = HX509_PARENT_NOT_CA; + else if (bc.pathLenConstraint) + if (depth - 1 > *bc.pathLenConstraint) + ret = HX509_CA_PATH_TOO_DEEP; + break; + } + free_BasicConstraints(&bc); + return ret; +} + +int +_hx509_cert_is_parent_cmp(const Certificate *subject, + const Certificate *issuer, + int allow_self_signed) +{ + int diff; + AuthorityKeyIdentifier ai; + SubjectKeyIdentifier si; + int ret_ai, ret_si, ret; + + ret = _hx509_name_cmp(&issuer->tbsCertificate.subject, + &subject->tbsCertificate.issuer, + &diff); + if (ret) + return ret; + if (diff) + return diff; + + memset(&ai, 0, sizeof(ai)); + memset(&si, 0, sizeof(si)); + + /* + * Try to find AuthorityKeyIdentifier, if it's not present in the + * subject certificate nor the parent. + */ + + ret_ai = find_extension_auth_key_id(subject, &ai); + if (ret_ai && ret_ai != HX509_EXTENSION_NOT_FOUND) + return 1; + ret_si = _hx509_find_extension_subject_key_id(issuer, &si); + if (ret_si && ret_si != HX509_EXTENSION_NOT_FOUND) + return -1; + + if (ret_si && ret_ai) + goto out; + if (ret_ai) + goto out; + if (ret_si) { + if (allow_self_signed) { + diff = 0; + goto out; + } else if (ai.keyIdentifier) { + diff = -1; + goto out; + } + } + + if (ai.keyIdentifier == NULL) { + Name name; + + if (ai.authorityCertIssuer == NULL) + return -1; + if (ai.authorityCertSerialNumber == NULL) + return -1; + + diff = der_heim_integer_cmp(ai.authorityCertSerialNumber, + &issuer->tbsCertificate.serialNumber); + if (diff) + return diff; + if (ai.authorityCertIssuer->len != 1) + return -1; + if (ai.authorityCertIssuer->val[0].element != choice_GeneralName_directoryName) + return -1; + + name.element = + ai.authorityCertIssuer->val[0].u.directoryName.element; + name.u.rdnSequence = + ai.authorityCertIssuer->val[0].u.directoryName.u.rdnSequence; + + ret = _hx509_name_cmp(&issuer->tbsCertificate.subject, + &name, + &diff); + if (ret) + return ret; + if (diff) + return diff; + diff = 0; + } else + diff = der_heim_octet_string_cmp(ai.keyIdentifier, &si); + if (diff) + goto out; + + out: + free_AuthorityKeyIdentifier(&ai); + free_SubjectKeyIdentifier(&si); + return diff; +} + +static int +certificate_is_anchor(hx509_context context, + hx509_certs trust_anchors, + const hx509_cert cert) +{ + hx509_query q; + hx509_cert c; + int ret; + + if (trust_anchors == NULL) + return 0; + + _hx509_query_clear(&q); + + q.match = HX509_QUERY_MATCH_CERTIFICATE; + q.certificate = _hx509_get_cert(cert); + + ret = hx509_certs_find(context, trust_anchors, &q, &c); + if (ret == 0) + hx509_cert_free(c); + return ret == 0; +} + +static int +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 it's empty set of relative DBs. + */ + +static int +subject_null_p(const Certificate *c) +{ + return c->tbsCertificate.subject.u.rdnSequence.len == 0; +} + + +static int +find_parent(hx509_context context, + time_t time_now, + hx509_certs trust_anchors, + hx509_path *path, + hx509_certs pool, + hx509_cert current, + hx509_cert *parent) +{ + AuthorityKeyIdentifier ai; + hx509_query q; + int ret; + + *parent = NULL; + memset(&ai, 0, sizeof(ai)); + + _hx509_query_clear(&q); + + if (!subject_null_p(current->data)) { + q.match |= HX509_QUERY_FIND_ISSUER_CERT; + q.subject = _hx509_get_cert(current); + } else { + ret = find_extension_auth_key_id(current->data, &ai); + if (ret) { + hx509_set_error_string(context, 0, HX509_CERTIFICATE_MALFORMED, + "Subjectless certificate missing AuthKeyID"); + return HX509_CERTIFICATE_MALFORMED; + } + + if (ai.keyIdentifier == NULL) { + free_AuthorityKeyIdentifier(&ai); + hx509_set_error_string(context, 0, HX509_CERTIFICATE_MALFORMED, + "Subjectless certificate missing keyIdentifier " + "inside AuthKeyID"); + return HX509_CERTIFICATE_MALFORMED; + } + + q.subject_id = ai.keyIdentifier; + q.match = HX509_QUERY_MATCH_SUBJECT_KEY_ID; + } + + q.path = path; + q.match |= HX509_QUERY_NO_MATCH_PATH; + + if (pool) { + q.timenow = time_now; + q.match |= HX509_QUERY_MATCH_TIME; + + ret = hx509_certs_find(context, pool, &q, parent); + if (ret == 0) { + free_AuthorityKeyIdentifier(&ai); + return 0; + } + q.match &= ~HX509_QUERY_MATCH_TIME; + } + + if (trust_anchors) { + ret = hx509_certs_find(context, trust_anchors, &q, parent); + if (ret == 0) { + free_AuthorityKeyIdentifier(&ai); + return ret; + } + } + free_AuthorityKeyIdentifier(&ai); + + { + hx509_name name; + char *str; + + ret = hx509_cert_get_subject(current, &name); + if (ret) { + hx509_clear_error_string(context); + return HX509_ISSUER_NOT_FOUND; + } + ret = hx509_name_to_string(name, &str); + hx509_name_free(&name); + if (ret) { + hx509_clear_error_string(context); + return HX509_ISSUER_NOT_FOUND; + } + + hx509_set_error_string(context, 0, HX509_ISSUER_NOT_FOUND, + "Failed to find issuer for " + "certificate with subject: '%s'", str); + free(str); + } + return HX509_ISSUER_NOT_FOUND; +} + +/* + * + */ + +static int +is_proxy_cert(hx509_context context, + const Certificate *cert, + ProxyCertInfo *rinfo) +{ + ProxyCertInfo info; + const Extension *e; + size_t size; + int ret, i = 0; + + if (rinfo) + memset(rinfo, 0, sizeof(*rinfo)); + + e = find_extension(cert, oid_id_pkix_pe_proxyCertInfo(), &i); + if (e == NULL) { + hx509_clear_error_string(context); + return HX509_EXTENSION_NOT_FOUND; + } + + ret = decode_ProxyCertInfo(e->extnValue.data, + e->extnValue.length, + &info, + &size); + if (ret) { + hx509_clear_error_string(context); + return ret; + } + if (size != e->extnValue.length) { + free_ProxyCertInfo(&info); + hx509_clear_error_string(context); + return HX509_EXTRA_DATA_AFTER_STRUCTURE; + } + if (rinfo == NULL) + free_ProxyCertInfo(&info); + else + *rinfo = info; + + return 0; +} + +/* + * Path operations are like MEMORY based keyset, but with exposed + * internal so we can do easy searches. + */ + +int +_hx509_path_append(hx509_context context, hx509_path *path, hx509_cert cert) +{ + hx509_cert *val; + val = realloc(path->val, (path->len + 1) * sizeof(path->val[0])); + if (val == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + + path->val = val; + path->val[path->len] = hx509_cert_ref(cert); + path->len++; + + return 0; +} + +void +_hx509_path_free(hx509_path *path) +{ + unsigned i; + + for (i = 0; i < path->len; i++) + hx509_cert_free(path->val[i]); + free(path->val); + path->val = NULL; + path->len = 0; +} + +/* + * Find path by looking up issuer for the top certificate and continue + * until an anchor certificate is found or max limit is found. A + * certificate never included twice in the path. + * + * If the trust anchors are not given, calculate optimistic path, just + * follow the chain upward until we no longer find a parent or we hit + * the max path limit. In this case, a failure will always be returned + * depending on what error condition is hit first. + * + * The path includes a path from the top certificate to the anchor + * certificate. + * + * The caller needs to free `path´ both on successful built path and + * failure. + */ + +int +_hx509_calculate_path(hx509_context context, + int flags, + time_t time_now, + hx509_certs anchors, + unsigned int max_depth, + hx509_cert cert, + hx509_certs pool, + hx509_path *path) +{ + hx509_cert parent, current; + int ret; + + if (max_depth == 0) + max_depth = HX509_VERIFY_MAX_DEPTH; + + ret = _hx509_path_append(context, path, cert); + if (ret) + return ret; + + current = hx509_cert_ref(cert); + + while (!certificate_is_anchor(context, anchors, current)) { + + ret = find_parent(context, time_now, anchors, path, + pool, current, &parent); + hx509_cert_free(current); + if (ret) + return ret; + + ret = _hx509_path_append(context, path, parent); + if (ret) + return ret; + current = parent; + + if (path->len > max_depth) { + hx509_cert_free(current); + hx509_set_error_string(context, 0, HX509_PATH_TOO_LONG, + "Path too long while bulding " + "certificate chain"); + return HX509_PATH_TOO_LONG; + } + } + + if ((flags & HX509_CALCULATE_PATH_NO_ANCHOR) && + path->len > 0 && + certificate_is_anchor(context, anchors, path->val[path->len - 1])) + { + hx509_cert_free(path->val[path->len - 1]); + path->len--; + } + + hx509_cert_free(current); + return 0; +} + +int +_hx509_AlgorithmIdentifier_cmp(const AlgorithmIdentifier *p, + const AlgorithmIdentifier *q) +{ + int diff; + diff = der_heim_oid_cmp(&p->algorithm, &q->algorithm); + if (diff) + return diff; + if (p->parameters) { + if (q->parameters) + return heim_any_cmp(p->parameters, + q->parameters); + else + return 1; + } else { + if (q->parameters) + return -1; + else + return 0; + } +} + +int +_hx509_Certificate_cmp(const Certificate *p, const Certificate *q) +{ + int diff; + diff = der_heim_bit_string_cmp(&p->signatureValue, &q->signatureValue); + if (diff) + return diff; + diff = _hx509_AlgorithmIdentifier_cmp(&p->signatureAlgorithm, + &q->signatureAlgorithm); + if (diff) + return diff; + diff = der_heim_octet_string_cmp(&p->tbsCertificate._save, + &q->tbsCertificate._save); + 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) +{ + if (c->basename) + return hx509_name_copy(context, c->basename, name); + if (is_proxy_cert(context, c->data, NULL) == 0) { + int ret = HX509_PROXY_CERTIFICATE_NOT_CANONICALIZED; + hx509_set_error_string(context, 0, ret, + "Proxy certificate have not been " + "canonicalize yet, no base name"); + return ret; + } + 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_AlgorithmIdentifier(hx509_context context, + hx509_cert p, + AlgorithmIdentifier *alg) +{ + 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) +{ + return p->private_key; +} + +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) + return 0; + return _hx509_private_key_exportable(p->private_key); +} + +int +_hx509_cert_private_decrypt(hx509_context context, + const heim_octet_string *ciphertext, + const heim_oid *encryption_oid, + hx509_cert p, + heim_octet_string *cleartext) +{ + cleartext->data = NULL; + cleartext->length = 0; + + if (p->private_key == NULL) { + hx509_set_error_string(context, 0, HX509_PRIVATE_KEY_MISSING, + "Private key missing"); + return HX509_PRIVATE_KEY_MISSING; + } + + return _hx509_private_key_private_decrypt(context, + ciphertext, + encryption_oid, + p->private_key, + cleartext); +} + +int +_hx509_cert_public_encrypt(hx509_context context, + const heim_octet_string *cleartext, + const hx509_cert p, + heim_oid *encryption_oid, + heim_octet_string *ciphertext) +{ + return _hx509_public_encrypt(context, + cleartext, p->data, + encryption_oid, ciphertext); +} + +/* + * + */ + +time_t +_hx509_Time2time_t(const Time *t) +{ + switch(t->element) { + case choice_Time_utcTime: + return t->u.utcTime; + case choice_Time_generalTime: + return t->u.generalTime; + } + return 0; +} + +/* + * + */ + +static int +init_name_constraints(hx509_name_constraints *nc) +{ + memset(nc, 0, sizeof(*nc)); + return 0; +} + +static int +add_name_constraints(hx509_context context, const Certificate *c, int not_ca, + hx509_name_constraints *nc) +{ + NameConstraints tnc; + int ret; + + ret = find_extension_name_constraints(c, &tnc); + if (ret == HX509_EXTENSION_NOT_FOUND) + return 0; + else if (ret) { + hx509_set_error_string(context, 0, ret, "Failed getting NameConstraints"); + return ret; + } else if (not_ca) { + ret = HX509_VERIFY_CONSTRAINTS; + hx509_set_error_string(context, 0, ret, "Not a CA and " + "have NameConstraints"); + } else { + NameConstraints *val; + val = realloc(nc->val, sizeof(nc->val[0]) * (nc->len + 1)); + if (val == NULL) { + hx509_clear_error_string(context); + ret = ENOMEM; + goto out; + } + nc->val = val; + ret = copy_NameConstraints(&tnc, &nc->val[nc->len]); + if (ret) { + hx509_clear_error_string(context); + goto out; + } + nc->len += 1; + } +out: + free_NameConstraints(&tnc); + return ret; +} + +static int +match_RDN(const RelativeDistinguishedName *c, + const RelativeDistinguishedName *n) +{ + int i; + + if (c->len != n->len) + 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; + 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; +} + +static int +match_X501Name(const Name *c, const Name *n) +{ + int i, ret; + + if (c->element != choice_Name_rdnSequence + || n->element != choice_Name_rdnSequence) + return 0; + if (c->u.rdnSequence.len > n->u.rdnSequence.len) + return HX509_NAME_CONSTRAINT_ERROR; + for (i = 0; i < c->u.rdnSequence.len; i++) { + ret = match_RDN(&c->u.rdnSequence.val[i], &n->u.rdnSequence.val[i]); + if (ret) + return ret; + } + return 0; +} + + +static int +match_general_name(const GeneralName *c, const GeneralName *n, int *match) +{ + /* + * Name constraints only apply to the same name type, see RFC3280, + * 4.2.1.11. + */ + assert(c->element == n->element); + + switch(c->element) { + case choice_GeneralName_otherName: + if (der_heim_oid_cmp(&c->u.otherName.type_id, + &n->u.otherName.type_id) != 0) + return HX509_NAME_CONSTRAINT_ERROR; + if (heim_any_cmp(&c->u.otherName.value, + &n->u.otherName.value) != 0) + return HX509_NAME_CONSTRAINT_ERROR; + *match = 1; + return 0; + case choice_GeneralName_rfc822Name: { + const char *s; + size_t len1, len2; + s = strchr(c->u.rfc822Name, '@'); + if (s) { + if (strcasecmp(c->u.rfc822Name, n->u.rfc822Name) != 0) + return HX509_NAME_CONSTRAINT_ERROR; + } else { + s = strchr(n->u.rfc822Name, '@'); + if (s == NULL) + return HX509_NAME_CONSTRAINT_ERROR; + len1 = strlen(c->u.rfc822Name); + len2 = strlen(s + 1); + if (len1 > len2) + 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 + 1] != '.') + return HX509_NAME_CONSTRAINT_ERROR; + } + *match = 1; + return 0; + } + case choice_GeneralName_dNSName: { + size_t lenc, lenn; + + lenc = strlen(c->u.dNSName); + lenn = strlen(n->u.dNSName); + if (lenc > lenn) + return HX509_NAME_CONSTRAINT_ERROR; + if (strcasecmp(&n->u.dNSName[lenn - lenc], c->u.dNSName) != 0) + return HX509_NAME_CONSTRAINT_ERROR; + if (lenc != lenn && n->u.dNSName[lenn - lenc - 1] != '.') + return HX509_NAME_CONSTRAINT_ERROR; + *match = 1; + return 0; + } + case choice_GeneralName_directoryName: { + Name c_name, n_name; + int ret; + + c_name._save.data = NULL; + c_name._save.length = 0; + c_name.element = c->u.directoryName.element; + c_name.u.rdnSequence = c->u.directoryName.u.rdnSequence; + + n_name._save.data = NULL; + n_name._save.length = 0; + n_name.element = n->u.directoryName.element; + n_name.u.rdnSequence = n->u.directoryName.u.rdnSequence; + + ret = match_X501Name(&c_name, &n_name); + if (ret == 0) + *match = 1; + return ret; + } + case choice_GeneralName_uniformResourceIdentifier: + case choice_GeneralName_iPAddress: + case choice_GeneralName_registeredID: + default: + return HX509_NAME_CONSTRAINT_ERROR; + } +} + +static int +match_alt_name(const GeneralName *n, const Certificate *c, + int *same, int *match) +{ + GeneralNames sa; + int ret, i, j; + + i = 0; + do { + ret = find_extension_subject_alt_name(c, &i, &sa); + if (ret == HX509_EXTENSION_NOT_FOUND) { + ret = 0; + break; + } else if (ret != 0) + break; + + for (j = 0; j < sa.len; j++) { + if (n->element == sa.val[j].element) { + *same = 1; + ret = match_general_name(n, &sa.val[j], match); + } + } + free_GeneralNames(&sa); + } while (1); + return ret; +} + + +static int +match_tree(const GeneralSubtrees *t, const Certificate *c, int *match) +{ + int name, alt_name, same; + unsigned int i; + int ret = 0; + + name = alt_name = same = *match = 0; + for (i = 0; i < t->len; i++) { + if (t->val[i].minimum && t->val[i].maximum) + return HX509_RANGE; + + /* + * If the constraint apply to directoryNames, test is with + * subjectName of the certificate if the certificate have a + * non-null (empty) subjectName. + */ + + if (t->val[i].base.element == choice_GeneralName_directoryName + && !subject_null_p(c)) + { + GeneralName certname; + + memset(&certname, 0, sizeof(certname)); + certname.element = choice_GeneralName_directoryName; + certname.u.directoryName.element = + c->tbsCertificate.subject.element; + certname.u.directoryName.u.rdnSequence = + c->tbsCertificate.subject.u.rdnSequence; + + ret = match_general_name(&t->val[i].base, &certname, &name); + } + + /* Handle subjectAltNames, this is icky since they + * restrictions only apply if the subjectAltName is of the + * same type. So if there have been a match of type, require + * altname to be set. + */ + ret = match_alt_name(&t->val[i].base, c, &same, &alt_name); + } + if (name && (!same || alt_name)) + *match = 1; + return ret; +} + +static int +check_name_constraints(hx509_context context, + const hx509_name_constraints *nc, + const Certificate *c) +{ + int match, ret; + int i; + + for (i = 0 ; i < nc->len; i++) { + GeneralSubtrees gs; + + if (nc->val[i].permittedSubtrees) { + GeneralSubtrees_SET(&gs, nc->val[i].permittedSubtrees); + ret = match_tree(&gs, c, &match); + if (ret) { + hx509_clear_error_string(context); + return ret; + } + /* allow null subjectNames, they wont matches anything */ + if (match == 0 && !subject_null_p(c)) { + hx509_set_error_string(context, 0, HX509_VERIFY_CONSTRAINTS, + "Error verify constraints, " + "certificate didn't match any " + "permitted subtree"); + return HX509_VERIFY_CONSTRAINTS; + } + } + if (nc->val[i].excludedSubtrees) { + GeneralSubtrees_SET(&gs, nc->val[i].excludedSubtrees); + ret = match_tree(&gs, c, &match); + if (ret) { + hx509_clear_error_string(context); + return ret; + } + if (match) { + hx509_set_error_string(context, 0, HX509_VERIFY_CONSTRAINTS, + "Error verify constraints, " + "certificate included in excluded " + "subtree"); + return HX509_VERIFY_CONSTRAINTS; + } + } + } + return 0; +} + +static void +free_name_constraints(hx509_name_constraints *nc) +{ + int i; + + for (i = 0 ; i < nc->len; i++) + free_NameConstraints(&nc->val[i]); + 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, + hx509_cert cert, + hx509_certs pool) +{ + hx509_name_constraints nc; + hx509_path path; + int ret, i, proxy_cert_depth, selfsigned_depth, diff; + enum certtype type; + Name proxy_issuer; + hx509_certs anchors = NULL; + + memset(&proxy_issuer, 0, sizeof(proxy_issuer)); + + ret = init_name_constraints(&nc); + if (ret) + return ret; + + path.val = NULL; + path.len = 0; + + if ((ctx->flags & HX509_VERIFY_CTX_F_TIME_SET) == 0) + ctx->time_now = time(NULL); + + /* + * + */ + if (ctx->trust_anchors) + anchors = _hx509_certs_ref(ctx->trust_anchors); + else if (context->default_trust_anchors && ALLOW_DEF_TA(ctx)) + anchors = _hx509_certs_ref(context->default_trust_anchors); + else { + ret = hx509_certs_init(context, "MEMORY:no-TA", 0, NULL, &anchors); + if (ret) + goto out; + } + + /* + * Calculate the path from the certificate user presented to the + * to an anchor. + */ + ret = _hx509_calculate_path(context, 0, ctx->time_now, + anchors, ctx->max_depth, + cert, pool, &path); + if (ret) + goto out; + + /* + * Check CA and proxy certificate chain from the top of the + * certificate chain. Also check certificate is valid with respect + * to the current time. + * + */ + + proxy_cert_depth = 0; + selfsigned_depth = 0; + + if (ctx->flags & HX509_VERIFY_CTX_F_ALLOW_PROXY_CERTIFICATE) + type = PROXY_CERT; + else + type = EE_CERT; + + for (i = 0; i < path.len; i++) { + Certificate *c; + time_t t; + + c = _hx509_get_cert(path.val[i]); + + /* + * Lets do some basic check on issuer like + * keyUsage.keyCertSign and basicConstraints.cA bit depending + * on what type of certificate this is. + */ + + switch (type) { + case CA_CERT: + + /* XXX make constants for keyusage */ + ret = check_key_usage(context, c, 1 << 5, + REQUIRE_RFC3280(ctx) ? TRUE : FALSE); + if (ret) { + hx509_set_error_string(context, HX509_ERROR_APPEND, ret, + "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; + + if (is_proxy_cert(context, c, &info) == 0) { + int j; + + if (info.pCPathLenConstraint != NULL && + *info.pCPathLenConstraint < i) + { + free_ProxyCertInfo(&info); + ret = HX509_PATH_TOO_LONG; + hx509_set_error_string(context, 0, ret, + "Proxy certificate chain " + "longer then allowed"); + goto out; + } + /* XXX MUST check info.proxyPolicy */ + free_ProxyCertInfo(&info); + + j = 0; + if (find_extension(c, oid_id_x509_ce_subjectAltName(), &j)) { + ret = HX509_PROXY_CERT_INVALID; + hx509_set_error_string(context, 0, ret, + "Proxy certificate have explicity " + "forbidden subjectAltName"); + goto out; + } + + j = 0; + if (find_extension(c, oid_id_x509_ce_issuerAltName(), &j)) { + ret = HX509_PROXY_CERT_INVALID; + hx509_set_error_string(context, 0, ret, + "Proxy certificate have explicity " + "forbidden issuerAltName"); + goto out; + } + + /* + * The subject name of the proxy certificate should be + * CN=XXX,<proxy issuer>, prune of CN and check if its + * the same over the whole chain of proxy certs and + * then check with the EE cert when we get to it. + */ + + if (proxy_cert_depth) { + 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"); + goto out; + } + } + + free_Name(&proxy_issuer); + + ret = copy_Name(&c->tbsCertificate.subject, &proxy_issuer); + if (ret) { + hx509_clear_error_string(context); + goto out; + } + + j = proxy_issuer.u.rdnSequence.len; + if (proxy_issuer.u.rdnSequence.len < 2 + || proxy_issuer.u.rdnSequence.val[j - 1].len > 1 + || der_heim_oid_cmp(&proxy_issuer.u.rdnSequence.val[j - 1].val[0].type, + oid_id_at_commonName())) + { + ret = HX509_PROXY_CERT_NAME_WRONG; + hx509_set_error_string(context, 0, ret, + "Proxy name too short or " + "does not have Common name " + "at the top"); + goto out; + } + + free_RelativeDistinguishedName(&proxy_issuer.u.rdnSequence.val[j - 1]); + proxy_issuer.u.rdnSequence.len -= 1; + + 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"); + goto out; + } + + break; + } else { + /* + * Now we are done with the proxy certificates, this + * cert was an EE cert and we we will fall though to + * EE checking below. + */ + type = EE_CERT; + /* FALLTHOUGH */ + } + } + case EE_CERT: + /* + * If there where any proxy certificates in the chain + * (proxy_cert_depth > 0), check that the proxy issuer + * matched proxy certificates "base" subject. + */ + if (proxy_cert_depth) { + + 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; + } + if (cert->basename) + hx509_name_free(&cert->basename); + + ret = _hx509_name_from_Name(&proxy_issuer, &cert->basename); + if (ret) { + hx509_clear_error_string(context); + goto out; + } + } + + break; + } + + ret = check_basic_constraints(context, c, type, + i - proxy_cert_depth - selfsigned_depth); + if (ret) + goto out; + + /* + * Don't check the trust anchors expiration time since they + * are transported out of band, from RFC3820. + */ + if (i + 1 != path.len || CHECK_TA(ctx)) { + + t = _hx509_Time2time_t(&c->tbsCertificate.validity.notBefore); + if (t > ctx->time_now) { + ret = HX509_CERT_USED_BEFORE_TIME; + hx509_clear_error_string(context); + goto out; + } + t = _hx509_Time2time_t(&c->tbsCertificate.validity.notAfter); + if (t < ctx->time_now) { + ret = HX509_CERT_USED_AFTER_TIME; + hx509_clear_error_string(context); + goto out; + } + } + + if (type == EE_CERT) + type = CA_CERT; + else if (type == PROXY_CERT) + proxy_cert_depth++; + } + + /* + * Verify constraints, do this backward so path constraints are + * checked in the right order. + */ + + for (ret = 0, i = path.len - 1; i >= 0; i--) { + Certificate *c; + int selfsigned; + + c = _hx509_get_cert(path.val[i]); + + ret = certificate_is_self_signed(context, c, &selfsigned); + if (ret) + goto out; + + /* verify name constraints, not for selfsigned and anchor */ + if (!selfsigned || i + 1 != path.len) { + ret = check_name_constraints(context, &nc, c); + if (ret) { + goto out; + } + } + ret = add_name_constraints(context, c, i == 0, &nc); + if (ret) + goto out; + + /* XXX verify all other silly constraints */ + + } + + /* + * Verify that no certificates has been revoked. + */ + + if (ctx->revoke_ctx) { + hx509_certs certs; + + ret = hx509_certs_init(context, "MEMORY:revoke-certs", 0, + NULL, &certs); + if (ret) + goto out; + + for (i = 0; i < path.len; i++) { + ret = hx509_certs_add(context, certs, path.val[i]); + if (ret) { + hx509_certs_free(&certs); + goto out; + } + } + ret = hx509_certs_merge(context, certs, pool); + if (ret) { + hx509_certs_free(&certs); + goto out; + } + + for (i = 0; i < path.len - 1; i++) { + int parent = (i < path.len - 1) ? i + 1 : i; + + ret = hx509_revoke_verify(context, + ctx->revoke_ctx, + certs, + ctx->time_now, + path.val[i], + path.val[parent]); + if (ret) { + hx509_certs_free(&certs); + goto out; + } + } + hx509_certs_free(&certs); + } + + /* + * Verify signatures, do this backward so public key working + * parameter is passed up from the anchor up though the chain. + */ + + for (i = path.len - 1; i >= 0; i--) { + Certificate *signer, *c; + + c = _hx509_get_cert(path.val[i]); + + /* is last in chain (trust anchor) */ + 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 (!selfsigned) + continue; + } else { + /* take next certificate in chain */ + signer = path.val[i + 1]->data; + } + + /* verify signatureValue */ + ret = _hx509_verify_signature_bitstring(context, + signer, + &c->signatureAlgorithm, + &c->tbsCertificate._save, + &c->signatureValue); + if (ret) { + hx509_set_error_string(context, HX509_ERROR_APPEND, ret, + "Failed to verify signature of certificate"); + goto out; + } + } + +out: + hx509_certs_free(&anchors); + free_Name(&proxy_issuer); + free_name_constraints(&nc); + _hx509_path_free(&path); + + 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, + const AlgorithmIdentifier *alg, + const heim_octet_string *data, + const heim_octet_string *sig) +{ + return _hx509_verify_signature(context, signer->data, alg, data, sig); +} + + +/** + * 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, + const hx509_cert cert, + int flags, + hx509_hostname_type type, + const char *hostname, + const struct sockaddr *sa, + /* XXX krb5_socklen_t */ int sa_size) +{ + GeneralNames san; + int ret, i, j; + + if (sa && sa_size <= 0) + return EINVAL; + + memset(&san, 0, sizeof(san)); + + i = 0; + do { + ret = find_extension_subject_alt_name(cert->data, &i, &san); + if (ret == HX509_EXTENSION_NOT_FOUND) { + ret = 0; + break; + } else if (ret != 0) + break; + + for (j = 0; j < san.len; j++) { + switch (san.val[j].element) { + case choice_GeneralName_dNSName: + if (strcasecmp(san.val[j].u.dNSName, hostname) == 0) { + free_GeneralNames(&san); + return 0; + } + break; + default: + break; + } + } + free_GeneralNames(&san); + } while (1); + + { + const Name *name = &cert->data->tbsCertificate.subject; + + /* match if first component is a CN= */ + if (name->u.rdnSequence.len > 0 + && name->u.rdnSequence.val[0].len == 1 + && der_heim_oid_cmp(&name->u.rdnSequence.val[0].val[0].type, + oid_id_at_commonName()) == 0) + { + DirectoryString *ds = &name->u.rdnSequence.val[0].val[0].value; + + switch (ds->element) { + case choice_DirectoryString_printableString: + if (strcasecmp(ds->u.printableString, hostname) == 0) + return 0; + break; + case choice_DirectoryString_ia5String: + if (strcasecmp(ds->u.ia5String, hostname) == 0) + return 0; + break; + case choice_DirectoryString_utf8String: + if (strcasecmp(ds->u.utf8String, hostname) == 0) + return 0; + default: + break; + } + } + } + + if ((flags & HX509_VHN_F_ALLOW_NO_MATCH) == 0) + ret = HX509_NAME_CONSTRAINT_ERROR; + + return ret; +} + +int +_hx509_set_cert_attribute(hx509_context context, + hx509_cert cert, + const heim_oid *oid, + const heim_octet_string *attr) +{ + hx509_cert_attribute a; + void *d; + + if (hx509_cert_get_attribute(cert, oid) != NULL) + return 0; + + d = realloc(cert->attrs.val, + sizeof(cert->attrs.val[0]) * (cert->attrs.len + 1)); + if (d == NULL) { + hx509_clear_error_string(context); + return ENOMEM; + } + cert->attrs.val = d; + + a = malloc(sizeof(*a)); + if (a == NULL) + return ENOMEM; + + der_copy_octet_string(attr, &a->data); + der_copy_oid(oid, &a->oid); + + cert->attrs.val[cert->attrs.len] = a; + cert->attrs.len++; + + 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) +{ + int i; + for (i = 0; i < cert->attrs.len; i++) + if (der_heim_oid_cmp(oid, &cert->attrs.val[i]->oid) == 0) + return cert->attrs.val[i]; + 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) +{ + if (cert->friendlyname) + free(cert->friendlyname); + cert->friendlyname = strdup(name); + if (cert->friendlyname == NULL) + return ENOMEM; + 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) +{ + hx509_cert_attribute a; + PKCS9_friendlyName n; + size_t sz; + int ret, i; + + if (cert->friendlyname) + return cert->friendlyname; + + a = hx509_cert_get_attribute(cert, oid_id_pkcs_9_at_friendlyName()); + if (a == NULL) { + hx509_name name; + + ret = hx509_cert_get_subject(cert, &name); + if (ret) + return NULL; + ret = hx509_name_to_string(name, &cert->friendlyname); + hx509_name_free(&name); + if (ret) + return NULL; + return cert->friendlyname; + } + + ret = decode_PKCS9_friendlyName(a->data.data, a->data.length, &n, &sz); + if (ret) + return NULL; + + if (n.len != 1) { + free_PKCS9_friendlyName(&n); + return NULL; + } + + cert->friendlyname = malloc(n.val[0].length + 1); + if (cert->friendlyname == NULL) { + free_PKCS9_friendlyName(&n); + return NULL; + } + + for (i = 0; i < n.val[0].length; i++) { + if (n.val[0].data[i] <= 0xff) + cert->friendlyname[i] = n.val[0].data[i] & 0xff; + else + cert->friendlyname[i] = 'X'; + } + cert->friendlyname[i] = '\0'; + free_PKCS9_friendlyName(&n); + + return cert->friendlyname; +} + +void +_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) +{ + *q = calloc(1, sizeof(**q)); + if (*q == NULL) + return ENOMEM; + 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) +{ + switch(option) { + case HX509_QUERY_OPTION_PRIVATE_KEY: + q->match |= HX509_QUERY_PRIVATE_KEY; + break; + case HX509_QUERY_OPTION_KU_ENCIPHERMENT: + q->match |= HX509_QUERY_KU_ENCIPHERMENT; + break; + case HX509_QUERY_OPTION_KU_DIGITALSIGNATURE: + q->match |= HX509_QUERY_KU_DIGITALSIGNATURE; + break; + case HX509_QUERY_OPTION_KU_KEYCERTSIGN: + q->match |= HX509_QUERY_KU_KEYCERTSIGN; + break; + case HX509_QUERY_OPTION_END: + default: + break; + } +} + +/** + * 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, + const heim_integer *serialNumber) +{ + int ret; + if (q->serial) { + der_free_heim_integer(q->serial); + free(q->serial); + } + q->serial = malloc(sizeof(*q->serial)); + if (q->serial == NULL) + return ENOMEM; + ret = der_copy_heim_integer(serialNumber, q->serial); + if (ret) { + free(q->serial); + q->serial = NULL; + return ret; + } + if (q->issuer_name) { + free_Name(q->issuer_name); + free(q->issuer_name); + } + q->issuer_name = malloc(sizeof(*q->issuer_name)); + if (q->issuer_name == NULL) + return ENOMEM; + ret = copy_Name(issuer, q->issuer_name); + if (ret) { + free(q->issuer_name); + q->issuer_name = NULL; + return ret; + } + q->match |= HX509_QUERY_MATCH_SERIALNUMBER|HX509_QUERY_MATCH_ISSUER_NAME; + 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) +{ + if (q->friendlyname) + free(q->friendlyname); + q->friendlyname = strdup(name); + if (q->friendlyname == NULL) + return ENOMEM; + q->match |= HX509_QUERY_MATCH_FRIENDLY_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; +} + +int +hx509_query_match_expr(hx509_context context, hx509_query *q, const char *expr) +{ + if (q->expr) { + _hx509_expr_free(q->expr); + q->expr = NULL; + } + + if (expr == NULL) { + q->match &= ~HX509_QUERY_MATCH_EXPR; + } else { + q->expr = _hx509_expr_parse(expr); + if (q->expr) + q->match |= HX509_QUERY_MATCH_EXPR; + } + + 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), + void *ctx) +{ + if (func) + q->match |= HX509_QUERY_MATCH_FUNCTION; + else + q->match &= ~HX509_QUERY_MATCH_FUNCTION; + q->cmp_func = func; + q->cmp_func_ctx = ctx; + 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); + } + if (q->issuer_name) { + free_Name(q->issuer_name); + free(q->issuer_name); + } + if (q->eku) { + der_free_oid(q->eku); + free(q->eku); + } + if (q->friendlyname) + free(q->friendlyname); + if (q->expr) + _hx509_expr_free(q->expr); + + memset(q, 0, sizeof(*q)); + free(q); +} + +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); + + if ((q->match & HX509_QUERY_FIND_ISSUER_CERT) && + _hx509_cert_is_parent_cmp(q->subject, c, 0) != 0) + return 0; + + if ((q->match & HX509_QUERY_MATCH_CERTIFICATE) && + _hx509_Certificate_cmp(q->certificate, c) != 0) + return 0; + + if ((q->match & HX509_QUERY_MATCH_SERIALNUMBER) + && der_heim_integer_cmp(&c->tbsCertificate.serialNumber, q->serial) != 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) { + 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; + + ret = _hx509_find_extension_subject_key_id(c, &si); + if (ret == 0) { + if (der_heim_octet_string_cmp(&si, q->subject_id) != 0) + ret = 1; + free_SubjectKeyIdentifier(&si); + } + if (ret) + return 0; + } + if ((q->match & HX509_QUERY_MATCH_ISSUER_ID)) + return 0; + if ((q->match & HX509_QUERY_PRIVATE_KEY) && + _hx509_cert_private_key(cert) == NULL) + return 0; + + { + unsigned ku = 0; + if (q->match & HX509_QUERY_KU_DIGITALSIGNATURE) + ku |= (1 << 0); + if (q->match & HX509_QUERY_KU_NONREPUDIATION) + ku |= (1 << 1); + if (q->match & HX509_QUERY_KU_ENCIPHERMENT) + ku |= (1 << 2); + if (q->match & HX509_QUERY_KU_DATAENCIPHERMENT) + ku |= (1 << 3); + if (q->match & HX509_QUERY_KU_KEYAGREEMENT) + ku |= (1 << 4); + if (q->match & HX509_QUERY_KU_KEYCERTSIGN) + ku |= (1 << 5); + if (q->match & HX509_QUERY_KU_CRLSIGN) + ku |= (1 << 6); + if (ku && check_key_usage(context, c, ku, TRUE)) + return 0; + } + if ((q->match & HX509_QUERY_ANCHOR)) + return 0; + + if (q->match & HX509_QUERY_MATCH_LOCAL_KEY_ID) { + hx509_cert_attribute a; + + a = hx509_cert_get_attribute(cert, oid_id_pkcs_9_at_localKeyId()); + if (a == NULL) + return 0; + if (der_heim_octet_string_cmp(&a->data, q->local_key_id) != 0) + return 0; + } + + if (q->match & HX509_QUERY_NO_MATCH_PATH) { + size_t i; + + for (i = 0; i < q->path->len; i++) + if (hx509_cert_cmp(q->path->val[i], cert) == 0) + return 0; + } + if (q->match & HX509_QUERY_MATCH_FRIENDLY_NAME) { + const char *name = hx509_cert_get_friendly_name(cert); + if (name == NULL) + return 0; + if (strcasecmp(q->friendlyname, name) != 0) + return 0; + } + if (q->match & HX509_QUERY_MATCH_FUNCTION) { + 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; + + os.data = c->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data; + os.length = + c->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8; + + ret = _hx509_verify_signature(context, + NULL, + hx509_signature_sha1(), + &os, + q->keyhash_sha1); + if (ret != 0) + return 0; + } + + if (q->match & HX509_QUERY_MATCH_TIME) { + time_t t; + t = _hx509_Time2time_t(&c->tbsCertificate.validity.notBefore); + if (t > q->timenow) + return 0; + t = _hx509_Time2time_t(&c->tbsCertificate.validity.notAfter); + if (t < q->timenow) + 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_MATCH_EXPR)) { + hx509_env env = NULL; + + ret = _hx509_cert_to_env(context, cert, &env); + if (ret) + return 0; + + ret = _hx509_expr_eval(context, env, q->expr); + hx509_env_free(&env); + if (ret == 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) +{ + if (context->querystat) + free(context->querystat); + context->querystat = strdup(fn); +} + +void +_hx509_query_statistic(hx509_context context, int type, const hx509_query *q) +{ + FILE *f; + if (context->querystat == NULL) + return; + f = fopen(context->querystat, "a"); + if (f == NULL) + return; + rk_cloexec_file(f); + fprintf(f, "%d %d\n", type, q->match); + fclose(f); +} + +static const char *statname[] = { + "find issuer cert", + "match serialnumber", + "match issuer name", + "match subject name", + "match subject key id", + "match issuer id", + "private key", + "ku encipherment", + "ku digitalsignature", + "ku keycertsign", + "ku crlsign", + "ku nonrepudiation", + "ku keyagreement", + "ku dataencipherment", + "anchor", + "match certificate", + "match local key id", + "no match path", + "match friendly name", + "match function", + "match key hash sha1", + "match time" +}; + +struct stat_el { + unsigned long stats; + unsigned int index; +}; + + +static int +stat_sort(const void *a, const void *b) +{ + const struct stat_el *ae = a; + const struct stat_el *be = 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) +{ + rtbl_t t; + FILE *f; + int type, mask, i, num; + unsigned long multiqueries = 0, totalqueries = 0; + struct stat_el stats[32]; + + if (context->querystat == NULL) + return; + f = fopen(context->querystat, "r"); + if (f == NULL) { + fprintf(out, "No statistic file %s: %s.\n", + context->querystat, strerror(errno)); + return; + } + rk_cloexec_file(f); + + for (i = 0; i < sizeof(stats)/sizeof(stats[0]); i++) { + stats[i].index = i; + stats[i].stats = 0; + } + + while (fscanf(f, "%d %d\n", &type, &mask) == 2) { + if (type != printtype) + continue; + num = i = 0; + while (mask && i < sizeof(stats)/sizeof(stats[0])) { + if (mask & 1) { + stats[i].stats++; + num++; + } + mask = mask >>1 ; + i++; + } + if (num > 1) + multiqueries++; + totalqueries++; + } + fclose(f); + + qsort(stats, sizeof(stats)/sizeof(stats[0]), sizeof(stats[0]), stat_sort); + + t = rtbl_create(); + if (t == NULL) + errx(1, "out of memory"); + + rtbl_set_separator (t, " "); + + rtbl_add_column_by_id (t, 0, "Name", 0); + rtbl_add_column_by_id (t, 1, "Counter", 0); + + + for (i = 0; i < sizeof(stats)/sizeof(stats[0]); i++) { + char str[10]; + + if (stats[i].index < sizeof(statname)/sizeof(statname[0])) + rtbl_add_column_entry_by_id (t, 0, statname[stats[i].index]); + else { + snprintf(str, sizeof(str), "%d", stats[i].index); + rtbl_add_column_entry_by_id (t, 0, str); + } + snprintf(str, sizeof(str), "%lu", stats[i].stats); + rtbl_add_column_entry_by_id (t, 1, str); + } + + rtbl_format(t, out); + rtbl_destroy(t); + + fprintf(out, "\nQueries: multi %lu total %lu\n", + 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) +{ + ExtKeyUsage e; + int ret, i; + + ret = find_extension_eku(_hx509_get_cert(cert), &e); + if (ret) { + hx509_clear_error_string(context); + return ret; + } + + for (i = 0; i < e.len; i++) { + if (der_heim_oid_cmp(eku, &e.val[i]) == 0) { + free_ExtKeyUsage(&e); + return 0; + } + if (allow_any_eku) { +#if 0 + if (der_heim_oid_cmp(id_any_eku, &e.val[i]) == 0) { + free_ExtKeyUsage(&e); + return 0; + } +#endif + } + } + free_ExtKeyUsage(&e); + hx509_clear_error_string(context); + return HX509_CERTIFICATE_MISSING_EKU; +} + +int +_hx509_cert_get_keyusage(hx509_context context, + hx509_cert c, + KeyUsage *ku) +{ + Certificate *cert; + const Extension *e; + size_t size; + int ret, i = 0; + + memset(ku, 0, sizeof(*ku)); + + cert = _hx509_get_cert(c); + + if (_hx509_cert_get_version(cert) < 3) + return 0; + + e = find_extension(cert, oid_id_x509_ce_keyUsage(), &i); + if (e == NULL) + return HX509_KU_CERT_MISSING; + + ret = decode_KeyUsage(e->extnValue.data, e->extnValue.length, ku, &size); + if (ret) + return ret; + return 0; +} + +int +_hx509_cert_get_eku(hx509_context context, + hx509_cert cert, + ExtKeyUsage *e) +{ + int ret; + + memset(e, 0, sizeof(*e)); + + ret = find_extension_eku(_hx509_get_cert(cert), e); + if (ret && ret != HX509_EXTENSION_NOT_FOUND) { + hx509_clear_error_string(context); + return ret; + } + 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) +{ + size_t size; + int ret; + + os->data = NULL; + os->length = 0; + + ASN1_MALLOC_ENCODE(Certificate, os->data, os->length, + _hx509_get_cert(c), &size, ret); + if (ret) { + os->data = NULL; + os->length = 0; + return ret; + } + if (os->length != size) + _hx509_abort("internal ASN.1 encoder error"); + + return ret; +} + +/* + * Last to avoid lost __attribute__s due to #undef. + */ + +#undef __attribute__ +#define __attribute__(X) + +void +_hx509_abort(const char *fmt, ...) + __attribute__ ((noreturn, format (printf, 1, 2))) +{ + va_list ap; + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + printf("\n"); + fflush(stdout); + 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); +} + +/** + * + */ + +int +_hx509_cert_to_env(hx509_context context, hx509_cert cert, hx509_env *env) +{ + ExtKeyUsage eku; + hx509_name name; + char *buf; + int ret; + hx509_env envcert = NULL; + + *env = NULL; + + /* version */ + asprintf(&buf, "%d", _hx509_cert_get_version(_hx509_get_cert(cert))); + ret = hx509_env_add(context, &envcert, "version", buf); + free(buf); + if (ret) + goto out; + + /* subject */ + ret = hx509_cert_get_subject(cert, &name); + if (ret) + goto out; + + ret = hx509_name_to_string(name, &buf); + if (ret) { + hx509_name_free(&name); + goto out; + } + + ret = hx509_env_add(context, &envcert, "subject", buf); + hx509_name_free(&name); + if (ret) + goto out; + + /* issuer */ + ret = hx509_cert_get_issuer(cert, &name); + if (ret) + goto out; + + ret = hx509_name_to_string(name, &buf); + hx509_name_free(&name); + if (ret) + goto out; + + ret = hx509_env_add(context, &envcert, "issuer", buf); + hx509_xfree(buf); + if (ret) + goto out; + + /* eku */ + + ret = _hx509_cert_get_eku(context, cert, &eku); + if (ret == HX509_EXTENSION_NOT_FOUND) + ; + else if (ret != 0) + goto out; + else { + int i; + hx509_env enveku = NULL; + + for (i = 0; i < eku.len; i++) { + + ret = der_print_heim_oid(&eku.val[i], '.', &buf); + if (ret) { + free_ExtKeyUsage(&eku); + hx509_env_free(&enveku); + goto out; + } + ret = hx509_env_add(context, &enveku, buf, "oid-name-here"); + free(buf); + if (ret) { + free_ExtKeyUsage(&eku); + hx509_env_free(&enveku); + goto out; + } + } + free_ExtKeyUsage(&eku); + + ret = hx509_env_add_binding(context, &envcert, "eku", enveku); + if (ret) { + hx509_env_free(&enveku); + goto out; + } + } + + ret = hx509_env_add_binding(context, env, "certificate", envcert); + if (ret) + goto out; + + return 0; + +out: + hx509_env_free(&envcert); + return ret; +} diff --git a/source4/heimdal/lib/hx509/cms.c b/source4/heimdal/lib/hx509/cms.c new file mode 100644 index 0000000000..629060a253 --- /dev/null +++ b/source4/heimdal/lib/hx509/cms.c @@ -0,0 +1,1434 @@ +/* + * Copyright (c) 2003 - 2007 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hx_locl.h" +RCSID("$Id$"); + +/** + * @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, + heim_octet_string *res) +{ + ContentInfo ci; + size_t size; + int ret; + + memset(res, 0, sizeof(*res)); + memset(&ci, 0, sizeof(ci)); + + ret = der_copy_oid(oid, &ci.contentType); + if (ret) + return ret; + 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; + } + + ASN1_MALLOC_ENCODE(ContentInfo, res->data, res->length, &ci, &size, ret); + free_ContentInfo(&ci); + if (ret) + return ret; + if (res->length != size) + _hx509_abort("internal ASN.1 encoder error"); + + 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, + heim_octet_string *out, + int *have_data) +{ + ContentInfo ci; + size_t size; + int ret; + + memset(oid, 0, sizeof(*oid)); + memset(out, 0, sizeof(*out)); + + ret = decode_ContentInfo(in->data, in->length, &ci, &size); + if (ret) + return ret; + + ret = der_copy_oid(&ci.contentType, oid); + if (ret) { + free_ContentInfo(&ci); + return ret; + } + if (ci.content) { + ret = der_copy_octet_string(ci.content, out); + if (ret) { + der_free_oid(oid); + free_ContentInfo(&ci); + return ret; + } + } else + memset(out, 0, sizeof(*out)); + + if (have_data) + *have_data = (ci.content != NULL) ? 1 : 0; + + free_ContentInfo(&ci); + + return 0; +} + +#define CMS_ID_SKI 0 +#define CMS_ID_NAME 1 + +static int +fill_CMSIdentifier(const hx509_cert cert, + int type, + CMSIdentifier *id) +{ + int ret; + + switch (type) { + case CMS_ID_SKI: + id->element = choice_CMSIdentifier_subjectKeyIdentifier; + ret = _hx509_find_extension_subject_key_id(_hx509_get_cert(cert), + &id->u.subjectKeyIdentifier); + if (ret == 0) + break; + /* FALL THOUGH */ + case CMS_ID_NAME: { + hx509_name name; + + id->element = choice_CMSIdentifier_issuerAndSerialNumber; + ret = hx509_cert_get_issuer(cert, &name); + if (ret) + return ret; + ret = hx509_name_to_Name(name, &id->u.issuerAndSerialNumber.issuer); + hx509_name_free(&name); + if (ret) + return ret; + + ret = hx509_cert_get_serialnumber(cert, &id->u.issuerAndSerialNumber.serialNumber); + break; + } + default: + _hx509_abort("CMS fill identifier with unknown type"); + } + return ret; +} + +static int +unparse_CMSIdentifier(hx509_context context, + CMSIdentifier *id, + char **str) +{ + int ret; + + *str = NULL; + switch (id->element) { + case choice_CMSIdentifier_issuerAndSerialNumber: { + IssuerAndSerialNumber *iasn; + char *serial, *name; + + iasn = &id->u.issuerAndSerialNumber; + + ret = _hx509_Name_to_string(&iasn->issuer, &name); + if(ret) + return ret; + ret = der_print_hex_heim_integer(&iasn->serialNumber, &serial); + if (ret) { + free(name); + return ret; + } + asprintf(str, "certificate issued by %s with serial number %s", + name, serial); + free(name); + free(serial); + break; + } + case choice_CMSIdentifier_subjectKeyIdentifier: { + KeyIdentifier *ki = &id->u.subjectKeyIdentifier; + char *keyid; + ssize_t len; + + len = hex_encode(ki->data, ki->length, &keyid); + if (len < 0) + return ENOMEM; + + asprintf(str, "certificate with id %s", keyid); + free(keyid); + break; + } + default: + asprintf(str, "certificate have unknown CMSidentifier type"); + break; + } + if (*str == NULL) + return ENOMEM; + return 0; +} + +static int +find_CMSIdentifier(hx509_context context, + CMSIdentifier *client, + hx509_certs certs, + time_t time_now, + hx509_cert *signer_cert, + int match) +{ + hx509_query q; + hx509_cert cert; + Certificate c; + int ret; + + memset(&c, 0, sizeof(c)); + _hx509_query_clear(&q); + + *signer_cert = NULL; + + switch (client->element) { + case choice_CMSIdentifier_issuerAndSerialNumber: + q.serial = &client->u.issuerAndSerialNumber.serialNumber; + q.issuer_name = &client->u.issuerAndSerialNumber.issuer; + q.match = HX509_QUERY_MATCH_SERIALNUMBER|HX509_QUERY_MATCH_ISSUER_NAME; + break; + case choice_CMSIdentifier_subjectKeyIdentifier: + q.subject_id = &client->u.subjectKeyIdentifier; + q.match = HX509_QUERY_MATCH_SUBJECT_KEY_ID; + break; + default: + hx509_set_error_string(context, 0, HX509_CMS_NO_RECIPIENT_CERTIFICATE, + "unknown CMS identifier element"); + return HX509_CMS_NO_RECIPIENT_CERTIFICATE; + } + + q.match |= match; + + q.match |= HX509_QUERY_MATCH_TIME; + if (time_now) + q.timenow = time_now; + else + q.timenow = time(NULL); + + ret = hx509_certs_find(context, certs, &q, &cert); + if (ret == HX509_CERT_NOT_FOUND) { + char *str; + + ret = unparse_CMSIdentifier(context, client, &str); + if (ret == 0) { + hx509_set_error_string(context, 0, + HX509_CMS_NO_RECIPIENT_CERTIFICATE, + "Failed to find %s", str); + } else + hx509_clear_error_string(context); + return HX509_CMS_NO_RECIPIENT_CERTIFICATE; + } else if (ret) { + hx509_set_error_string(context, HX509_ERROR_APPEND, + HX509_CMS_NO_RECIPIENT_CERTIFICATE, + "Failed to find CMS id in cert store"); + return HX509_CMS_NO_RECIPIENT_CERTIFICATE; + } + + *signer_cert = cert; + + 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 time_now set the current time, if zero the library uses now as the date. + * @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, + int flags, + const void *data, + size_t length, + const heim_octet_string *encryptedContent, + time_t time_now, + heim_oid *contentType, + heim_octet_string *content) +{ + heim_octet_string key; + EnvelopedData ed; + hx509_cert cert; + AlgorithmIdentifier *ai; + const heim_octet_string *enccontent; + heim_octet_string *params, params_data; + heim_octet_string ivec; + size_t size; + int ret, i, matched = 0, findflags = 0; + + + memset(&key, 0, sizeof(key)); + memset(&ed, 0, sizeof(ed)); + memset(&ivec, 0, sizeof(ivec)); + memset(content, 0, sizeof(*content)); + memset(contentType, 0, sizeof(*contentType)); + + if ((flags & HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT) == 0) + findflags |= HX509_QUERY_KU_ENCIPHERMENT; + + ret = decode_EnvelopedData(data, length, &ed, &size); + if (ret) { + hx509_set_error_string(context, 0, ret, + "Failed to decode EnvelopedData"); + return ret; + } + + if (ed.recipientInfos.len == 0) { + ret = HX509_CMS_NO_RECIPIENT_CERTIFICATE; + hx509_set_error_string(context, 0, ret, + "No recipient info in enveloped data"); + goto out; + } + + enccontent = ed.encryptedContentInfo.encryptedContent; + if (enccontent == NULL) { + if (encryptedContent == NULL) { + ret = HX509_CMS_NO_DATA_AVAILABLE; + hx509_set_error_string(context, 0, ret, + "Content missing from encrypted data"); + goto out; + } + enccontent = encryptedContent; + } else if (encryptedContent != NULL) { + ret = HX509_CMS_NO_DATA_AVAILABLE; + hx509_set_error_string(context, 0, ret, + "Both internal and external encrypted data"); + goto out; + } + + cert = NULL; + for (i = 0; i < ed.recipientInfos.len; i++) { + KeyTransRecipientInfo *ri; + char *str; + int ret2; + + ri = &ed.recipientInfos.val[i]; + + ret = find_CMSIdentifier(context, &ri->rid, certs, + time_now, &cert, + HX509_QUERY_PRIVATE_KEY|findflags); + if (ret) + continue; + + matched = 1; /* found a matching certificate, let decrypt */ + + ret = _hx509_cert_private_decrypt(context, + &ri->encryptedKey, + &ri->keyEncryptionAlgorithm.algorithm, + cert, &key); + + hx509_cert_free(cert); + if (ret == 0) + break; /* succuessfully decrypted cert */ + cert = NULL; + ret2 = unparse_CMSIdentifier(context, &ri->rid, &str); + if (ret2 == 0) { + hx509_set_error_string(context, HX509_ERROR_APPEND, ret, + "Failed to decrypt with %s", str); + free(str); + } + } + + if (!matched) { + ret = HX509_CMS_NO_RECIPIENT_CERTIFICATE; + hx509_set_error_string(context, 0, ret, + "No private key matched any certificate"); + goto out; + } + + if (cert == NULL) { + ret = HX509_CMS_NO_RECIPIENT_CERTIFICATE; + hx509_set_error_string(context, HX509_ERROR_APPEND, ret, + "No private key decrypted the transfer key"); + goto out; + } + + ret = der_copy_oid(&ed.encryptedContentInfo.contentType, contentType); + if (ret) { + hx509_set_error_string(context, 0, ret, + "Failed to copy EnvelopedData content oid"); + goto out; + } + + ai = &ed.encryptedContentInfo.contentEncryptionAlgorithm; + if (ai->parameters) { + params_data.data = ai->parameters->data; + params_data.length = ai->parameters->length; + params = ¶ms_data; + } else + params = NULL; + + { + hx509_crypto crypto; + + ret = hx509_crypto_init(context, NULL, &ai->algorithm, &crypto); + if (ret) + goto out; + + if (params) { + ret = hx509_crypto_set_params(context, crypto, params, &ivec); + if (ret) { + hx509_crypto_destroy(crypto); + goto out; + } + } + + ret = hx509_crypto_set_key_data(crypto, key.data, key.length); + if (ret) { + hx509_crypto_destroy(crypto); + hx509_set_error_string(context, 0, ret, + "Failed to set key for decryption " + "of EnvelopedData"); + goto out; + } + + ret = hx509_crypto_decrypt(crypto, + enccontent->data, + enccontent->length, + ivec.length ? &ivec : NULL, + content); + hx509_crypto_destroy(crypto); + if (ret) { + hx509_set_error_string(context, 0, ret, + "Failed to decrypt EnvelopedData"); + goto out; + } + } + +out: + + free_EnvelopedData(&ed); + der_free_octet_string(&key); + if (ivec.length) + der_free_octet_string(&ivec); + if (ret) { + der_free_oid(contentType); + der_free_octet_string(content); + } + + 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, + hx509_cert cert, + const void *data, + size_t length, + const heim_oid *encryption_type, + const heim_oid *contentType, + heim_octet_string *content) +{ + KeyTransRecipientInfo *ri; + heim_octet_string ivec; + heim_octet_string key; + hx509_crypto crypto = NULL; + EnvelopedData ed; + size_t size; + int ret; + + memset(&ivec, 0, sizeof(ivec)); + memset(&key, 0, sizeof(key)); + memset(&ed, 0, sizeof(ed)); + memset(content, 0, sizeof(*content)); + + if (encryption_type == NULL) + encryption_type = oid_id_aes_256_cbc(); + + ret = _hx509_check_key_usage(context, cert, 1 << 2, TRUE); + if (ret) + goto out; + + ret = hx509_crypto_init(context, NULL, encryption_type, &crypto); + if (ret) + goto out; + + ret = hx509_crypto_set_random_key(crypto, &key); + if (ret) { + hx509_set_error_string(context, 0, ret, + "Create random key for EnvelopedData content"); + goto out; + } + + ret = hx509_crypto_random_iv(crypto, &ivec); + if (ret) { + hx509_set_error_string(context, 0, ret, + "Failed to create a random iv"); + goto out; + } + + ret = hx509_crypto_encrypt(crypto, + data, + length, + &ivec, + &ed.encryptedContentInfo.encryptedContent); + if (ret) { + hx509_set_error_string(context, 0, ret, + "Failed to encrypt EnvelopedData content"); + goto out; + } + + { + AlgorithmIdentifier *enc_alg; + enc_alg = &ed.encryptedContentInfo.contentEncryptionAlgorithm; + ret = der_copy_oid(encryption_type, &enc_alg->algorithm); + if (ret) { + hx509_set_error_string(context, 0, ret, + "Failed to set crypto oid " + "for EnvelopedData"); + goto out; + } + ALLOC(enc_alg->parameters, 1); + if (enc_alg->parameters == NULL) { + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, + "Failed to allocate crypto paramaters " + "for EnvelopedData"); + goto out; + } + + ret = hx509_crypto_get_params(context, + crypto, + &ivec, + enc_alg->parameters); + if (ret) { + goto out; + } + } + + ALLOC_SEQ(&ed.recipientInfos, 1); + if (ed.recipientInfos.val == NULL) { + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, + "Failed to allocate recipients info " + "for EnvelopedData"); + goto out; + } + + ri = &ed.recipientInfos.val[0]; + + ri->version = 0; + ret = fill_CMSIdentifier(cert, CMS_ID_SKI, &ri->rid); + if (ret) { + hx509_set_error_string(context, 0, ret, + "Failed to set CMS identifier info " + "for EnvelopedData"); + goto out; + } + + ret = _hx509_cert_public_encrypt(context, + &key, cert, + &ri->keyEncryptionAlgorithm.algorithm, + &ri->encryptedKey); + if (ret) { + hx509_set_error_string(context, HX509_ERROR_APPEND, ret, + "Failed to encrypt transport key for " + "EnvelopedData"); + goto out; + } + + /* + * + */ + + ed.version = 0; + ed.originatorInfo = NULL; + + ret = der_copy_oid(contentType, &ed.encryptedContentInfo.contentType); + if (ret) { + hx509_set_error_string(context, 0, ret, + "Failed to copy content oid for " + "EnvelopedData"); + goto out; + } + + ed.unprotectedAttrs = NULL; + + ASN1_MALLOC_ENCODE(EnvelopedData, content->data, content->length, + &ed, &size, ret); + if (ret) { + hx509_set_error_string(context, 0, ret, + "Failed to encode EnvelopedData"); + goto out; + } + if (size != content->length) + _hx509_abort("internal ASN.1 encoder error"); + +out: + if (crypto) + hx509_crypto_destroy(crypto); + if (ret) + der_free_octet_string(content); + der_free_octet_string(&key); + der_free_octet_string(&ivec); + free_EnvelopedData(&ed); + + return ret; +} + +static int +any_to_certs(hx509_context context, const SignedData *sd, hx509_certs certs) +{ + int ret, i; + + if (sd->certificates == NULL) + return 0; + + for (i = 0; i < sd->certificates->len; i++) { + hx509_cert c; + + ret = hx509_cert_init_data(context, + sd->certificates->val[i].data, + sd->certificates->val[i].length, + &c); + if (ret) + return ret; + ret = hx509_certs_add(context, certs, c); + hx509_cert_free(c); + if (ret) + return ret; + } + + return 0; +} + +static const Attribute * +find_attribute(const CMSAttributes *attr, const heim_oid *oid) +{ + int i; + for (i = 0; i < attr->len; i++) + if (der_heim_oid_cmp(&attr->val[i].type, oid) == 0) + return &attr->val[i]; + 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 pool, + heim_oid *contentType, + heim_octet_string *content, + hx509_certs *signer_certs) +{ + SignerInfo *signer_info; + hx509_cert cert = NULL; + hx509_certs certs = NULL; + SignedData sd; + size_t size; + int ret, i, found_valid_sig; + + *signer_certs = NULL; + content->data = NULL; + content->length = 0; + contentType->length = 0; + contentType->components = NULL; + + memset(&sd, 0, sizeof(sd)); + + ret = decode_SignedData(data, length, &sd, &size); + if (ret) { + hx509_set_error_string(context, 0, ret, + "Failed to decode SignedData"); + goto out; + } + + if (sd.encapContentInfo.eContent == NULL && signedContent == NULL) { + ret = HX509_CMS_NO_DATA_AVAILABLE; + hx509_set_error_string(context, 0, ret, + "No content data in SignedData"); + goto out; + } + if (sd.encapContentInfo.eContent && signedContent) { + ret = HX509_CMS_NO_DATA_AVAILABLE; + hx509_set_error_string(context, 0, ret, + "Both external and internal SignedData"); + goto out; + } + if (sd.encapContentInfo.eContent) + signedContent = sd.encapContentInfo.eContent; + + ret = hx509_certs_init(context, "MEMORY:cms-cert-buffer", + 0, NULL, &certs); + if (ret) + goto out; + + ret = hx509_certs_init(context, "MEMORY:cms-signer-certs", + 0, NULL, signer_certs); + if (ret) + goto out; + + /* XXX Check CMS version */ + + ret = any_to_certs(context, &sd, certs); + if (ret) + goto out; + + if (pool) { + ret = hx509_certs_merge(context, certs, pool); + if (ret) + goto out; + } + + for (found_valid_sig = 0, i = 0; i < sd.signerInfos.len; i++) { + heim_octet_string *signed_data; + const heim_oid *match_oid; + heim_oid decode_oid; + + signer_info = &sd.signerInfos.val[i]; + match_oid = NULL; + + if (signer_info->signature.length == 0) { + ret = HX509_CMS_MISSING_SIGNER_DATA; + hx509_set_error_string(context, 0, ret, + "SignerInfo %d in SignedData " + "missing sigature", i); + continue; + } + + ret = find_CMSIdentifier(context, &signer_info->sid, certs, + _hx509_verify_get_time(ctx), &cert, + HX509_QUERY_KU_DIGITALSIGNATURE); + if (ret) + continue; + + if (signer_info->signedAttrs) { + const Attribute *attr; + + CMSAttributes sa; + heim_octet_string os; + + sa.val = signer_info->signedAttrs->val; + sa.len = signer_info->signedAttrs->len; + + /* verify that sigature exists */ + attr = find_attribute(&sa, oid_id_pkcs9_messageDigest()); + if (attr == NULL) { + ret = HX509_CRYPTO_SIGNATURE_MISSING; + hx509_set_error_string(context, 0, ret, + "SignerInfo have signed attributes " + "but messageDigest (signature) " + "is missing"); + goto next_sigature; + } + if (attr->value.len != 1) { + ret = HX509_CRYPTO_SIGNATURE_MISSING; + hx509_set_error_string(context, 0, ret, + "SignerInfo have more then one " + "messageDigest (signature)"); + goto next_sigature; + } + + ret = decode_MessageDigest(attr->value.val[0].data, + attr->value.val[0].length, + &os, + &size); + if (ret) { + hx509_set_error_string(context, 0, ret, + "Failed to decode " + "messageDigest (signature)"); + goto next_sigature; + } + + ret = _hx509_verify_signature(context, + NULL, + &signer_info->digestAlgorithm, + signedContent, + &os); + der_free_octet_string(&os); + if (ret) { + hx509_set_error_string(context, HX509_ERROR_APPEND, ret, + "Failed to verify messageDigest"); + goto next_sigature; + } + + /* + * Fetch content oid inside signedAttrs or set it to + * id-pkcs7-data. + */ + attr = find_attribute(&sa, oid_id_pkcs9_contentType()); + if (attr == NULL) { + match_oid = oid_id_pkcs7_data(); + } else { + if (attr->value.len != 1) { + ret = HX509_CMS_DATA_OID_MISMATCH; + hx509_set_error_string(context, 0, ret, + "More then one oid in signedAttrs"); + goto next_sigature; + + } + ret = decode_ContentType(attr->value.val[0].data, + attr->value.val[0].length, + &decode_oid, + &size); + if (ret) { + hx509_set_error_string(context, 0, ret, + "Failed to decode " + "oid in signedAttrs"); + goto next_sigature; + } + match_oid = &decode_oid; + } + + ALLOC(signed_data, 1); + if (signed_data == NULL) { + if (match_oid == &decode_oid) + der_free_oid(&decode_oid); + ret = ENOMEM; + hx509_clear_error_string(context); + goto next_sigature; + } + + ASN1_MALLOC_ENCODE(CMSAttributes, + signed_data->data, + signed_data->length, + &sa, + &size, ret); + if (ret) { + if (match_oid == &decode_oid) + der_free_oid(&decode_oid); + free(signed_data); + hx509_clear_error_string(context); + goto next_sigature; + } + if (size != signed_data->length) + _hx509_abort("internal ASN.1 encoder error"); + + } else { + signed_data = rk_UNCONST(signedContent); + match_oid = oid_id_pkcs7_data(); + } + + if (der_heim_oid_cmp(match_oid, &sd.encapContentInfo.eContentType)) { + ret = HX509_CMS_DATA_OID_MISMATCH; + hx509_set_error_string(context, 0, ret, + "Oid in message mismatch from the expected"); + } + if (match_oid == &decode_oid) + der_free_oid(&decode_oid); + + if (ret == 0) { + ret = hx509_verify_signature(context, + cert, + &signer_info->signatureAlgorithm, + signed_data, + &signer_info->signature); + if (ret) + hx509_set_error_string(context, HX509_ERROR_APPEND, ret, + "Failed to verify sigature in " + "CMS SignedData"); + } + if (signed_data != signedContent) { + der_free_octet_string(signed_data); + free(signed_data); + } + if (ret) + goto next_sigature; + + ret = hx509_verify_path(context, ctx, cert, certs); + if (ret) + goto next_sigature; + + ret = hx509_certs_add(context, *signer_certs, cert); + if (ret) + goto next_sigature; + + found_valid_sig++; + + next_sigature: + if (cert) + hx509_cert_free(cert); + cert = NULL; + } + if (found_valid_sig == 0) { + if (ret == 0) { + ret = HX509_CMS_SIGNER_NOT_FOUND; + hx509_set_error_string(context, 0, ret, + "No signers where found"); + } + goto out; + } + + ret = der_copy_oid(&sd.encapContentInfo.eContentType, contentType); + if (ret) { + hx509_clear_error_string(context); + goto out; + } + + content->data = malloc(signedContent->length); + if (content->data == NULL) { + hx509_clear_error_string(context); + ret = ENOMEM; + goto out; + } + content->length = signedContent->length; + memcpy(content->data, signedContent->data, content->length); + +out: + free_SignedData(&sd); + if (certs) + hx509_certs_free(&certs); + if (ret) { + if (*signer_certs) + hx509_certs_free(signer_certs); + der_free_oid(contentType); + der_free_octet_string(content); + } + + return ret; +} + +static int +add_one_attribute(Attribute **attr, + unsigned int *len, + const heim_oid *oid, + heim_octet_string *data) +{ + void *d; + int ret; + + d = realloc(*attr, sizeof((*attr)[0]) * (*len + 1)); + if (d == NULL) + return ENOMEM; + (*attr) = d; + + ret = der_copy_oid(oid, &(*attr)[*len].type); + if (ret) + return ret; + + ALLOC_SEQ(&(*attr)[*len].value, 1); + if ((*attr)[*len].value.val == NULL) { + der_free_oid(&(*attr)[*len].type); + return ENOMEM; + } + + (*attr)[*len].value.val[0].data = data->data; + (*attr)[*len].value.val[0].length = data->length; + + *len += 1; + + 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, + const heim_oid *eContentType, + const void *data, size_t length, + const AlgorithmIdentifier *digest_alg, + hx509_cert cert, + hx509_peer_info peer, + hx509_certs anchors, + hx509_certs pool, + heim_octet_string *signed_data) +{ + AlgorithmIdentifier digest; + hx509_name name; + SignerInfo *signer_info; + heim_octet_string buf, content, sigdata = { 0, NULL }; + SignedData sd; + int ret; + size_t size; + hx509_path path; + int cmsidflag = CMS_ID_SKI; + + memset(&sd, 0, sizeof(sd)); + memset(&name, 0, sizeof(name)); + memset(&path, 0, sizeof(path)); + memset(&digest, 0, sizeof(digest)); + + content.data = rk_UNCONST(data); + content.length = length; + + if (flags & HX509_CMS_SIGATURE_ID_NAME) + cmsidflag = CMS_ID_NAME; + + if (_hx509_cert_private_key(cert) == NULL) { + hx509_set_error_string(context, 0, HX509_PRIVATE_KEY_MISSING, + "Private key missing for signing"); + return HX509_PRIVATE_KEY_MISSING; + } + + if (digest_alg == NULL) { + ret = hx509_crypto_select(context, HX509_SELECT_DIGEST, + _hx509_cert_private_key(cert), peer, &digest); + } else { + ret = copy_AlgorithmIdentifier(digest_alg, &digest); + if (ret) + hx509_clear_error_string(context); + } + if (ret) + goto out; + + sd.version = CMSVersion_v3; + + if (eContentType == NULL) + eContentType = oid_id_pkcs7_data(); + + der_copy_oid(eContentType, &sd.encapContentInfo.eContentType); + + /* */ + if ((flags & HX509_CMS_SIGATURE_DETACHED) == 0) { + ALLOC(sd.encapContentInfo.eContent, 1); + if (sd.encapContentInfo.eContent == NULL) { + hx509_clear_error_string(context); + ret = ENOMEM; + goto out; + } + + sd.encapContentInfo.eContent->data = malloc(length); + if (sd.encapContentInfo.eContent->data == NULL) { + hx509_clear_error_string(context); + ret = ENOMEM; + goto out; + } + memcpy(sd.encapContentInfo.eContent->data, data, length); + sd.encapContentInfo.eContent->length = length; + } + + ALLOC_SEQ(&sd.signerInfos, 1); + if (sd.signerInfos.val == NULL) { + hx509_clear_error_string(context); + ret = ENOMEM; + goto out; + } + + signer_info = &sd.signerInfos.val[0]; + + signer_info->version = 1; + + ret = fill_CMSIdentifier(cert, cmsidflag, &signer_info->sid); + if (ret) { + hx509_clear_error_string(context); + goto out; + } + + signer_info->signedAttrs = NULL; + signer_info->unsignedAttrs = NULL; + + + ret = copy_AlgorithmIdentifier(&digest, &signer_info->digestAlgorithm); + if (ret) { + hx509_clear_error_string(context); + goto out; + } + + /* + * If it isn't pkcs7-data send signedAttributes + */ + + if (der_heim_oid_cmp(eContentType, oid_id_pkcs7_data()) != 0) { + CMSAttributes sa; + heim_octet_string sig; + + ALLOC(signer_info->signedAttrs, 1); + if (signer_info->signedAttrs == NULL) { + ret = ENOMEM; + goto out; + } + + ret = _hx509_create_signature(context, + NULL, + &digest, + &content, + NULL, + &sig); + if (ret) + goto out; + + ASN1_MALLOC_ENCODE(MessageDigest, + buf.data, + buf.length, + &sig, + &size, + ret); + der_free_octet_string(&sig); + if (ret) { + hx509_clear_error_string(context); + goto out; + } + if (size != buf.length) + _hx509_abort("internal ASN.1 encoder error"); + + ret = add_one_attribute(&signer_info->signedAttrs->val, + &signer_info->signedAttrs->len, + oid_id_pkcs9_messageDigest(), + &buf); + if (ret) { + hx509_clear_error_string(context); + goto out; + } + + + ASN1_MALLOC_ENCODE(ContentType, + buf.data, + buf.length, + eContentType, + &size, + ret); + if (ret) + goto out; + if (size != buf.length) + _hx509_abort("internal ASN.1 encoder error"); + + ret = add_one_attribute(&signer_info->signedAttrs->val, + &signer_info->signedAttrs->len, + oid_id_pkcs9_contentType(), + &buf); + if (ret) { + hx509_clear_error_string(context); + goto out; + } + + sa.val = signer_info->signedAttrs->val; + sa.len = signer_info->signedAttrs->len; + + ASN1_MALLOC_ENCODE(CMSAttributes, + sigdata.data, + sigdata.length, + &sa, + &size, + ret); + if (ret) { + hx509_clear_error_string(context); + goto out; + } + if (size != sigdata.length) + _hx509_abort("internal ASN.1 encoder error"); + } else { + sigdata.data = content.data; + sigdata.length = content.length; + } + + + { + AlgorithmIdentifier sigalg; + + ret = hx509_crypto_select(context, HX509_SELECT_PUBLIC_SIG, + _hx509_cert_private_key(cert), peer, + &sigalg); + if (ret) + goto out; + + ret = _hx509_create_signature(context, + _hx509_cert_private_key(cert), + &sigalg, + &sigdata, + &signer_info->signatureAlgorithm, + &signer_info->signature); + free_AlgorithmIdentifier(&sigalg); + if (ret) + goto out; + } + + ALLOC_SEQ(&sd.digestAlgorithms, 1); + if (sd.digestAlgorithms.val == NULL) { + ret = ENOMEM; + hx509_clear_error_string(context); + goto out; + } + + ret = copy_AlgorithmIdentifier(&digest, &sd.digestAlgorithms.val[0]); + if (ret) { + hx509_clear_error_string(context); + goto out; + } + + /* + * Provide best effort path + */ + if (pool) { + _hx509_calculate_path(context, + HX509_CALCULATE_PATH_NO_ANCHOR, + time(NULL), + anchors, + 0, + cert, + pool, + &path); + } else + _hx509_path_append(context, &path, cert); + + + if (path.len) { + int i; + + ALLOC(sd.certificates, 1); + if (sd.certificates == NULL) { + hx509_clear_error_string(context); + ret = ENOMEM; + goto out; + } + ALLOC_SEQ(sd.certificates, path.len); + if (sd.certificates->val == NULL) { + hx509_clear_error_string(context); + ret = ENOMEM; + goto out; + } + + for (i = 0; i < path.len; i++) { + ret = hx509_cert_binary(context, path.val[i], + &sd.certificates->val[i]); + if (ret) { + hx509_clear_error_string(context); + goto out; + } + } + } + + ASN1_MALLOC_ENCODE(SignedData, + signed_data->data, signed_data->length, + &sd, &size, ret); + if (ret) { + hx509_clear_error_string(context); + goto out; + } + if (signed_data->length != size) + _hx509_abort("internal ASN.1 encoder error"); + +out: + if (sigdata.data != content.data) + der_free_octet_string(&sigdata); + free_AlgorithmIdentifier(&digest); + _hx509_path_free(&path); + free_SignedData(&sd); + + return ret; +} + +int +hx509_cms_decrypt_encrypted(hx509_context context, + hx509_lock lock, + const void *data, + size_t length, + heim_oid *contentType, + heim_octet_string *content) +{ + heim_octet_string cont; + CMSEncryptedData ed; + AlgorithmIdentifier *ai; + int ret; + + memset(content, 0, sizeof(*content)); + memset(&cont, 0, sizeof(cont)); + + ret = decode_CMSEncryptedData(data, length, &ed, NULL); + if (ret) { + hx509_set_error_string(context, 0, ret, + "Failed to decode CMSEncryptedData"); + return ret; + } + + if (ed.encryptedContentInfo.encryptedContent == NULL) { + ret = HX509_CMS_NO_DATA_AVAILABLE; + hx509_set_error_string(context, 0, ret, + "No content in EncryptedData"); + goto out; + } + + ret = der_copy_oid(&ed.encryptedContentInfo.contentType, contentType); + if (ret) { + hx509_clear_error_string(context); + goto out; + } + + ai = &ed.encryptedContentInfo.contentEncryptionAlgorithm; + if (ai->parameters == NULL) { + ret = HX509_ALG_NOT_SUPP; + hx509_clear_error_string(context); + goto out; + } + + ret = _hx509_pbe_decrypt(context, + lock, + ai, + ed.encryptedContentInfo.encryptedContent, + &cont); + if (ret) + goto out; + + *content = cont; + +out: + if (ret) { + if (cont.data) + free(cont.data); + } + free_CMSEncryptedData(&ed); + return ret; +} diff --git a/source4/heimdal/lib/hx509/collector.c b/source4/heimdal/lib/hx509/collector.c new file mode 100644 index 0000000000..d8212927e6 --- /dev/null +++ b/source4/heimdal/lib/hx509/collector.c @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hx_locl.h" +RCSID("$Id$"); + +struct private_key { + AlgorithmIdentifier alg; + hx509_private_key private_key; + heim_octet_string localKeyId; +}; + +struct hx509_collector { + hx509_lock lock; + hx509_certs unenvelop_certs; + hx509_certs certs; + struct { + struct private_key **data; + size_t len; + } val; +}; + + +int +_hx509_collector_alloc(hx509_context context, hx509_lock lock, struct hx509_collector **collector) +{ + struct hx509_collector *c; + int ret; + + *collector = NULL; + + c = calloc(1, sizeof(*c)); + if (c == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + c->lock = lock; + + ret = hx509_certs_init(context, "MEMORY:collector-unenvelop-cert", + 0,NULL, &c->unenvelop_certs); + if (ret) { + free(c); + return ret; + } + c->val.data = NULL; + c->val.len = 0; + ret = hx509_certs_init(context, "MEMORY:collector-tmp-store", + 0, NULL, &c->certs); + if (ret) { + hx509_certs_free(&c->unenvelop_certs); + free(c); + return ret; + } + + *collector = c; + return 0; +} + +hx509_lock +_hx509_collector_get_lock(struct hx509_collector *c) +{ + return c->lock; +} + + +int +_hx509_collector_certs_add(hx509_context context, + struct hx509_collector *c, + hx509_cert cert) +{ + return hx509_certs_add(context, c->certs, cert); +} + +static void +free_private_key(struct private_key *key) +{ + free_AlgorithmIdentifier(&key->alg); + if (key->private_key) + _hx509_private_key_free(&key->private_key); + der_free_octet_string(&key->localKeyId); + free(key); +} + +int +_hx509_collector_private_key_add(hx509_context context, + struct hx509_collector *c, + const AlgorithmIdentifier *alg, + hx509_private_key private_key, + const heim_octet_string *key_data, + const heim_octet_string *localKeyId) +{ + struct private_key *key; + void *d; + int ret; + + key = calloc(1, sizeof(*key)); + if (key == NULL) + return ENOMEM; + + d = realloc(c->val.data, (c->val.len + 1) * sizeof(c->val.data[0])); + if (d == NULL) { + free(key); + hx509_set_error_string(context, 0, ENOMEM, "Out of memory"); + return ENOMEM; + } + c->val.data = d; + + ret = copy_AlgorithmIdentifier(alg, &key->alg); + if (ret) { + hx509_set_error_string(context, 0, ret, "Failed to copy " + "AlgorithmIdentifier"); + goto out; + } + if (private_key) { + key->private_key = private_key; + } else { + ret = _hx509_parse_private_key(context, &alg->algorithm, + key_data->data, key_data->length, + &key->private_key); + if (ret) + goto out; + } + if (localKeyId) { + ret = der_copy_octet_string(localKeyId, &key->localKeyId); + if (ret) { + hx509_set_error_string(context, 0, ret, + "Failed to copy localKeyId"); + goto out; + } + } else + memset(&key->localKeyId, 0, sizeof(key->localKeyId)); + + c->val.data[c->val.len] = key; + c->val.len++; + +out: + if (ret) + free_private_key(key); + + return ret; +} + +static int +match_localkeyid(hx509_context context, + struct private_key *value, + hx509_certs certs) +{ + hx509_cert cert; + hx509_query q; + int ret; + + if (value->localKeyId.length == 0) { + hx509_set_error_string(context, 0, HX509_LOCAL_ATTRIBUTE_MISSING, + "No local key attribute on private key"); + return HX509_LOCAL_ATTRIBUTE_MISSING; + } + + _hx509_query_clear(&q); + q.match |= HX509_QUERY_MATCH_LOCAL_KEY_ID; + + q.local_key_id = &value->localKeyId; + + ret = hx509_certs_find(context, certs, &q, &cert); + if (ret == 0) { + + if (value->private_key) + _hx509_cert_assign_key(cert, value->private_key); + hx509_cert_free(cert); + } + return ret; +} + +static int +match_keys(hx509_context context, struct private_key *value, hx509_certs certs) +{ + hx509_cursor cursor; + hx509_cert c; + int ret, found = HX509_CERT_NOT_FOUND; + + if (value->private_key == NULL) { + hx509_set_error_string(context, 0, HX509_PRIVATE_KEY_MISSING, + "No private key to compare with"); + return HX509_PRIVATE_KEY_MISSING; + } + + ret = hx509_certs_start_seq(context, certs, &cursor); + if (ret) + return ret; + + c = NULL; + while (1) { + ret = hx509_certs_next_cert(context, certs, cursor, &c); + if (ret) + break; + if (c == NULL) + break; + if (_hx509_cert_private_key(c)) { + hx509_cert_free(c); + continue; + } + + ret = _hx509_match_keys(c, value->private_key); + if (ret) { + _hx509_cert_assign_key(c, value->private_key); + hx509_cert_free(c); + found = 0; + break; + } + hx509_cert_free(c); + } + + hx509_certs_end_seq(context, certs, cursor); + + if (found) + hx509_clear_error_string(context); + + return found; +} + +int +_hx509_collector_collect_certs(hx509_context context, + struct hx509_collector *c, + hx509_certs *ret_certs) +{ + hx509_certs certs; + int ret, i; + + *ret_certs = NULL; + + ret = hx509_certs_init(context, "MEMORY:collector-store", 0, NULL, &certs); + if (ret) + return ret; + + ret = hx509_certs_merge(context, certs, c->certs); + if (ret) { + hx509_certs_free(&certs); + return ret; + } + + for (i = 0; i < c->val.len; i++) { + ret = match_localkeyid(context, c->val.data[i], certs); + if (ret == 0) + continue; + ret = match_keys(context, c->val.data[i], certs); + if (ret == 0) + continue; + } + + *ret_certs = certs; + + return 0; +} + +int +_hx509_collector_collect_private_keys(hx509_context context, + struct hx509_collector *c, + hx509_private_key **keys) +{ + int i, nkeys; + + *keys = NULL; + + for (i = 0, nkeys = 0; i < c->val.len; i++) + if (c->val.data[i]->private_key) + nkeys++; + + *keys = calloc(nkeys + 1, sizeof(**keys)); + if (*keys == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "malloc - out of memory"); + return ENOMEM; + } + + for (i = 0, nkeys = 0; i < c->val.len; i++) { + if (c->val.data[i]->private_key) { + (*keys)[nkeys++] = c->val.data[i]->private_key; + c->val.data[i]->private_key = NULL; + } + } + (*keys)[nkeys++] = NULL; + + return 0; +} + + +void +_hx509_collector_free(struct hx509_collector *c) +{ + int i; + + if (c->unenvelop_certs) + hx509_certs_free(&c->unenvelop_certs); + if (c->certs) + hx509_certs_free(&c->certs); + for (i = 0; i < c->val.len; i++) + free_private_key(c->val.data[i]); + if (c->val.data) + free(c->val.data); + free(c); +} diff --git a/source4/heimdal/lib/hx509/crmf.asn1 b/source4/heimdal/lib/hx509/crmf.asn1 new file mode 100644 index 0000000000..3d8403c8e8 --- /dev/null +++ b/source4/heimdal/lib/hx509/crmf.asn1 @@ -0,0 +1,113 @@ +-- $Id$ +PKCS10 DEFINITIONS ::= + +BEGIN + +IMPORTS + Time, + GeneralName, + SubjectPublicKeyInfo, + RelativeDistinguishedName, + AttributeTypeAndValue, + Extension, + AlgorithmIdentifier + FROM rfc2459 + heim_any + FROM heim; + +CRMFRDNSequence ::= SEQUENCE OF RelativeDistinguishedName + +Controls ::= SEQUENCE -- SIZE(1..MAX) -- OF AttributeTypeAndValue + +-- XXX IMPLICIT brokenness +POPOSigningKey ::= SEQUENCE { + poposkInput [0] IMPLICIT POPOSigningKeyInput OPTIONAL, + algorithmIdentifier AlgorithmIdentifier, + signature BIT STRING } + +PKMACValue ::= SEQUENCE { + algId AlgorithmIdentifier, + value BIT STRING +} + +-- XXX IMPLICIT brokenness +POPOSigningKeyInput ::= SEQUENCE { + authInfo CHOICE { + sender [0] IMPLICIT GeneralName, + publicKeyMAC PKMACValue + }, + publicKey SubjectPublicKeyInfo +} -- from CertTemplate + + +PBMParameter ::= SEQUENCE { + salt OCTET STRING, + owf AlgorithmIdentifier, + iterationCount INTEGER, + mac AlgorithmIdentifier +} + +SubsequentMessage ::= INTEGER { + encrCert (0), + challengeResp (1) +} + +-- XXX IMPLICIT brokenness +POPOPrivKey ::= CHOICE { + thisMessage [0] BIT STRING, -- Deprecated + subsequentMessage [1] IMPLICIT SubsequentMessage, + dhMAC [2] BIT STRING, -- Deprecated + agreeMAC [3] IMPLICIT PKMACValue, + encryptedKey [4] heim_any +} + +-- XXX IMPLICIT brokenness +ProofOfPossession ::= CHOICE { + raVerified [0] NULL, + signature [1] POPOSigningKey, + keyEncipherment [2] POPOPrivKey, + keyAgreement [3] POPOPrivKey +} + +CertTemplate ::= SEQUENCE { + version [0] INTEGER OPTIONAL, + serialNumber [1] INTEGER OPTIONAL, + signingAlg [2] SEQUENCE { + algorithm OBJECT IDENTIFIER, + parameters heim_any OPTIONAL + } -- AlgorithmIdentifier -- OPTIONAL, + issuer [3] IMPLICIT CHOICE { + rdnSequence CRMFRDNSequence + } -- Name -- OPTIONAL, + validity [4] SEQUENCE { + notBefore [0] Time OPTIONAL, + notAfter [1] Time OPTIONAL + } -- OptionalValidity -- OPTIONAL, + subject [5] IMPLICIT CHOICE { + rdnSequence CRMFRDNSequence + } -- Name -- OPTIONAL, + publicKey [6] IMPLICIT SEQUENCE { + algorithm AlgorithmIdentifier, + subjectPublicKey BIT STRING OPTIONAL + } -- SubjectPublicKeyInfo -- OPTIONAL, + issuerUID [7] IMPLICIT BIT STRING OPTIONAL, + subjectUID [8] IMPLICIT BIT STRING OPTIONAL, + extensions [9] IMPLICIT SEQUENCE OF Extension OPTIONAL +} + +CertRequest ::= SEQUENCE { + certReqId INTEGER, + certTemplate CertTemplate, + controls Controls OPTIONAL +} + +CertReqMsg ::= SEQUENCE { + certReq CertRequest, + popo ProofOfPossession OPTIONAL, + regInfo SEQUENCE OF AttributeTypeAndValue OPTIONAL } + +CertReqMessages ::= SEQUENCE OF CertReqMsg + + +END + diff --git a/source4/heimdal/lib/hx509/crypto.c b/source4/heimdal/lib/hx509/crypto.c new file mode 100644 index 0000000000..e16977c6bf --- /dev/null +++ b/source4/heimdal/lib/hx509/crypto.c @@ -0,0 +1,2706 @@ +/* + * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hx_locl.h" +RCSID("$Id$"); + +struct hx509_crypto; + +struct signature_alg; + +enum crypto_op_type { + COT_SIGN +}; + +struct hx509_generate_private_context { + const heim_oid *key_oid; + int isCA; + unsigned long num_bits; +}; + +struct hx509_private_key_ops { + const char *pemtype; + const heim_oid *(*key_oid)(void); + int (*get_spki)(hx509_context, + const hx509_private_key, + SubjectPublicKeyInfo *); + int (*export)(hx509_context context, + const hx509_private_key, + heim_octet_string *); + int (*import)(hx509_context, + const void *data, + size_t len, + hx509_private_key private_key); + 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); + int (*sign)(hx509_context context, + const hx509_private_key, + const AlgorithmIdentifier *, + const heim_octet_string *, + AlgorithmIdentifier *, + heim_octet_string *); +#if 0 + const AlgorithmIdentifier *(*preferred_sig_alg) + (const hx509_private_key, + const hx509_peer_info); + int (*unwrap)(hx509_context context, + const hx509_private_key, + const AlgorithmIdentifier *, + const heim_octet_string *, + heim_octet_string *); +#endif +}; + +struct hx509_private_key { + unsigned int ref; + const struct signature_alg *md; + const heim_oid *signature_alg; + union { + RSA *rsa; + void *keydata; + } private_key; + /* new crypto layer */ + hx509_private_key_ops *ops; +}; + +/* + * + */ + +struct signature_alg { + const char *name; + const heim_oid *(*sig_oid)(void); + const AlgorithmIdentifier *(*sig_alg)(void); + const heim_oid *(*key_oid)(void); + const heim_oid *(*digest_oid)(void); + int flags; +#define PROVIDE_CONF 1 +#define REQUIRE_SIGNER 2 + +#define SIG_DIGEST 0x100 +#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 *, + const AlgorithmIdentifier *, + const heim_octet_string *, + const heim_octet_string *); + int (*create_signature)(hx509_context, + const struct signature_alg *, + const hx509_private_key, + const AlgorithmIdentifier *, + const heim_octet_string *, + AlgorithmIdentifier *, + heim_octet_string *); +}; + +/* + * + */ + +static BIGNUM * +heim_int2BN(const heim_integer *i) +{ + BIGNUM *bn; + + bn = BN_bin2bn(i->data, i->length, NULL); + BN_set_negative(bn, i->negative); + return bn; +} + +/* + * + */ + +static int +set_digest_alg(DigestAlgorithmIdentifier *id, + const heim_oid *oid, + const void *param, size_t length) +{ + int ret; + if (param) { + id->parameters = malloc(sizeof(*id->parameters)); + if (id->parameters == NULL) + return ENOMEM; + id->parameters->data = malloc(length); + if (id->parameters->data == NULL) { + free(id->parameters); + id->parameters = NULL; + return ENOMEM; + } + memcpy(id->parameters->data, param, length); + id->parameters->length = length; + } else + id->parameters = NULL; + ret = der_copy_oid(oid, &id->algorithm); + if (ret) { + if (id->parameters) { + free(id->parameters->data); + free(id->parameters); + id->parameters = NULL; + } + return ret; + } + return 0; +} + +/* + * + */ + +static int +rsa_verify_signature(hx509_context context, + const struct signature_alg *sig_alg, + const Certificate *signer, + const AlgorithmIdentifier *alg, + const heim_octet_string *data, + const heim_octet_string *sig) +{ + const SubjectPublicKeyInfo *spi; + DigestInfo di; + unsigned char *to; + int tosize, retsize; + int ret; + RSA *rsa; + RSAPublicKey pk; + size_t size; + + memset(&di, 0, sizeof(di)); + + spi = &signer->tbsCertificate.subjectPublicKeyInfo; + + rsa = RSA_new(); + if (rsa == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + ret = decode_RSAPublicKey(spi->subjectPublicKey.data, + spi->subjectPublicKey.length / 8, + &pk, &size); + if (ret) { + hx509_set_error_string(context, 0, ret, "Failed to decode RSAPublicKey"); + goto out; + } + + rsa->n = heim_int2BN(&pk.modulus); + rsa->e = heim_int2BN(&pk.publicExponent); + + free_RSAPublicKey(&pk); + + if (rsa->n == NULL || rsa->e == NULL) { + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, "out of memory"); + goto out; + } + + tosize = RSA_size(rsa); + to = malloc(tosize); + if (to == NULL) { + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, "out of memory"); + goto out; + } + + retsize = RSA_public_decrypt(sig->length, (unsigned char *)sig->data, + to, rsa, RSA_PKCS1_PADDING); + if (retsize <= 0) { + ret = HX509_CRYPTO_SIG_INVALID_FORMAT; + hx509_set_error_string(context, 0, ret, + "RSA public decrypt failed: %d", retsize); + free(to); + goto out; + } + if (retsize > tosize) + _hx509_abort("internal rsa decryption failure: ret > tosize"); + + if (sig_alg->flags & RA_RSA_USES_DIGEST_INFO) { + + 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; + } + + 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); + } + + out: + free_DigestInfo(&di); + RSA_free(rsa); + return ret; +} + +static int +rsa_create_signature(hx509_context context, + const struct signature_alg *sig_alg, + const hx509_private_key signer, + const AlgorithmIdentifier *alg, + const heim_octet_string *data, + AlgorithmIdentifier *signatureAlgorithm, + heim_octet_string *sig) +{ + const AlgorithmIdentifier *digest_alg; + heim_octet_string indata; + const heim_oid *sig_oid; + size_t size; + int ret; + + if (alg) + sig_oid = &alg->algorithm; + else + sig_oid = signer->signature_alg; + + if (der_heim_oid_cmp(sig_oid, oid_id_pkcs1_sha256WithRSAEncryption()) == 0) { + digest_alg = hx509_signature_sha256(); + } else if (der_heim_oid_cmp(sig_oid, oid_id_pkcs1_sha1WithRSAEncryption()) == 0) { + digest_alg = hx509_signature_sha1(); + } else if (der_heim_oid_cmp(sig_oid, oid_id_pkcs1_md5WithRSAEncryption()) == 0) { + digest_alg = hx509_signature_md5(); + } else if (der_heim_oid_cmp(sig_oid, oid_id_pkcs1_md5WithRSAEncryption()) == 0) { + digest_alg = hx509_signature_md5(); + } else if (der_heim_oid_cmp(sig_oid, oid_id_dsa_with_sha1()) == 0) { + 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; + + if (signatureAlgorithm) { + ret = set_digest_alg(signatureAlgorithm, sig_oid, "\x05\x00", 2); + if (ret) { + hx509_clear_error_string(context); + return ret; + } + } + + 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; + } + if (indata.length != size) + _hx509_abort("internal ASN.1 encoder error"); + } else { + indata = *data; + } + + sig->length = RSA_size(signer->private_key.rsa); + sig->data = malloc(sig->length); + if (sig->data == NULL) { + der_free_octet_string(&indata); + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + + ret = RSA_private_encrypt(indata.length, indata.data, + sig->data, + signer->private_key.rsa, + RSA_PKCS1_PADDING); + 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, + "RSA private decrypt failed: %d", ret); + return ret; + } + if (ret > sig->length) + _hx509_abort("RSA signature prelen longer the output len"); + + sig->length = ret; + + return 0; +} + +static int +rsa_private_key_import(hx509_context context, + const void *data, + size_t len, + hx509_private_key private_key) +{ + const unsigned char *p = data; + + private_key->private_key.rsa = + d2i_RSAPrivateKey(NULL, &p, len); + if (private_key->private_key.rsa == NULL) { + hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, + "Failed to parse RSA key"); + return HX509_PARSING_KEY_FAILED; + } + private_key->signature_alg = oid_id_pkcs1_sha1WithRSAEncryption(); + + return 0; +} + +static int +rsa_private_key2SPKI(hx509_context context, + hx509_private_key private_key, + SubjectPublicKeyInfo *spki) +{ + int len, ret; + + memset(spki, 0, sizeof(*spki)); + + len = i2d_RSAPublicKey(private_key->private_key.rsa, NULL); + + spki->subjectPublicKey.data = malloc(len); + if (spki->subjectPublicKey.data == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "malloc - out of memory"); + return ENOMEM; + } + spki->subjectPublicKey.length = len * 8; + + ret = set_digest_alg(&spki->algorithm,oid_id_pkcs1_rsaEncryption(), + "\x05\x00", 2); + if (ret) { + hx509_set_error_string(context, 0, ret, "malloc - out of memory"); + free(spki->subjectPublicKey.data); + spki->subjectPublicKey.data = NULL; + spki->subjectPublicKey.length = 0; + return ret; + } + + { + unsigned char *pp = spki->subjectPublicKey.data; + i2d_RSAPublicKey(private_key->private_key.rsa, &pp); + } + + return 0; +} + +static int +rsa_generate_private_key(hx509_context context, + struct hx509_generate_private_context *ctx, + hx509_private_key private_key) +{ + BIGNUM *e; + int ret; + unsigned long bits; + + static const int default_rsa_e = 65537; + static const int default_rsa_bits = 1024; + + private_key->private_key.rsa = RSA_new(); + if (private_key->private_key.rsa == NULL) { + hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, + "Failed to generate RSA key"); + return HX509_PARSING_KEY_FAILED; + } + + e = BN_new(); + BN_set_word(e, default_rsa_e); + + bits = default_rsa_bits; + + if (ctx->num_bits) + bits = ctx->num_bits; + else if (ctx->isCA) + bits *= 2; + + ret = RSA_generate_key_ex(private_key->private_key.rsa, bits, e, NULL); + BN_free(e); + if (ret != 1) { + hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, + "Failed to generate RSA key"); + return HX509_PARSING_KEY_FAILED; + } + private_key->signature_alg = oid_id_pkcs1_sha1WithRSAEncryption(); + + return 0; +} + +static int +rsa_private_key_export(hx509_context context, + const hx509_private_key key, + heim_octet_string *data) +{ + int ret; + + data->data = NULL; + data->length = 0; + + ret = i2d_RSAPrivateKey(key->private_key.rsa, NULL); + if (ret <= 0) { + ret = EINVAL; + hx509_set_error_string(context, 0, ret, + "Private key is not exportable"); + return ret; + } + + data->data = malloc(ret); + if (data->data == NULL) { + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, "malloc out of memory"); + return ret; + } + data->length = ret; + + { + unsigned char *p = data->data; + i2d_RSAPrivateKey(key->private_key.rsa, &p); + } + + 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", + oid_id_pkcs1_rsaEncryption, + rsa_private_key2SPKI, + rsa_private_key_export, + rsa_private_key_import, + rsa_generate_private_key, + rsa_get_internal +}; + + +/* + * + */ + +static int +dsa_verify_signature(hx509_context context, + const struct signature_alg *sig_alg, + const Certificate *signer, + const AlgorithmIdentifier *alg, + const heim_octet_string *data, + const heim_octet_string *sig) +{ + const SubjectPublicKeyInfo *spi; + DSAPublicKey pk; + DSAParams param; + size_t size; + DSA *dsa; + int ret; + + spi = &signer->tbsCertificate.subjectPublicKeyInfo; + + dsa = DSA_new(); + if (dsa == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + + ret = decode_DSAPublicKey(spi->subjectPublicKey.data, + spi->subjectPublicKey.length / 8, + &pk, &size); + if (ret) + goto out; + + dsa->pub_key = heim_int2BN(&pk); + + free_DSAPublicKey(&pk); + + if (dsa->pub_key == NULL) { + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, "out of memory"); + goto out; + } + + if (spi->algorithm.parameters == NULL) { + ret = HX509_CRYPTO_SIG_INVALID_FORMAT; + hx509_set_error_string(context, 0, ret, "DSA parameters missing"); + goto out; + } + + ret = decode_DSAParams(spi->algorithm.parameters->data, + spi->algorithm.parameters->length, + ¶m, + &size); + if (ret) { + hx509_set_error_string(context, 0, ret, "DSA parameters failed to decode"); + goto out; + } + + dsa->p = heim_int2BN(¶m.p); + dsa->q = heim_int2BN(¶m.q); + dsa->g = heim_int2BN(¶m.g); + + free_DSAParams(¶m); + + if (dsa->p == NULL || dsa->q == NULL || dsa->g == NULL) { + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, "out of memory"); + goto out; + } + + ret = DSA_verify(-1, data->data, data->length, + (unsigned char*)sig->data, sig->length, + dsa); + if (ret == 1) + ret = 0; + else if (ret == 0 || ret == -1) { + ret = HX509_CRYPTO_BAD_SIGNATURE; + hx509_set_error_string(context, 0, ret, "BAD DSA sigature"); + } else { + ret = HX509_CRYPTO_SIG_INVALID_FORMAT; + hx509_set_error_string(context, 0, ret, "Invalid format of DSA sigature"); + } + + out: + DSA_free(dsa); + + return ret; +} + +#if 0 +static int +dsa_parse_private_key(hx509_context context, + const void *data, + size_t len, + hx509_private_key private_key) +{ + const unsigned char *p = data; + + private_key->private_key.dsa = + d2i_DSAPrivateKey(NULL, &p, len); + if (private_key->private_key.dsa == NULL) + return EINVAL; + private_key->signature_alg = oid_id_dsa_with_sha1(); + + return 0; +/* else */ + hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, + "No support to parse DSA keys"); + return HX509_PARSING_KEY_FAILED; +} +#endif + + +static int +sha1_verify_signature(hx509_context context, + const struct signature_alg *sig_alg, + const Certificate *signer, + const AlgorithmIdentifier *alg, + const heim_octet_string *data, + const heim_octet_string *sig) +{ + unsigned char digest[SHA_DIGEST_LENGTH]; + SHA_CTX m; + + if (sig->length != SHA_DIGEST_LENGTH) { + hx509_set_error_string(context, 0, HX509_CRYPTO_SIG_INVALID_FORMAT, + "SHA1 sigature have wrong length"); + return HX509_CRYPTO_SIG_INVALID_FORMAT; + } + + SHA1_Init(&m); + SHA1_Update(&m, data->data, data->length); + SHA1_Final (digest, &m); + + if (memcmp(digest, sig->data, SHA_DIGEST_LENGTH) != 0) { + hx509_set_error_string(context, 0, HX509_CRYPTO_BAD_SIGNATURE, + "Bad SHA1 sigature"); + return HX509_CRYPTO_BAD_SIGNATURE; + } + + return 0; +} + +static int +sha256_create_signature(hx509_context context, + const struct signature_alg *sig_alg, + const hx509_private_key signer, + const AlgorithmIdentifier *alg, + const heim_octet_string *data, + AlgorithmIdentifier *signatureAlgorithm, + heim_octet_string *sig) +{ + SHA256_CTX m; + + memset(sig, 0, sizeof(*sig)); + + if (signatureAlgorithm) { + int ret; + ret = set_digest_alg(signatureAlgorithm, (*sig_alg->sig_oid)(), + "\x05\x00", 2); + if (ret) + return ret; + } + + + sig->data = malloc(SHA256_DIGEST_LENGTH); + if (sig->data == NULL) { + sig->length = 0; + return ENOMEM; + } + sig->length = SHA256_DIGEST_LENGTH; + + SHA256_Init(&m); + SHA256_Update(&m, data->data, data->length); + SHA256_Final (sig->data, &m); + + return 0; +} + +static int +sha256_verify_signature(hx509_context context, + const struct signature_alg *sig_alg, + const Certificate *signer, + const AlgorithmIdentifier *alg, + const heim_octet_string *data, + const heim_octet_string *sig) +{ + unsigned char digest[SHA256_DIGEST_LENGTH]; + SHA256_CTX m; + + if (sig->length != SHA256_DIGEST_LENGTH) { + hx509_set_error_string(context, 0, HX509_CRYPTO_SIG_INVALID_FORMAT, + "SHA256 sigature have wrong length"); + return HX509_CRYPTO_SIG_INVALID_FORMAT; + } + + SHA256_Init(&m); + SHA256_Update(&m, data->data, data->length); + SHA256_Final (digest, &m); + + if (memcmp(digest, sig->data, SHA256_DIGEST_LENGTH) != 0) { + hx509_set_error_string(context, 0, HX509_CRYPTO_BAD_SIGNATURE, + "Bad SHA256 sigature"); + return HX509_CRYPTO_BAD_SIGNATURE; + } + + return 0; +} + +static int +sha1_create_signature(hx509_context context, + const struct signature_alg *sig_alg, + const hx509_private_key signer, + const AlgorithmIdentifier *alg, + const heim_octet_string *data, + AlgorithmIdentifier *signatureAlgorithm, + heim_octet_string *sig) +{ + SHA_CTX m; + + memset(sig, 0, sizeof(*sig)); + + if (signatureAlgorithm) { + int ret; + ret = set_digest_alg(signatureAlgorithm, (*sig_alg->sig_oid)(), + "\x05\x00", 2); + if (ret) + return ret; + } + + + sig->data = malloc(SHA_DIGEST_LENGTH); + if (sig->data == NULL) { + sig->length = 0; + return ENOMEM; + } + sig->length = SHA_DIGEST_LENGTH; + + SHA1_Init(&m); + SHA1_Update(&m, data->data, data->length); + SHA1_Final (sig->data, &m); + + return 0; +} + +static int +md5_verify_signature(hx509_context context, + const struct signature_alg *sig_alg, + const Certificate *signer, + const AlgorithmIdentifier *alg, + const heim_octet_string *data, + const heim_octet_string *sig) +{ + unsigned char digest[MD5_DIGEST_LENGTH]; + MD5_CTX m; + + if (sig->length != MD5_DIGEST_LENGTH) { + hx509_set_error_string(context, 0, HX509_CRYPTO_SIG_INVALID_FORMAT, + "MD5 sigature have wrong length"); + return HX509_CRYPTO_SIG_INVALID_FORMAT; + } + + MD5_Init(&m); + MD5_Update(&m, data->data, data->length); + MD5_Final (digest, &m); + + if (memcmp(digest, sig->data, MD5_DIGEST_LENGTH) != 0) { + hx509_set_error_string(context, 0, HX509_CRYPTO_BAD_SIGNATURE, + "Bad MD5 sigature"); + return HX509_CRYPTO_BAD_SIGNATURE; + } + + return 0; +} + +static int +md2_verify_signature(hx509_context context, + const struct signature_alg *sig_alg, + const Certificate *signer, + const AlgorithmIdentifier *alg, + const heim_octet_string *data, + const heim_octet_string *sig) +{ + unsigned char digest[MD2_DIGEST_LENGTH]; + MD2_CTX m; + + if (sig->length != MD2_DIGEST_LENGTH) { + hx509_set_error_string(context, 0, HX509_CRYPTO_SIG_INVALID_FORMAT, + "MD2 sigature have wrong length"); + return HX509_CRYPTO_SIG_INVALID_FORMAT; + } + + MD2_Init(&m); + MD2_Update(&m, data->data, data->length); + MD2_Final (digest, &m); + + if (memcmp(digest, sig->data, MD2_DIGEST_LENGTH) != 0) { + hx509_set_error_string(context, 0, HX509_CRYPTO_BAD_SIGNATURE, + "Bad MD2 sigature"); + return HX509_CRYPTO_BAD_SIGNATURE; + } + + 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|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG, + rsa_verify_signature, + rsa_create_signature +}; + +static const struct signature_alg rsa_with_sha256_alg = { + "rsa-with-sha256", + oid_id_pkcs1_sha256WithRSAEncryption, + hx509_signature_rsa_with_sha256, + oid_id_pkcs1_rsaEncryption, + oid_id_sha256, + PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG, + rsa_verify_signature, + rsa_create_signature +}; + +static const struct signature_alg rsa_with_sha1_alg = { + "rsa-with-sha1", + oid_id_pkcs1_sha1WithRSAEncryption, + hx509_signature_rsa_with_sha1, + oid_id_pkcs1_rsaEncryption, + oid_id_secsig_sha_1, + PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG, + rsa_verify_signature, + rsa_create_signature +}; + +static const struct signature_alg rsa_with_md5_alg = { + "rsa-with-md5", + oid_id_pkcs1_md5WithRSAEncryption, + hx509_signature_rsa_with_md5, + oid_id_pkcs1_rsaEncryption, + oid_id_rsa_digest_md5, + PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG, + rsa_verify_signature, + rsa_create_signature +}; + +static const struct signature_alg rsa_with_md2_alg = { + "rsa-with-md2", + oid_id_pkcs1_md2WithRSAEncryption, + hx509_signature_rsa_with_md2, + oid_id_pkcs1_rsaEncryption, + oid_id_rsa_digest_md2, + PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG, + rsa_verify_signature, + rsa_create_signature +}; + +static const struct signature_alg dsa_sha1_alg = { + "dsa-with-sha1", + oid_id_dsa_with_sha1, + NULL, + oid_id_dsa, + oid_id_secsig_sha_1, + PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG, + dsa_verify_signature, + /* create_signature */ NULL, +}; + +static const struct signature_alg sha256_alg = { + "sha-256", + oid_id_sha256, + hx509_signature_sha256, + NULL, + NULL, + SIG_DIGEST, + sha256_verify_signature, + sha256_create_signature +}; + +static const struct signature_alg sha1_alg = { + "sha1", + oid_id_secsig_sha_1, + hx509_signature_sha1, + NULL, + NULL, + SIG_DIGEST, + sha1_verify_signature, + sha1_create_signature +}; + +static const struct signature_alg md5_alg = { + "rsa-md5", + oid_id_rsa_digest_md5, + hx509_signature_md5, + NULL, + NULL, + SIG_DIGEST, + md5_verify_signature +}; + +static const struct signature_alg md2_alg = { + "rsa-md2", + oid_id_rsa_digest_md2, + hx509_signature_md2, + NULL, + NULL, + SIG_DIGEST, + md2_verify_signature +}; + +/* + * Order matter in this structure, "best" first for each "key + * compatible" type (type is RSA, DSA, none, etc) + */ + +static const struct signature_alg *sig_algs[] = { + &rsa_with_sha256_alg, + &rsa_with_sha1_alg, + &pkcs1_rsa_sha1_alg, + &rsa_with_md5_alg, + &rsa_with_md2_alg, + &heim_rsa_pkcs1_x509, + &dsa_sha1_alg, + &sha256_alg, + &sha1_alg, + &md5_alg, + &md2_alg, + NULL +}; + +static const struct signature_alg * +find_sig_alg(const heim_oid *oid) +{ + int i; + for (i = 0; sig_algs[i]; i++) + if (der_heim_oid_cmp((*sig_algs[i]->sig_oid)(), oid) == 0) + return sig_algs[i]; + return NULL; +} + +/* + * + */ + +static struct hx509_private_key_ops *private_algs[] = { + &rsa_private_key_ops, + NULL +}; + +static hx509_private_key_ops * +find_private_alg(const heim_oid *oid) +{ + int i; + for (i = 0; private_algs[i]; i++) { + if (private_algs[i]->key_oid == NULL) + continue; + if (der_heim_oid_cmp((*private_algs[i]->key_oid)(), oid) == 0) + return private_algs[i]; + } + return NULL; +} + + +int +_hx509_verify_signature(hx509_context context, + const Certificate *signer, + const AlgorithmIdentifier *alg, + const heim_octet_string *data, + const heim_octet_string *sig) +{ + const struct signature_alg *md; + + md = find_sig_alg(&alg->algorithm); + if (md == NULL) { + hx509_clear_error_string(context); + return HX509_SIG_ALG_NO_SUPPORTED; + } + if (signer && (md->flags & PROVIDE_CONF) == 0) { + hx509_clear_error_string(context); + return HX509_CRYPTO_SIG_NO_CONF; + } + if (signer == NULL && (md->flags & REQUIRE_SIGNER)) { + hx509_clear_error_string(context); + return HX509_CRYPTO_SIGNATURE_WITHOUT_SIGNER; + } + if (md->key_oid && signer) { + const SubjectPublicKeyInfo *spi; + spi = &signer->tbsCertificate.subjectPublicKeyInfo; + + if (der_heim_oid_cmp(&spi->algorithm.algorithm, (*md->key_oid)()) != 0) { + hx509_clear_error_string(context); + return HX509_SIG_ALG_DONT_MATCH_KEY_ALG; + } + } + return (*md->verify_signature)(context, md, signer, alg, data, sig); +} + +int +_hx509_verify_signature_bitstring(hx509_context context, + const Certificate *signer, + const AlgorithmIdentifier *alg, + const heim_octet_string *data, + const heim_bit_string *sig) +{ + heim_octet_string os; + + if (sig->length & 7) { + hx509_set_error_string(context, 0, HX509_CRYPTO_SIG_INVALID_FORMAT, + "signature not multiple of 8 bits"); + return HX509_CRYPTO_SIG_INVALID_FORMAT; + } + + os.data = sig->data; + os.length = sig->length / 8; + + return _hx509_verify_signature(context, signer, alg, data, &os); +} + +int +_hx509_create_signature(hx509_context context, + const hx509_private_key signer, + const AlgorithmIdentifier *alg, + const heim_octet_string *data, + AlgorithmIdentifier *signatureAlgorithm, + heim_octet_string *sig) +{ + const struct signature_alg *md; + + if (signer && signer->ops && signer->ops->handle_alg && + (*signer->ops->handle_alg)(signer, alg, COT_SIGN)) + { + return (*signer->ops->sign)(context, signer, alg, data, + signatureAlgorithm, sig); + } + + md = find_sig_alg(&alg->algorithm); + if (md == NULL) { + hx509_set_error_string(context, 0, HX509_SIG_ALG_NO_SUPPORTED, + "algorithm no supported"); + return HX509_SIG_ALG_NO_SUPPORTED; + } + + if (signer && (md->flags & PROVIDE_CONF) == 0) { + hx509_set_error_string(context, 0, HX509_SIG_ALG_NO_SUPPORTED, + "algorithm provides no conf"); + return HX509_CRYPTO_SIG_NO_CONF; + } + + return (*md->create_signature)(context, md, signer, alg, data, + signatureAlgorithm, sig); +} + +int +_hx509_create_signature_bitstring(hx509_context context, + const hx509_private_key signer, + const AlgorithmIdentifier *alg, + const heim_octet_string *data, + AlgorithmIdentifier *signatureAlgorithm, + heim_bit_string *sig) +{ + heim_octet_string os; + int ret; + + ret = _hx509_create_signature(context, signer, alg, + data, signatureAlgorithm, &os); + if (ret) + return ret; + sig->data = os.data; + sig->length = os.length * 8; + return 0; +} + +int +_hx509_public_encrypt(hx509_context context, + const heim_octet_string *cleartext, + const Certificate *cert, + heim_oid *encryption_oid, + heim_octet_string *ciphertext) +{ + const SubjectPublicKeyInfo *spi; + unsigned char *to; + int tosize; + int ret; + RSA *rsa; + RSAPublicKey pk; + size_t size; + + ciphertext->data = NULL; + ciphertext->length = 0; + + spi = &cert->tbsCertificate.subjectPublicKeyInfo; + + rsa = RSA_new(); + if (rsa == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + + ret = decode_RSAPublicKey(spi->subjectPublicKey.data, + spi->subjectPublicKey.length / 8, + &pk, &size); + if (ret) { + RSA_free(rsa); + hx509_set_error_string(context, 0, ret, "RSAPublicKey decode failure"); + return ret; + } + rsa->n = heim_int2BN(&pk.modulus); + rsa->e = heim_int2BN(&pk.publicExponent); + + free_RSAPublicKey(&pk); + + if (rsa->n == NULL || rsa->e == NULL) { + RSA_free(rsa); + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + + tosize = RSA_size(rsa); + to = malloc(tosize); + if (to == NULL) { + RSA_free(rsa); + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + + ret = RSA_public_encrypt(cleartext->length, + (unsigned char *)cleartext->data, + to, rsa, RSA_PKCS1_PADDING); + RSA_free(rsa); + if (ret <= 0) { + free(to); + hx509_set_error_string(context, 0, HX509_CRYPTO_RSA_PUBLIC_ENCRYPT, + "RSA public encrypt failed with %d", ret); + return HX509_CRYPTO_RSA_PUBLIC_ENCRYPT; + } + if (ret > tosize) + _hx509_abort("internal rsa decryption failure: ret > tosize"); + + ciphertext->length = ret; + ciphertext->data = to; + + ret = der_copy_oid(oid_id_pkcs1_rsaEncryption(), encryption_oid); + if (ret) { + der_free_octet_string(ciphertext); + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + + return 0; +} + +int +_hx509_private_key_private_decrypt(hx509_context context, + const heim_octet_string *ciphertext, + const heim_oid *encryption_oid, + hx509_private_key p, + heim_octet_string *cleartext) +{ + int ret; + + cleartext->data = NULL; + cleartext->length = 0; + + if (p->private_key.rsa == NULL) { + hx509_set_error_string(context, 0, HX509_PRIVATE_KEY_MISSING, + "Private RSA key missing"); + return HX509_PRIVATE_KEY_MISSING; + } + + cleartext->length = RSA_size(p->private_key.rsa); + cleartext->data = malloc(cleartext->length); + if (cleartext->data == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + ret = RSA_private_decrypt(ciphertext->length, ciphertext->data, + cleartext->data, + p->private_key.rsa, + RSA_PKCS1_PADDING); + if (ret <= 0) { + der_free_octet_string(cleartext); + hx509_set_error_string(context, 0, HX509_CRYPTO_RSA_PRIVATE_DECRYPT, + "Failed to decrypt using private key: %d", ret); + return HX509_CRYPTO_RSA_PRIVATE_DECRYPT; + } + if (cleartext->length < ret) + _hx509_abort("internal rsa decryption failure: ret > tosize"); + + cleartext->length = ret; + + return 0; +} + + +int +_hx509_parse_private_key(hx509_context context, + const heim_oid *key_oid, + const void *data, + size_t len, + hx509_private_key *private_key) +{ + struct hx509_private_key_ops *ops; + int ret; + + *private_key = NULL; + + ops = find_private_alg(key_oid); + if (ops == NULL) { + hx509_clear_error_string(context); + return HX509_SIG_ALG_NO_SUPPORTED; + } + + ret = _hx509_private_key_init(private_key, ops, NULL); + if (ret) { + hx509_set_error_string(context, 0, ret, "out of memory"); + return ret; + } + + ret = (*ops->import)(context, data, len, *private_key); + if (ret) + _hx509_private_key_free(private_key); + + return ret; +} + +/* + * + */ + +int +_hx509_private_key2SPKI(hx509_context context, + hx509_private_key private_key, + SubjectPublicKeyInfo *spki) +{ + const struct hx509_private_key_ops *ops = private_key->ops; + if (ops == NULL || ops->get_spki == NULL) { + hx509_set_error_string(context, 0, HX509_UNIMPLEMENTED_OPERATION, + "Private key have no key2SPKI function"); + return HX509_UNIMPLEMENTED_OPERATION; + } + return (*ops->get_spki)(context, private_key, spki); +} + +int +_hx509_generate_private_key_init(hx509_context context, + const heim_oid *oid, + struct hx509_generate_private_context **ctx) +{ + *ctx = NULL; + + if (der_heim_oid_cmp(oid, oid_id_pkcs1_rsaEncryption()) != 0) { + hx509_set_error_string(context, 0, EINVAL, + "private key not an RSA key"); + return EINVAL; + } + + *ctx = calloc(1, sizeof(**ctx)); + if (*ctx == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + (*ctx)->key_oid = oid; + + return 0; +} + +int +_hx509_generate_private_key_is_ca(hx509_context context, + struct hx509_generate_private_context *ctx) +{ + ctx->isCA = 1; + return 0; +} + +int +_hx509_generate_private_key_bits(hx509_context context, + struct hx509_generate_private_context *ctx, + unsigned long bits) +{ + ctx->num_bits = bits; + return 0; +} + + +void +_hx509_generate_private_key_free(struct hx509_generate_private_context **ctx) +{ + free(*ctx); + *ctx = NULL; +} + +int +_hx509_generate_private_key(hx509_context context, + struct hx509_generate_private_context *ctx, + hx509_private_key *private_key) +{ + struct hx509_private_key_ops *ops; + int ret; + + *private_key = NULL; + + ops = find_private_alg(ctx->key_oid); + if (ops == NULL) { + hx509_clear_error_string(context); + return HX509_SIG_ALG_NO_SUPPORTED; + } + + ret = _hx509_private_key_init(private_key, ops, NULL); + if (ret) { + hx509_set_error_string(context, 0, ret, "out of memory"); + return ret; + } + + ret = (*ops->generate_private_key)(context, ctx, *private_key); + if (ret) + _hx509_private_key_free(private_key); + + return ret; +} + + +/* + * + */ + +static const heim_octet_string null_entry_oid = { 2, rk_UNCONST("\x05\x00") }; + +static const unsigned sha512_oid_tree[] = { 2, 16, 840, 1, 101, 3, 4, 2, 3 }; +const AlgorithmIdentifier _hx509_signature_sha512_data = { + { 9, rk_UNCONST(sha512_oid_tree) }, rk_UNCONST(&null_entry_oid) +}; + +static const unsigned sha384_oid_tree[] = { 2, 16, 840, 1, 101, 3, 4, 2, 2 }; +const AlgorithmIdentifier _hx509_signature_sha384_data = { + { 9, rk_UNCONST(sha384_oid_tree) }, rk_UNCONST(&null_entry_oid) +}; + +static const unsigned sha256_oid_tree[] = { 2, 16, 840, 1, 101, 3, 4, 2, 1 }; +const AlgorithmIdentifier _hx509_signature_sha256_data = { + { 9, rk_UNCONST(sha256_oid_tree) }, rk_UNCONST(&null_entry_oid) +}; + +static const unsigned sha1_oid_tree[] = { 1, 3, 14, 3, 2, 26 }; +const AlgorithmIdentifier _hx509_signature_sha1_data = { + { 6, rk_UNCONST(sha1_oid_tree) }, rk_UNCONST(&null_entry_oid) +}; + +static const unsigned md5_oid_tree[] = { 1, 2, 840, 113549, 2, 5 }; +const AlgorithmIdentifier _hx509_signature_md5_data = { + { 6, rk_UNCONST(md5_oid_tree) }, rk_UNCONST(&null_entry_oid) +}; + +static const unsigned md2_oid_tree[] = { 1, 2, 840, 113549, 2, 2 }; +const AlgorithmIdentifier _hx509_signature_md2_data = { + { 6, rk_UNCONST(md2_oid_tree) }, rk_UNCONST(&null_entry_oid) +}; + +static const unsigned rsa_with_sha512_oid[] ={ 1, 2, 840, 113549, 1, 1, 13 }; +const AlgorithmIdentifier _hx509_signature_rsa_with_sha512_data = { + { 7, rk_UNCONST(rsa_with_sha512_oid) }, NULL +}; + +static const unsigned rsa_with_sha384_oid[] ={ 1, 2, 840, 113549, 1, 1, 12 }; +const AlgorithmIdentifier _hx509_signature_rsa_with_sha384_data = { + { 7, rk_UNCONST(rsa_with_sha384_oid) }, NULL +}; + +static const unsigned rsa_with_sha256_oid[] ={ 1, 2, 840, 113549, 1, 1, 11 }; +const AlgorithmIdentifier _hx509_signature_rsa_with_sha256_data = { + { 7, rk_UNCONST(rsa_with_sha256_oid) }, NULL +}; + +static const unsigned rsa_with_sha1_oid[] ={ 1, 2, 840, 113549, 1, 1, 5 }; +const AlgorithmIdentifier _hx509_signature_rsa_with_sha1_data = { + { 7, rk_UNCONST(rsa_with_sha1_oid) }, NULL +}; + +static const unsigned rsa_with_md5_oid[] ={ 1, 2, 840, 113549, 1, 1, 4 }; +const AlgorithmIdentifier _hx509_signature_rsa_with_md5_data = { + { 7, rk_UNCONST(rsa_with_md5_oid) }, NULL +}; + +static const unsigned rsa_with_md2_oid[] ={ 1, 2, 840, 113549, 1, 1, 2 }; +const AlgorithmIdentifier _hx509_signature_rsa_with_md2_data = { + { 7, rk_UNCONST(rsa_with_md2_oid) }, NULL +}; + +static const unsigned rsa_oid[] ={ 1, 2, 840, 113549, 1, 1, 1 }; +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 +}; + +static const unsigned aes128_cbc_oid[] ={ 2, 16, 840, 1, 101, 3, 4, 1, 2 }; +const AlgorithmIdentifier _hx509_crypto_aes128_cbc_data = { + { 9, rk_UNCONST(aes128_cbc_oid) }, NULL +}; + +static const unsigned aes256_cbc_oid[] ={ 2, 16, 840, 1, 101, 3, 4, 1, 42 }; +const AlgorithmIdentifier _hx509_crypto_aes256_cbc_data = { + { 9, rk_UNCONST(aes256_cbc_oid) }, NULL +}; + +const AlgorithmIdentifier * +hx509_signature_sha512(void) +{ return &_hx509_signature_sha512_data; } + +const AlgorithmIdentifier * +hx509_signature_sha384(void) +{ return &_hx509_signature_sha384_data; } + +const AlgorithmIdentifier * +hx509_signature_sha256(void) +{ return &_hx509_signature_sha256_data; } + +const AlgorithmIdentifier * +hx509_signature_sha1(void) +{ return &_hx509_signature_sha1_data; } + +const AlgorithmIdentifier * +hx509_signature_md5(void) +{ return &_hx509_signature_md5_data; } + +const AlgorithmIdentifier * +hx509_signature_md2(void) +{ return &_hx509_signature_md2_data; } + +const AlgorithmIdentifier * +hx509_signature_rsa_with_sha512(void) +{ return &_hx509_signature_rsa_with_sha512_data; } + +const AlgorithmIdentifier * +hx509_signature_rsa_with_sha384(void) +{ return &_hx509_signature_rsa_with_sha384_data; } + +const AlgorithmIdentifier * +hx509_signature_rsa_with_sha256(void) +{ return &_hx509_signature_rsa_with_sha256_data; } + +const AlgorithmIdentifier * +hx509_signature_rsa_with_sha1(void) +{ return &_hx509_signature_rsa_with_sha1_data; } + +const AlgorithmIdentifier * +hx509_signature_rsa_with_md5(void) +{ return &_hx509_signature_rsa_with_md5_data; } + +const AlgorithmIdentifier * +hx509_signature_rsa_with_md2(void) +{ return &_hx509_signature_rsa_with_md2_data; } + +const AlgorithmIdentifier * +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; } + +const AlgorithmIdentifier * +hx509_crypto_aes128_cbc(void) +{ return &_hx509_crypto_aes128_cbc_data; } + +const AlgorithmIdentifier * +hx509_crypto_aes256_cbc(void) +{ return &_hx509_crypto_aes256_cbc_data; } + +/* + * + */ + +const AlgorithmIdentifier * _hx509_crypto_default_sig_alg = + &_hx509_signature_rsa_with_sha1_data; +const AlgorithmIdentifier * _hx509_crypto_default_digest_alg = + &_hx509_signature_sha1_data; +const AlgorithmIdentifier * _hx509_crypto_default_secret_alg = + &_hx509_crypto_aes128_cbc_data; + +/* + * + */ + +int +_hx509_private_key_init(hx509_private_key *key, + hx509_private_key_ops *ops, + void *keydata) +{ + *key = calloc(1, sizeof(**key)); + if (*key == NULL) + return ENOMEM; + (*key)->ref = 1; + (*key)->ops = ops; + (*key)->private_key.keydata = keydata; + return 0; +} + +hx509_private_key +_hx509_private_key_ref(hx509_private_key key) +{ + if (key->ref == 0) + _hx509_abort("key refcount <= 0 on ref"); + key->ref++; + if (key->ref == UINT_MAX) + _hx509_abort("key refcount == UINT_MAX on ref"); + return key; +} + +const char * +_hx509_private_pem_name(hx509_private_key key) +{ + return key->ops->pemtype; +} + +int +_hx509_private_key_free(hx509_private_key *key) +{ + if (key == NULL || *key == NULL) + return 0; + + if ((*key)->ref == 0) + _hx509_abort("key refcount == 0 on free"); + if (--(*key)->ref > 0) + return 0; + + if ((*key)->private_key.rsa) + RSA_free((*key)->private_key.rsa); + (*key)->private_key.rsa = NULL; + free(*key); + *key = NULL; + return 0; +} + +void +_hx509_private_key_assign_rsa(hx509_private_key key, void *ptr) +{ + if (key->private_key.rsa) + RSA_free(key->private_key.rsa); + key->private_key.rsa = ptr; + key->signature_alg = oid_id_pkcs1_sha1WithRSAEncryption(); + key->md = &pkcs1_rsa_sha1_alg; +} + +int +_hx509_private_key_oid(hx509_context context, + const hx509_private_key key, + heim_oid *data) +{ + int ret; + ret = der_copy_oid((*key->ops->key_oid)(), data); + if (ret) + hx509_set_error_string(context, 0, ret, "malloc out of memory"); + return ret; +} + +int +_hx509_private_key_exportable(hx509_private_key key) +{ + if (key->ops->export == NULL) + return 0; + 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, + heim_octet_string *data) +{ + if (key->ops->export == NULL) { + hx509_clear_error_string(context); + return HX509_UNIMPLEMENTED_OPERATION; + } + return (*key->ops->export)(context, key, data); +} + +/* + * + */ + +struct hx509cipher { + const char *name; + const heim_oid *(*oid_func)(void); + const AlgorithmIdentifier *(*ai_func)(void); + const EVP_CIPHER *(*evp_func)(void); + int (*get_params)(hx509_context, const hx509_crypto, + const heim_octet_string *, heim_octet_string *); + int (*set_params)(hx509_context, const heim_octet_string *, + hx509_crypto, heim_octet_string *); +}; + +struct hx509_crypto_data { + char *name; + const struct hx509cipher *cipher; + const EVP_CIPHER *c; + heim_octet_string key; + heim_oid oid; + void *param; +}; + +/* + * + */ + +static const heim_oid * +oid_private_rc2_40(void) +{ + static unsigned oid_data[] = { 127, 1 }; + static const heim_oid oid = { 2, oid_data }; + + return &oid; +} + + +/* + * + */ + +static int +CMSCBCParam_get(hx509_context context, const hx509_crypto crypto, + const heim_octet_string *ivec, heim_octet_string *param) +{ + size_t size; + int ret; + + assert(crypto->param == NULL); + if (ivec == NULL) + return 0; + + ASN1_MALLOC_ENCODE(CMSCBCParameter, param->data, param->length, + ivec, &size, ret); + if (ret == 0 && size != param->length) + _hx509_abort("Internal asn1 encoder failure"); + if (ret) + hx509_clear_error_string(context); + return ret; +} + +static int +CMSCBCParam_set(hx509_context context, const heim_octet_string *param, + hx509_crypto crypto, heim_octet_string *ivec) +{ + int ret; + if (ivec == NULL) + return 0; + + ret = decode_CMSCBCParameter(param->data, param->length, ivec, NULL); + if (ret) + hx509_clear_error_string(context); + + return ret; +} + +struct _RC2_params { + int maximum_effective_key; +}; + +static int +CMSRC2CBCParam_get(hx509_context context, const hx509_crypto crypto, + const heim_octet_string *ivec, heim_octet_string *param) +{ + CMSRC2CBCParameter rc2params; + const struct _RC2_params *p = crypto->param; + int maximum_effective_key = 128; + size_t size; + int ret; + + memset(&rc2params, 0, sizeof(rc2params)); + + if (p) + maximum_effective_key = p->maximum_effective_key; + + switch(maximum_effective_key) { + case 40: + rc2params.rc2ParameterVersion = 160; + break; + case 64: + rc2params.rc2ParameterVersion = 120; + break; + case 128: + rc2params.rc2ParameterVersion = 58; + break; + } + rc2params.iv = *ivec; + + ASN1_MALLOC_ENCODE(CMSRC2CBCParameter, param->data, param->length, + &rc2params, &size, ret); + if (ret == 0 && size != param->length) + _hx509_abort("Internal asn1 encoder failure"); + + return ret; +} + +static int +CMSRC2CBCParam_set(hx509_context context, const heim_octet_string *param, + hx509_crypto crypto, heim_octet_string *ivec) +{ + CMSRC2CBCParameter rc2param; + struct _RC2_params *p; + size_t size; + int ret; + + ret = decode_CMSRC2CBCParameter(param->data, param->length, + &rc2param, &size); + if (ret) { + hx509_clear_error_string(context); + return ret; + } + + p = calloc(1, sizeof(*p)); + if (p == NULL) { + free_CMSRC2CBCParameter(&rc2param); + hx509_clear_error_string(context); + return ENOMEM; + } + switch(rc2param.rc2ParameterVersion) { + case 160: + crypto->c = EVP_rc2_40_cbc(); + p->maximum_effective_key = 40; + break; + case 120: + crypto->c = EVP_rc2_64_cbc(); + p->maximum_effective_key = 64; + break; + case 58: + crypto->c = EVP_rc2_cbc(); + p->maximum_effective_key = 128; + break; + default: + free(p); + free_CMSRC2CBCParameter(&rc2param); + return HX509_CRYPTO_SIG_INVALID_FORMAT; + } + if (ivec) + ret = der_copy_octet_string(&rc2param.iv, ivec); + free_CMSRC2CBCParameter(&rc2param); + if (ret) { + free(p); + hx509_clear_error_string(context); + } else + crypto->param = p; + + return ret; +} + +/* + * + */ + +static const struct hx509cipher ciphers[] = { + { + "rc2-cbc", + oid_id_pkcs3_rc2_cbc, + NULL, + EVP_rc2_cbc, + CMSRC2CBCParam_get, + CMSRC2CBCParam_set + }, + { + "rc2-cbc", + oid_id_rsadsi_rc2_cbc, + NULL, + EVP_rc2_cbc, + CMSRC2CBCParam_get, + CMSRC2CBCParam_set + }, + { + "rc2-40-cbc", + oid_private_rc2_40, + NULL, + EVP_rc2_40_cbc, + CMSRC2CBCParam_get, + CMSRC2CBCParam_set + }, + { + "des-ede3-cbc", + oid_id_pkcs3_des_ede3_cbc, + NULL, + EVP_des_ede3_cbc, + CMSCBCParam_get, + CMSCBCParam_set + }, + { + "des-ede3-cbc", + oid_id_rsadsi_des_ede3_cbc, + hx509_crypto_des_rsdi_ede3_cbc, + EVP_des_ede3_cbc, + CMSCBCParam_get, + CMSCBCParam_set + }, + { + "aes-128-cbc", + oid_id_aes_128_cbc, + hx509_crypto_aes128_cbc, + EVP_aes_128_cbc, + CMSCBCParam_get, + CMSCBCParam_set + }, + { + "aes-192-cbc", + oid_id_aes_192_cbc, + NULL, + EVP_aes_192_cbc, + CMSCBCParam_get, + CMSCBCParam_set + }, + { + "aes-256-cbc", + oid_id_aes_256_cbc, + hx509_crypto_aes256_cbc, + EVP_aes_256_cbc, + CMSCBCParam_get, + CMSCBCParam_set + } +}; + +static const struct hx509cipher * +find_cipher_by_oid(const heim_oid *oid) +{ + int i; + + for (i = 0; i < sizeof(ciphers)/sizeof(ciphers[0]); i++) + if (der_heim_oid_cmp(oid, (*ciphers[i].oid_func)()) == 0) + return &ciphers[i]; + + return NULL; +} + +static const struct hx509cipher * +find_cipher_by_name(const char *name) +{ + int i; + + for (i = 0; i < sizeof(ciphers)/sizeof(ciphers[0]); i++) + if (strcasecmp(name, ciphers[i].name) == 0) + return &ciphers[i]; + + return NULL; +} + + +const heim_oid * +hx509_crypto_enctype_by_name(const char *name) +{ + const struct hx509cipher *cipher; + + cipher = find_cipher_by_name(name); + if (cipher == NULL) + return NULL; + return (*cipher->oid_func)(); +} + +int +hx509_crypto_init(hx509_context context, + const char *provider, + const heim_oid *enctype, + hx509_crypto *crypto) +{ + const struct hx509cipher *cipher; + + *crypto = NULL; + + cipher = find_cipher_by_oid(enctype); + if (cipher == NULL) { + hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP, + "Algorithm not supported"); + return HX509_ALG_NOT_SUPP; + } + + *crypto = calloc(1, sizeof(**crypto)); + if (*crypto == NULL) { + hx509_clear_error_string(context); + return ENOMEM; + } + + (*crypto)->cipher = cipher; + (*crypto)->c = (*cipher->evp_func)(); + + if (der_copy_oid(enctype, &(*crypto)->oid)) { + hx509_crypto_destroy(*crypto); + *crypto = NULL; + hx509_clear_error_string(context); + return ENOMEM; + } + + return 0; +} + +const char * +hx509_crypto_provider(hx509_crypto crypto) +{ + return "unknown"; +} + +void +hx509_crypto_destroy(hx509_crypto crypto) +{ + if (crypto->name) + free(crypto->name); + if (crypto->key.data) + free(crypto->key.data); + if (crypto->param) + free(crypto->param); + der_free_oid(&crypto->oid); + memset(crypto, 0, sizeof(*crypto)); + free(crypto); +} + +int +hx509_crypto_set_key_name(hx509_crypto crypto, const char *name) +{ + return 0; +} + +int +hx509_crypto_set_key_data(hx509_crypto crypto, const void *data, size_t length) +{ + if (EVP_CIPHER_key_length(crypto->c) > length) + return HX509_CRYPTO_INTERNAL_ERROR; + + if (crypto->key.data) { + free(crypto->key.data); + crypto->key.data = NULL; + crypto->key.length = 0; + } + crypto->key.data = malloc(length); + if (crypto->key.data == NULL) + return ENOMEM; + memcpy(crypto->key.data, data, length); + crypto->key.length = length; + + return 0; +} + +int +hx509_crypto_set_random_key(hx509_crypto crypto, heim_octet_string *key) +{ + if (crypto->key.data) { + free(crypto->key.data); + crypto->key.length = 0; + } + + crypto->key.length = EVP_CIPHER_key_length(crypto->c); + crypto->key.data = malloc(crypto->key.length); + if (crypto->key.data == NULL) { + crypto->key.length = 0; + return ENOMEM; + } + if (RAND_bytes(crypto->key.data, crypto->key.length) <= 0) { + free(crypto->key.data); + crypto->key.data = NULL; + crypto->key.length = 0; + return HX509_CRYPTO_INTERNAL_ERROR; + } + if (key) + return der_copy_octet_string(&crypto->key, key); + else + return 0; +} + +int +hx509_crypto_set_params(hx509_context context, + hx509_crypto crypto, + const heim_octet_string *param, + heim_octet_string *ivec) +{ + return (*crypto->cipher->set_params)(context, param, crypto, ivec); +} + +int +hx509_crypto_get_params(hx509_context context, + hx509_crypto crypto, + const heim_octet_string *ivec, + heim_octet_string *param) +{ + return (*crypto->cipher->get_params)(context, crypto, ivec, param); +} + +int +hx509_crypto_random_iv(hx509_crypto crypto, heim_octet_string *ivec) +{ + ivec->length = EVP_CIPHER_iv_length(crypto->c); + ivec->data = malloc(ivec->length); + if (ivec->data == NULL) { + ivec->length = 0; + return ENOMEM; + } + + if (RAND_bytes(ivec->data, ivec->length) <= 0) { + free(ivec->data); + ivec->data = NULL; + ivec->length = 0; + return HX509_CRYPTO_INTERNAL_ERROR; + } + return 0; +} + +int +hx509_crypto_encrypt(hx509_crypto crypto, + const void *data, + const size_t length, + const heim_octet_string *ivec, + heim_octet_string **ciphertext) +{ + EVP_CIPHER_CTX evp; + size_t padsize; + int ret; + + *ciphertext = NULL; + + assert(EVP_CIPHER_iv_length(crypto->c) == ivec->length); + + EVP_CIPHER_CTX_init(&evp); + + ret = EVP_CipherInit_ex(&evp, crypto->c, NULL, + crypto->key.data, ivec->data, 1); + if (ret != 1) { + EVP_CIPHER_CTX_cleanup(&evp); + ret = HX509_CRYPTO_INTERNAL_ERROR; + goto out; + } + + *ciphertext = calloc(1, sizeof(**ciphertext)); + if (*ciphertext == NULL) { + ret = ENOMEM; + goto out; + } + + if (EVP_CIPHER_block_size(crypto->c) == 1) { + padsize = 0; + } else { + int bsize = EVP_CIPHER_block_size(crypto->c); + padsize = bsize - (length % bsize); + } + (*ciphertext)->length = length + padsize; + (*ciphertext)->data = malloc(length + padsize); + if ((*ciphertext)->data == NULL) { + ret = ENOMEM; + goto out; + } + + memcpy((*ciphertext)->data, data, length); + if (padsize) { + int i; + unsigned char *p = (*ciphertext)->data; + p += length; + for (i = 0; i < padsize; i++) + *p++ = padsize; + } + + ret = EVP_Cipher(&evp, (*ciphertext)->data, + (*ciphertext)->data, + length + padsize); + if (ret != 1) { + ret = HX509_CRYPTO_INTERNAL_ERROR; + goto out; + } + ret = 0; + + out: + if (ret) { + if (*ciphertext) { + if ((*ciphertext)->data) { + free((*ciphertext)->data); + } + free(*ciphertext); + *ciphertext = NULL; + } + } + EVP_CIPHER_CTX_cleanup(&evp); + + return ret; +} + +int +hx509_crypto_decrypt(hx509_crypto crypto, + const void *data, + const size_t length, + heim_octet_string *ivec, + heim_octet_string *clear) +{ + EVP_CIPHER_CTX evp; + void *idata = NULL; + int ret; + + clear->data = NULL; + clear->length = 0; + + if (ivec && EVP_CIPHER_iv_length(crypto->c) < ivec->length) + return HX509_CRYPTO_INTERNAL_ERROR; + + if (crypto->key.data == NULL) + return HX509_CRYPTO_INTERNAL_ERROR; + + if (ivec) + idata = ivec->data; + + EVP_CIPHER_CTX_init(&evp); + + ret = EVP_CipherInit_ex(&evp, crypto->c, NULL, + crypto->key.data, idata, 0); + if (ret != 1) { + EVP_CIPHER_CTX_cleanup(&evp); + return HX509_CRYPTO_INTERNAL_ERROR; + } + + clear->length = length; + clear->data = malloc(length); + if (clear->data == NULL) { + EVP_CIPHER_CTX_cleanup(&evp); + clear->length = 0; + return ENOMEM; + } + + if (EVP_Cipher(&evp, clear->data, data, length) != 1) { + return HX509_CRYPTO_INTERNAL_ERROR; + } + EVP_CIPHER_CTX_cleanup(&evp); + + if (EVP_CIPHER_block_size(crypto->c) > 1) { + int padsize; + unsigned char *p; + int j, bsize = EVP_CIPHER_block_size(crypto->c); + + if (clear->length < bsize) { + ret = HX509_CMS_PADDING_ERROR; + goto out; + } + + p = clear->data; + p += clear->length - 1; + padsize = *p; + if (padsize > bsize) { + ret = HX509_CMS_PADDING_ERROR; + goto out; + } + clear->length -= padsize; + for (j = 0; j < padsize; j++) { + if (*p-- != padsize) { + ret = HX509_CMS_PADDING_ERROR; + goto out; + } + } + } + + return 0; + + out: + if (clear->data) + free(clear->data); + clear->data = NULL; + clear->length = 0; + return ret; +} + +typedef int (*PBE_string2key_func)(hx509_context, + const char *, + const heim_octet_string *, + hx509_crypto *, heim_octet_string *, + heim_octet_string *, + const heim_oid *, const EVP_MD *); + +static int +PBE_string2key(hx509_context context, + const char *password, + const heim_octet_string *parameters, + hx509_crypto *crypto, + heim_octet_string *key, heim_octet_string *iv, + const heim_oid *enc_oid, + const EVP_MD *md) +{ + PKCS12_PBEParams p12params; + int passwordlen; + hx509_crypto c; + int iter, saltlen, ret; + unsigned char *salt; + + passwordlen = password ? strlen(password) : 0; + + if (parameters == NULL) + return HX509_ALG_NOT_SUPP; + + ret = decode_PKCS12_PBEParams(parameters->data, + parameters->length, + &p12params, NULL); + if (ret) + goto out; + + if (p12params.iterations) + iter = *p12params.iterations; + else + iter = 1; + salt = p12params.salt.data; + saltlen = p12params.salt.length; + + if (!PKCS12_key_gen (password, passwordlen, salt, saltlen, + PKCS12_KEY_ID, iter, key->length, key->data, md)) { + ret = HX509_CRYPTO_INTERNAL_ERROR; + goto out; + } + + if (!PKCS12_key_gen (password, passwordlen, salt, saltlen, + PKCS12_IV_ID, iter, iv->length, iv->data, md)) { + ret = HX509_CRYPTO_INTERNAL_ERROR; + goto out; + } + + ret = hx509_crypto_init(context, NULL, enc_oid, &c); + if (ret) + goto out; + + ret = hx509_crypto_set_key_data(c, key->data, key->length); + if (ret) { + hx509_crypto_destroy(c); + goto out; + } + + *crypto = c; +out: + free_PKCS12_PBEParams(&p12params); + return ret; +} + +static const heim_oid * +find_string2key(const heim_oid *oid, + const EVP_CIPHER **c, + const EVP_MD **md, + PBE_string2key_func *s2k) +{ + if (der_heim_oid_cmp(oid, oid_id_pbewithSHAAnd40BitRC2_CBC()) == 0) { + *c = EVP_rc2_40_cbc(); + *md = EVP_sha1(); + *s2k = PBE_string2key; + return oid_private_rc2_40(); + } else if (der_heim_oid_cmp(oid, oid_id_pbeWithSHAAnd128BitRC2_CBC()) == 0) { + *c = EVP_rc2_cbc(); + *md = EVP_sha1(); + *s2k = PBE_string2key; + return oid_id_pkcs3_rc2_cbc(); +#if 0 + } else if (der_heim_oid_cmp(oid, oid_id_pbeWithSHAAnd40BitRC4()) == 0) { + *c = EVP_rc4_40(); + *md = EVP_sha1(); + *s2k = PBE_string2key; + return NULL; + } else if (der_heim_oid_cmp(oid, oid_id_pbeWithSHAAnd128BitRC4()) == 0) { + *c = EVP_rc4(); + *md = EVP_sha1(); + *s2k = PBE_string2key; + return oid_id_pkcs3_rc4(); +#endif + } else if (der_heim_oid_cmp(oid, oid_id_pbeWithSHAAnd3_KeyTripleDES_CBC()) == 0) { + *c = EVP_des_ede3_cbc(); + *md = EVP_sha1(); + *s2k = PBE_string2key; + return oid_id_pkcs3_des_ede3_cbc(); + } + + return NULL; +} + +/* + * + */ + +int +_hx509_pbe_encrypt(hx509_context context, + hx509_lock lock, + const AlgorithmIdentifier *ai, + const heim_octet_string *content, + heim_octet_string *econtent) +{ + hx509_clear_error_string(context); + return EINVAL; +} + +/* + * + */ + +int +_hx509_pbe_decrypt(hx509_context context, + hx509_lock lock, + const AlgorithmIdentifier *ai, + const heim_octet_string *econtent, + heim_octet_string *content) +{ + const struct _hx509_password *pw; + heim_octet_string key, iv; + const heim_oid *enc_oid; + const EVP_CIPHER *c; + const EVP_MD *md; + PBE_string2key_func s2k; + int i, ret = 0; + + memset(&key, 0, sizeof(key)); + memset(&iv, 0, sizeof(iv)); + + memset(content, 0, sizeof(*content)); + + enc_oid = find_string2key(&ai->algorithm, &c, &md, &s2k); + if (enc_oid == NULL) { + hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP, + "String to key algorithm not supported"); + ret = HX509_ALG_NOT_SUPP; + goto out; + } + + key.length = EVP_CIPHER_key_length(c); + key.data = malloc(key.length); + if (key.data == NULL) { + ret = ENOMEM; + hx509_clear_error_string(context); + goto out; + } + + iv.length = EVP_CIPHER_iv_length(c); + iv.data = malloc(iv.length); + if (iv.data == NULL) { + ret = ENOMEM; + hx509_clear_error_string(context); + goto out; + } + + pw = _hx509_lock_get_passwords(lock); + + ret = HX509_CRYPTO_INTERNAL_ERROR; + for (i = 0; i < pw->len + 1; i++) { + hx509_crypto crypto; + const char *password; + + if (i < pw->len) + password = pw->val[i]; + else if (i < pw->len + 1) + password = ""; + else + password = NULL; + + ret = (*s2k)(context, password, ai->parameters, &crypto, + &key, &iv, enc_oid, md); + if (ret) + goto out; + + ret = hx509_crypto_decrypt(crypto, + econtent->data, + econtent->length, + &iv, + content); + hx509_crypto_destroy(crypto); + if (ret == 0) + goto out; + + } +out: + if (key.data) + der_free_octet_string(&key); + if (iv.data) + der_free_octet_string(&iv); + return ret; +} + +/* + * + */ + + +int +_hx509_match_keys(hx509_cert c, hx509_private_key private_key) +{ + const Certificate *cert; + const SubjectPublicKeyInfo *spi; + RSAPublicKey pk; + RSA *rsa; + size_t size; + int ret; + + if (private_key->private_key.rsa == NULL) + return 0; + + rsa = private_key->private_key.rsa; + if (rsa->d == NULL || rsa->p == NULL || rsa->q == NULL) + return 0; + + cert = _hx509_get_cert(c); + spi = &cert->tbsCertificate.subjectPublicKeyInfo; + + rsa = RSA_new(); + if (rsa == NULL) + return 0; + + ret = decode_RSAPublicKey(spi->subjectPublicKey.data, + spi->subjectPublicKey.length / 8, + &pk, &size); + if (ret) { + RSA_free(rsa); + return 0; + } + rsa->n = heim_int2BN(&pk.modulus); + rsa->e = heim_int2BN(&pk.publicExponent); + + free_RSAPublicKey(&pk); + + rsa->d = BN_dup(private_key->private_key.rsa->d); + rsa->p = BN_dup(private_key->private_key.rsa->p); + rsa->q = BN_dup(private_key->private_key.rsa->q); + rsa->dmp1 = BN_dup(private_key->private_key.rsa->dmp1); + rsa->dmq1 = BN_dup(private_key->private_key.rsa->dmq1); + rsa->iqmp = BN_dup(private_key->private_key.rsa->iqmp); + + if (rsa->n == NULL || rsa->e == NULL || + rsa->d == NULL || rsa->p == NULL|| rsa->q == NULL || + rsa->dmp1 == NULL || rsa->dmq1 == NULL) { + RSA_free(rsa); + return 0; + } + + ret = RSA_check_key(rsa); + RSA_free(rsa); + + return ret == 1; +} + +static const heim_oid * +find_keytype(const hx509_private_key key) +{ + const struct signature_alg *md; + + if (key == NULL) + return NULL; + + md = find_sig_alg(key->signature_alg); + if (md == NULL) + return NULL; + return (*md->key_oid)(); +} + + +int +hx509_crypto_select(const hx509_context context, + int type, + const hx509_private_key source, + hx509_peer_info peer, + AlgorithmIdentifier *selected) +{ + const AlgorithmIdentifier *def; + size_t i, j; + int ret, bits; + + memset(selected, 0, sizeof(*selected)); + + if (type == HX509_SELECT_DIGEST) { + bits = SIG_DIGEST; + def = _hx509_crypto_default_digest_alg; + } else if (type == HX509_SELECT_PUBLIC_SIG) { + bits = SIG_PUBLIC_SIG; + /* XXX depend on `source´ and `peer´ */ + def = _hx509_crypto_default_sig_alg; + } else if (type == HX509_SELECT_SECRET_ENC) { + bits = SIG_SECRET; + def = _hx509_crypto_default_secret_alg; + } else { + hx509_set_error_string(context, 0, EINVAL, + "Unknown type %d of selection", type); + return EINVAL; + } + + if (peer) { + const heim_oid *keytype = NULL; + + keytype = find_keytype(source); + + for (i = 0; i < peer->len; i++) { + for (j = 0; sig_algs[j]; j++) { + if ((sig_algs[j]->flags & bits) != bits) + continue; + if (der_heim_oid_cmp((*sig_algs[j]->sig_oid)(), + &peer->val[i].algorithm) != 0) + continue; + if (keytype && sig_algs[j]->key_oid && + der_heim_oid_cmp(keytype, (*sig_algs[j]->key_oid)())) + continue; + + /* found one, use that */ + ret = copy_AlgorithmIdentifier(&peer->val[i], selected); + if (ret) + hx509_clear_error_string(context); + return ret; + } + if (bits & SIG_SECRET) { + const struct hx509cipher *cipher; + + cipher = find_cipher_by_oid(&peer->val[i].algorithm); + if (cipher == NULL) + continue; + if (cipher->ai_func == NULL) + continue; + ret = copy_AlgorithmIdentifier(cipher->ai_func(), selected); + if (ret) + hx509_clear_error_string(context); + return ret; + } + } + } + + /* use default */ + ret = copy_AlgorithmIdentifier(def, selected); + if (ret) + hx509_clear_error_string(context); + return ret; +} + +int +hx509_crypto_available(hx509_context context, + int type, + hx509_cert source, + AlgorithmIdentifier **val, + unsigned int *plen) +{ + const heim_oid *keytype = NULL; + unsigned int len, i; + void *ptr; + int bits, ret; + + *val = NULL; + + if (type == HX509_SELECT_ALL) { + bits = SIG_DIGEST | SIG_PUBLIC_SIG | SIG_SECRET; + } else if (type == HX509_SELECT_DIGEST) { + bits = SIG_DIGEST; + } else if (type == HX509_SELECT_PUBLIC_SIG) { + bits = SIG_PUBLIC_SIG; + } else { + hx509_set_error_string(context, 0, EINVAL, + "Unknown type %d of available", type); + return EINVAL; + } + + if (source) + keytype = find_keytype(_hx509_cert_private_key(source)); + + len = 0; + for (i = 0; sig_algs[i]; i++) { + if ((sig_algs[i]->flags & bits) == 0) + continue; + if (sig_algs[i]->sig_alg == NULL) + continue; + if (keytype && sig_algs[i]->key_oid && + der_heim_oid_cmp((*sig_algs[i]->key_oid)(), keytype)) + continue; + + /* found one, add that to the list */ + ptr = realloc(*val, sizeof(**val) * (len + 1)); + if (ptr == NULL) + goto out; + *val = ptr; + + ret = copy_AlgorithmIdentifier((*sig_algs[i]->sig_alg)(), &(*val)[len]); + if (ret) + goto out; + len++; + } + + /* Add AES */ + if (bits & SIG_SECRET) { + + for (i = 0; i < sizeof(ciphers)/sizeof(ciphers[0]); i++) { + + if (ciphers[i].ai_func == NULL) + continue; + + ptr = realloc(*val, sizeof(**val) * (len + 1)); + if (ptr == NULL) + goto out; + *val = ptr; + + ret = copy_AlgorithmIdentifier((ciphers[i].ai_func)(), &(*val)[len]); + if (ret) + goto out; + len++; + } + } + + *plen = len; + return 0; + +out: + for (i = 0; i < len; i++) + free_AlgorithmIdentifier(&(*val)[i]); + free(*val); + *val = NULL; + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; +} + +void +hx509_crypto_free_algs(AlgorithmIdentifier *val, + unsigned int len) +{ + unsigned int i; + for (i = 0; i < len; i++) + free_AlgorithmIdentifier(&val[i]); + free(val); +} diff --git a/source4/heimdal/lib/hx509/env.c b/source4/heimdal/lib/hx509/env.c new file mode 100644 index 0000000000..9d771c506f --- /dev/null +++ b/source4/heimdal/lib/hx509/env.c @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2007 - 2008 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hx_locl.h" +RCSID("$Id$"); + +/** + * @page page_env Hx509 enviroment functions + * + * See the library functions here: @ref hx509_env + */ + +/** + * 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) +{ + hx509_env n; + + n = malloc(sizeof(*n)); + if (n == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + + n->type = env_string; + n->next = NULL; + n->name = strdup(key); + if (n->name == NULL) { + free(n); + return ENOMEM; + } + n->u.string = strdup(value); + if (n->u.string == NULL) { + free(n->name); + free(n); + return ENOMEM; + } + + /* add to tail */ + if (*env) { + hx509_env e = *env; + while (e->next) + e = e->next; + e->next = n; + } else + *env = n; + + return 0; +} + +/** + * Add a new key/binding 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 list binding list to add + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_env + */ + +int +hx509_env_add_binding(hx509_context context, hx509_env *env, + const char *key, hx509_env list) +{ + hx509_env n; + + n = malloc(sizeof(*n)); + if (n == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + + n->type = env_list; + n->next = NULL; + n->name = strdup(key); + if (n->name == NULL) { + free(n); + return ENOMEM; + } + n->u.list = list; + + /* add to tail */ + if (*env) { + hx509_env e = *env; + while (e->next) + e = e->next; + e->next = n; + } else + *env = n; + + return 0; +} + + +/** + * Search the hx509_env for a length based 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) +{ + while(env) { + if (strncmp(key, env->name ,len) == 0 + && env->name[len] == '\0' && env->type == env_string) + return env->u.string; + env = env->next; + } + return NULL; +} + +/** + * 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. + * + * @return the value if the key is found, NULL otherwise. + * + * @ingroup hx509_env + */ + +const char * +hx509_env_find(hx509_context context, hx509_env env, const char *key) +{ + while(env) { + if (strcmp(key, env->name) == 0 && env->type == env_string) + return env->u.string; + env = env->next; + } + return NULL; +} + +/** + * Search the hx509_env for a binding. + * + * @param context A hx509 context. + * @param env enviroment to add the enviroment variable too. + * @param key key to search for. + * + * @return the binding if the key is found, NULL if not found. + * + * @ingroup hx509_env + */ + +hx509_env +hx509_env_find_binding(hx509_context context, + hx509_env env, + const char *key) +{ + while(env) { + if (strcmp(key, env->name) == 0 && env->type == env_list) + return env->u.list; + env = env->next; + } + return NULL; +} + +static void +env_free(hx509_env b) +{ + while(b) { + hx509_env next = b->next; + + if (b->type == env_string) + free(b->u.string); + else if (b->type == env_list) + env_free(b->u.list); + + free(b->name); + free(b); + b = next; + } +} + +/** + * Free an hx509_env enviroment context. + * + * @param env the enviroment to free. + * + * @ingroup hx509_env + */ + +void +hx509_env_free(hx509_env *env) +{ + if (*env) + env_free(*env); + *env = NULL; +} diff --git a/source4/heimdal/lib/hx509/error.c b/source4/heimdal/lib/hx509/error.c new file mode 100644 index 0000000000..9eeecb227c --- /dev/null +++ b/source4/heimdal/lib/hx509/error.c @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hx_locl.h" +RCSID("$Id$"); + +/** + * @page page_error Hx509 error reporting functions + * + * See the library functions here: @ref hx509_error + */ + +struct hx509_error_data { + hx509_error next; + int code; + char *msg; +}; + +static void +free_error_string(hx509_error msg) +{ + while(msg) { + hx509_error m2 = msg->next; + free(msg->msg); + free(msg); + msg = m2; + } +} + +/** + * Resets the error strings the hx509 context. + * + * @param context A hx509 context. + * + * @ingroup hx509_error + */ + +void +hx509_clear_error_string(hx509_context context) +{ + free_error_string(context->error); + 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) +{ + hx509_error msg; + + msg = calloc(1, sizeof(*msg)); + if (msg == NULL) { + hx509_clear_error_string(context); + return; + } + + if (vasprintf(&msg->msg, fmt, ap) == -1) { + hx509_clear_error_string(context); + free(msg); + return; + } + msg->code = code; + + if (flags & HX509_ERROR_APPEND) { + msg->next = context->error; + context->error = msg; + } else { + free_error_string(context->error); + context->error = msg; + } +} + +/** + * 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, ...) +{ + va_list ap; + + va_start(ap, fmt); + hx509_set_error_stringv(context, flags, code, fmt, ap); + 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) +{ + struct rk_strpool *p = NULL; + hx509_error msg = context->error; + + if (msg == NULL || msg->code != error_code) { + const char *cstr; + char *str; + + cstr = com_right(context->et_list, error_code); + if (cstr) + return strdup(cstr); + cstr = strerror(error_code); + if (cstr) + return strdup(cstr); + if (asprintf(&str, "<unknown error: %d>", error_code) == -1) + return NULL; + return str; + } + + for (msg = context->error; msg; msg = msg->next) + p = rk_strpoolprintf(p, "%s%s", msg->msg, + msg->next != NULL ? "; " : ""); + + 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, ...) +{ + va_list ap; + const char *msg; + char *str; + + va_start(ap, fmt); + vasprintf(&str, fmt, ap); + va_end(ap); + msg = hx509_get_error_string(context, error_code); + if (msg == NULL) + msg = "no error"; + + errx(exit_code, "%s: %s", str, msg); +} diff --git a/source4/heimdal/lib/hx509/file.c b/source4/heimdal/lib/hx509/file.c new file mode 100644 index 0000000000..c8f0e9a642 --- /dev/null +++ b/source4/heimdal/lib/hx509/file.c @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2005 - 2006 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hx_locl.h" +RCSID("$ID$"); + +int +_hx509_map_file_os(const char *fn, heim_octet_string *os) +{ + size_t length; + void *data; + int ret; + + ret = rk_undumpdata(fn, &data, &length); + + os->data = data; + os->length = length; + + return ret; +} + +void +_hx509_unmap_file_os(heim_octet_string *os) +{ + rk_xfree(os->data); +} + +int +_hx509_write_file(const char *fn, const void *data, size_t length) +{ + rk_dumpdata(fn, data, length); + return 0; +} + +/* + * + */ + +static void +header(FILE *f, const char *type, const char *str) +{ + fprintf(f, "-----%s %s-----\n", type, str); +} + +int +hx509_pem_write(hx509_context context, const char *type, + hx509_pem_header *headers, FILE *f, + const void *data, size_t size) +{ + const char *p = data; + size_t length; + char *line; + +#define ENCODE_LINE_LENGTH 54 + + header(f, "BEGIN", type); + + while (headers) { + fprintf(f, "%s: %s\n%s", + headers->header, headers->value, + headers->next ? "" : "\n"); + headers = headers->next; + } + + while (size > 0) { + ssize_t l; + + length = size; + if (length > ENCODE_LINE_LENGTH) + length = ENCODE_LINE_LENGTH; + + l = base64_encode(p, length, &line); + if (l < 0) { + hx509_set_error_string(context, 0, ENOMEM, + "malloc - out of memory"); + return ENOMEM; + } + size -= length; + fprintf(f, "%s\n", line); + p += length; + free(line); + } + + header(f, "END", type); + + return 0; +} + +/* + * + */ + +int +hx509_pem_add_header(hx509_pem_header **headers, + const char *header, const char *value) +{ + hx509_pem_header *h; + + h = calloc(1, sizeof(*h)); + if (h == NULL) + return ENOMEM; + h->header = strdup(header); + if (h->header == NULL) { + free(h); + return ENOMEM; + } + h->value = strdup(value); + if (h->value == NULL) { + free(h->header); + free(h); + return ENOMEM; + } + + h->next = *headers; + *headers = h; + + return 0; +} + +void +hx509_pem_free_header(hx509_pem_header *headers) +{ + hx509_pem_header *h; + while (headers) { + h = headers; + headers = headers->next; + free(h->header); + free(h->value); + free(h); + } +} + +/* + * + */ + +const char * +hx509_pem_find_header(const hx509_pem_header *h, const char *header) +{ + while(h) { + if (strcmp(header, h->header) == 0) + return h->value; + h = h->next; + } + return NULL; +} + + +/* + * + */ + +int +hx509_pem_read(hx509_context context, + FILE *f, + hx509_pem_read_func func, + void *ctx) +{ + hx509_pem_header *headers = NULL; + char *type = NULL; + void *data = NULL; + size_t len = 0; + char buf[1024]; + int ret = HX509_PARSING_KEY_FAILED; + + enum { BEFORE, SEARCHHEADER, INHEADER, INDATA, DONE } where; + + where = BEFORE; + + while (fgets(buf, sizeof(buf), f) != NULL) { + char *p; + int i; + + i = strcspn(buf, "\n"); + if (buf[i] == '\n') { + buf[i] = '\0'; + if (i > 0) + i--; + } + if (buf[i] == '\r') { + buf[i] = '\0'; + if (i > 0) + i--; + } + + switch (where) { + case BEFORE: + if (strncmp("-----BEGIN ", buf, 11) == 0) { + type = strdup(buf + 11); + if (type == NULL) + break; + p = strchr(type, '-'); + if (p) + *p = '\0'; + where = SEARCHHEADER; + } + break; + case SEARCHHEADER: + p = strchr(buf, ':'); + if (p == NULL) { + where = INDATA; + goto indata; + } + /* FALLTHOUGH */ + case INHEADER: + if (buf[0] == '\0') { + where = INDATA; + break; + } + p = strchr(buf, ':'); + if (p) { + *p++ = '\0'; + while (isspace((int)*p)) + p++; + ret = hx509_pem_add_header(&headers, buf, p); + if (ret) + abort(); + } + break; + case INDATA: + indata: + + if (strncmp("-----END ", buf, 9) == 0) { + where = DONE; + break; + } + + p = emalloc(i); + i = base64_decode(buf, p); + if (i < 0) { + free(p); + goto out; + } + + data = erealloc(data, len + i); + memcpy(((char *)data) + len, p, i); + free(p); + len += i; + break; + case DONE: + abort(); + } + + if (where == DONE) { + ret = (*func)(context, type, headers, data, len, ctx); + out: + free(data); + data = NULL; + len = 0; + free(type); + type = NULL; + where = BEFORE; + hx509_pem_free_header(headers); + headers = NULL; + if (ret) + break; + } + } + + if (where != BEFORE) { + hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, + "File ends before end of PEM end tag"); + ret = HX509_PARSING_KEY_FAILED; + } + if (data) + free(data); + if (type) + free(type); + if (headers) + hx509_pem_free_header(headers); + + return ret; +} diff --git a/source4/heimdal/lib/hx509/hx509.h b/source4/heimdal/lib/hx509/hx509.h new file mode 100644 index 0000000000..289f8d04a7 --- /dev/null +++ b/source4/heimdal/lib/hx509/hx509.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* $Id$ */ + +#ifndef HEIMDAL_HX509_H +#define HEIMDAL_HX509_H 1 + +#include <heim_asn1.h> +#include <rfc2459_asn1.h> + +typedef struct hx509_cert_attribute_data *hx509_cert_attribute; +typedef struct hx509_cert_data *hx509_cert; +typedef struct hx509_certs_data *hx509_certs; +typedef struct hx509_context_data *hx509_context; +typedef struct hx509_crypto_data *hx509_crypto; +typedef struct hx509_lock_data *hx509_lock; +typedef struct hx509_name_data *hx509_name; +typedef struct hx509_private_key *hx509_private_key; +typedef struct hx509_validate_ctx_data *hx509_validate_ctx; +typedef struct hx509_verify_ctx_data *hx509_verify_ctx; +typedef struct hx509_revoke_ctx_data *hx509_revoke_ctx; +typedef struct hx509_query_data hx509_query; +typedef void * hx509_cursor; +typedef struct hx509_request_data *hx509_request; +typedef struct hx509_error_data *hx509_error; +typedef struct hx509_peer_info *hx509_peer_info; +typedef struct hx509_ca_tbs *hx509_ca_tbs; +typedef struct hx509_env_data *hx509_env; +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 +}; + +struct hx509_cert_attribute_data { + heim_oid oid; + heim_octet_string data; +}; + +typedef enum { + HX509_PROMPT_TYPE_PASSWORD = 0x1, /* password, hidden */ + HX509_PROMPT_TYPE_QUESTION = 0x2, /* question, not hidden */ + HX509_PROMPT_TYPE_INFO = 0x4 /* infomation, reply doesn't matter */ +} hx509_prompt_type; + +typedef struct hx509_prompt { + const char *prompt; + hx509_prompt_type type; + heim_octet_string reply; +} hx509_prompt; + +typedef int (*hx509_prompter_fct)(void *, const hx509_prompt *); + +typedef struct hx509_octet_string_list { + size_t len; + heim_octet_string *val; +} hx509_octet_string_list; + +typedef struct hx509_pem_header { + struct hx509_pem_header *next; + char *header; + char *value; +} hx509_pem_header; + +typedef int +(*hx509_pem_read_func)(hx509_context, const char *, const hx509_pem_header *, + const void *, size_t, void *ctx); + +/* + * Options passed to hx509_query_match_option. + */ +typedef enum { + HX509_QUERY_OPTION_PRIVATE_KEY = 1, + HX509_QUERY_OPTION_KU_ENCIPHERMENT = 2, + HX509_QUERY_OPTION_KU_DIGITALSIGNATURE = 3, + HX509_QUERY_OPTION_KU_KEYCERTSIGN = 4, + HX509_QUERY_OPTION_END = 0xffff +} hx509_query_option; + +/* 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 + +/* flags to hx509_cms_unenvelope */ +#define HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT 0x01 + +/* selectors passed to hx509_crypto_select and hx509_crypto_available */ +#define HX509_SELECT_ALL 0 +#define HX509_SELECT_DIGEST 1 +#define HX509_SELECT_PUBLIC_SIG 2 +#define HX509_SELECT_PUBLIC_ENC 3 +#define HX509_SELECT_SECRET_ENC 4 + +/* flags to hx509_ca_tbs_set_template */ +#define HX509_CA_TEMPLATE_SUBJECT 1 +#define HX509_CA_TEMPLATE_SERIAL 2 +#define HX509_CA_TEMPLATE_NOTBEFORE 4 +#define HX509_CA_TEMPLATE_NOTAFTER 8 +#define HX509_CA_TEMPLATE_SPKI 16 +#define HX509_CA_TEMPLATE_KU 32 +#define HX509_CA_TEMPLATE_EKU 64 + +/* flags hx509_cms_create_signed* */ +#define HX509_CMS_SIGATURE_DETACHED 1 +#define HX509_CMS_SIGATURE_ID_NAME 2 + +/* hx509_verify_hostname nametype */ +typedef enum { + HX509_HN_HOSTNAME = 0, + HX509_HN_DNSSRV +} hx509_hostname_type; + +#include <hx509-protos.h> +#include <hx509_err.h> + +#endif /* HEIMDAL_HX509_H */ diff --git a/source4/heimdal/lib/hx509/hx509_err.et b/source4/heimdal/lib/hx509/hx509_err.et new file mode 100644 index 0000000000..c1dfaf587e --- /dev/null +++ b/source4/heimdal/lib/hx509/hx509_err.et @@ -0,0 +1,101 @@ +# +# Error messages for the hx509 library +# +# This might look like a com_err file, but is not +# +id "$Id$" + +error_table hx +prefix HX509 + +# path validateion and construction related errors +error_code BAD_TIMEFORMAT, "ASN.1 failed call to system time library" +error_code EXTENSION_NOT_FOUND, "Extension not found" +error_code NO_PATH, "Certification path not found" +error_code PARENT_NOT_CA, "Parent certificate is not a CA" +error_code CA_PATH_TOO_DEEP, "CA path too deep" +error_code SIG_ALG_NO_SUPPORTED, "Signature algorithm not supported" +error_code SIG_ALG_DONT_MATCH_KEY_ALG, "Signature algorithm doesn't match certificate key" +error_code CERT_USED_BEFORE_TIME, "Certificate used before it became valid" +error_code CERT_USED_AFTER_TIME, "Certificate used after it became invalid" +error_code PRIVATE_KEY_MISSING, "Private key required for the operation is missing" +error_code ALG_NOT_SUPP, "Algorithm not supported" +error_code ISSUER_NOT_FOUND, "Issuer couldn't be found" +error_code VERIFY_CONSTRAINTS, "Error verifing constraints" +error_code RANGE, "Number too large" +error_code NAME_CONSTRAINT_ERROR, "Error while verifing name constraints" +error_code PATH_TOO_LONG, "Path is too long, failed to find valid anchor" +error_code KU_CERT_MISSING, "Required keyusage for this certificate is missing" +error_code CERT_NOT_FOUND, "Certificate not found" +error_code UNKNOWN_LOCK_COMMAND, "Unknown lock command" +error_code PARENT_IS_CA, "Parent certificate is a CA" +error_code EXTRA_DATA_AFTER_STRUCTURE, "Extra data was found after the structure" +error_code PROXY_CERT_INVALID, "Proxy certificate is invalid" +error_code PROXY_CERT_NAME_WRONG, "Proxy certificate name is wrong" +error_code NAME_MALFORMED, "Name is malformated" +error_code CERTIFICATE_MALFORMED, "Certificate is malformated" +error_code CERTIFICATE_MISSING_EKU, "Certificate is missing a required EKU" +error_code PROXY_CERTIFICATE_NOT_CANONICALIZED, "Proxy certificate not canonicalize" + +# cms related errors +index 32 +prefix HX509_CMS +error_code FAILED_CREATE_SIGATURE, "Failed to create signature" +error_code MISSING_SIGNER_DATA, "Missing signer data" +error_code SIGNER_NOT_FOUND, "Couldn't find signers certificate" +error_code NO_DATA_AVAILABLE, "No data to perform the operation on" +error_code INVALID_DATA, "Data in the message is invalid" +error_code PADDING_ERROR, "Padding in the message invalid" +error_code NO_RECIPIENT_CERTIFICATE, "Couldn't find recipient certificate" +error_code DATA_OID_MISMATCH, "Mismatch bewteen signed type and unsigned type" + +# crypto related errors +index 64 +prefix HX509_CRYPTO +error_code INTERNAL_ERROR, "Internal error in the crypto engine" +error_code EXTERNAL_ERROR, "External error in the crypto engine" +error_code SIGNATURE_MISSING, "Signature missing for data" +error_code BAD_SIGNATURE, "Signature is not valid" +error_code SIG_NO_CONF, "Sigature doesn't provide confidentiality" +error_code SIG_INVALID_FORMAT, "Invalid format on signature" +error_code OID_MISMATCH, "Mismatch bewteen oids" +error_code NO_PROMPTER, "No prompter function defined" +error_code SIGNATURE_WITHOUT_SIGNER, "Signature require signer, but non available" +error_code RSA_PUBLIC_ENCRYPT, "RSA public encyption failed" +error_code RSA_PRIVATE_ENCRYPT, "RSA public encyption failed" +error_code RSA_PUBLIC_DECRYPT, "RSA private decryption failed" +error_code RSA_PRIVATE_DECRYPT, "RSA private decryption failed" + +# revoke related errors +index 96 +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 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" +error_code REVOKE_NOT_SAME_PARENT, "Doesn't have same parent as other certificates" +error_code CERT_NOT_IN_OCSP, "Certificates not in OCSP reply" + +# misc error +index 108 +error_code LOCAL_ATTRIBUTE_MISSING, "No local key attribute" +error_code PARSING_KEY_FAILED, "Failed to parse key" +error_code UNSUPPORTED_OPERATION, "Unsupported operation" +error_code UNIMPLEMENTED_OPERATION, "Unimplemented operation" +error_code PARSING_NAME_FAILED, "Failed to parse name" + +# keystore related error +index 128 +prefix HX509_PKCS11 +error_code NO_SLOT, "No smartcard reader/device found" +error_code NO_TOKEN, "No smartcard in reader" +error_code NO_MECH, "No supported mech(s)" +error_code TOKEN_CONFUSED, "Token or slot failed in inconsistent way" +error_code OPEN_SESSION, "Failed to open session to slot" +error_code LOGIN, "Failed to login to slot" +error_code LOAD, "Failed to load PKCS module" + +end diff --git a/source4/heimdal/lib/hx509/hx_locl.h b/source4/heimdal/lib/hx509/hx_locl.h new file mode 100644 index 0000000000..4cf7a54e13 --- /dev/null +++ b/source4/heimdal/lib/hx509/hx_locl.h @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2004 - 2006 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> +#include <strings.h> +#include <assert.h> +#include <stdarg.h> +#include <err.h> +#include <limits.h> + +#include <getarg.h> +#include <base64.h> +#include <hex.h> +#include <roken.h> +#include <com_err.h> +#include <parse_units.h> +#include <parse_bytes.h> + +#include <krb5-types.h> + +#include <rfc2459_asn1.h> +#include <cms_asn1.h> +#include <pkcs8_asn1.h> +#include <pkcs9_asn1.h> +#include <pkcs12_asn1.h> +#include <ocsp_asn1.h> +#include <pkcs10_asn1.h> +#include <asn1_err.h> +#include <pkinit_asn1.h> + +#include <der.h> + +#define HC_DEPRECATED_CRYPTO +#include "crypto-headers.h" + +struct hx509_keyset_ops; +struct hx509_collector; +struct hx509_generate_private_context; +typedef struct hx509_path hx509_path; + +#include <hx509.h> + +typedef void (*_hx509_cert_release_func)(struct hx509_cert_data *, void *); + +typedef struct hx509_private_key_ops hx509_private_key_ops; + +#include "sel.h" + +#include <hx509-private.h> +#include <hx509_err.h> + +struct hx509_peer_info { + hx509_cert cert; + AlgorithmIdentifier *val; + size_t len; +}; + +#define HX509_CERTS_FIND_SERIALNUMBER 1 +#define HX509_CERTS_FIND_ISSUER 2 +#define HX509_CERTS_FIND_SUBJECT 4 +#define HX509_CERTS_FIND_ISSUER_KEY_ID 8 +#define HX509_CERTS_FIND_SUBJECT_KEY_ID 16 + +struct hx509_name_data { + Name der_name; +}; + +struct hx509_path { + size_t len; + hx509_cert *val; +}; + +struct hx509_query_data { + int match; +#define HX509_QUERY_FIND_ISSUER_CERT 0x000001 +#define HX509_QUERY_MATCH_SERIALNUMBER 0x000002 +#define HX509_QUERY_MATCH_ISSUER_NAME 0x000004 +#define HX509_QUERY_MATCH_SUBJECT_NAME 0x000008 +#define HX509_QUERY_MATCH_SUBJECT_KEY_ID 0x000010 +#define HX509_QUERY_MATCH_ISSUER_ID 0x000020 +#define HX509_QUERY_PRIVATE_KEY 0x000040 +#define HX509_QUERY_KU_ENCIPHERMENT 0x000080 +#define HX509_QUERY_KU_DIGITALSIGNATURE 0x000100 +#define HX509_QUERY_KU_KEYCERTSIGN 0x000200 +#define HX509_QUERY_KU_CRLSIGN 0x000400 +#define HX509_QUERY_KU_NONREPUDIATION 0x000800 +#define HX509_QUERY_KU_KEYAGREEMENT 0x001000 +#define HX509_QUERY_KU_DATAENCIPHERMENT 0x002000 +#define HX509_QUERY_ANCHOR 0x004000 +#define HX509_QUERY_MATCH_CERTIFICATE 0x008000 +#define HX509_QUERY_MATCH_LOCAL_KEY_ID 0x010000 +#define HX509_QUERY_NO_MATCH_PATH 0x020000 +#define HX509_QUERY_MATCH_FRIENDLY_NAME 0x040000 +#define HX509_QUERY_MATCH_FUNCTION 0x080000 +#define HX509_QUERY_MATCH_KEY_HASH_SHA1 0x100000 +#define HX509_QUERY_MATCH_TIME 0x200000 +#define HX509_QUERY_MATCH_EKU 0x400000 +#define HX509_QUERY_MATCH_EXPR 0x800000 +#define HX509_QUERY_MASK 0xffffff + Certificate *subject; + Certificate *certificate; + heim_integer *serial; + heim_octet_string *subject_id; + heim_octet_string *local_key_id; + Name *issuer_name; + Name *subject_name; + hx509_path *path; + char *friendlyname; + int (*cmp_func)(void *, hx509_cert); + void *cmp_func_ctx; + heim_octet_string *keyhash_sha1; + time_t timenow; + heim_oid *eku; + struct hx_expr *expr; +}; + +struct hx509_keyset_ops { + const char *name; + int flags; + int (*init)(hx509_context, hx509_certs, void **, + int, const char *, hx509_lock); + int (*store)(hx509_context, hx509_certs, void *, int, hx509_lock); + int (*free)(hx509_certs, void *); + int (*add)(hx509_context, hx509_certs, void *, hx509_cert); + int (*query)(hx509_context, hx509_certs, void *, + const hx509_query *, hx509_cert *); + int (*iter_start)(hx509_context, hx509_certs, void *, void **); + int (*iter)(hx509_context, hx509_certs, void *, void *, hx509_cert *); + int (*iter_end)(hx509_context, hx509_certs, void *, void *); + int (*printinfo)(hx509_context, hx509_certs, + void *, int (*)(void *, const char *), void *); + int (*getkeys)(hx509_context, hx509_certs, void *, hx509_private_key **); + int (*addkey)(hx509_context, hx509_certs, void *, hx509_private_key); +}; + +struct _hx509_password { + size_t len; + char **val; +}; + +extern hx509_lock _hx509_empty_lock; + +struct hx509_context_data { + struct hx509_keyset_ops **ks_ops; + int ks_num_ops; + int flags; +#define HX509_CTX_VERIFY_MISSING_OK 1 + int ocsp_time_diff; +#define HX509_DEFAULT_OCSP_TIME_DIFF (5*60) + hx509_error error; + struct et_list *et_list; + char *querystat; + hx509_certs default_trust_anchors; +}; + +/* _hx509_calculate_path flag field */ +#define HX509_CALCULATE_PATH_NO_ANCHOR 1 + +/* environment */ +struct hx509_env_data { + enum { env_string, env_list } type; + char *name; + struct hx509_env_data *next; + union { + char *string; + struct hx509_env_data *list; + } u; +}; + + +extern const AlgorithmIdentifier * _hx509_crypto_default_sig_alg; +extern const AlgorithmIdentifier * _hx509_crypto_default_digest_alg; +extern const AlgorithmIdentifier * _hx509_crypto_default_secret_alg; + +/* + * Configurable options + */ + +#ifdef __APPLE__ +#define HX509_DEFAULT_ANCHORS "KEYCHAIN:system-anchors" +#endif diff --git a/source4/heimdal/lib/hx509/keyset.c b/source4/heimdal/lib/hx509/keyset.c new file mode 100644 index 0000000000..bb36221aff --- /dev/null +++ b/source4/heimdal/lib/hx509/keyset.c @@ -0,0 +1,677 @@ +/* + * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hx_locl.h" +RCSID("$Id$"); + +/** + * @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 { + unsigned int ref; + struct hx509_keyset_ops *ops; + void *ops_data; +}; + +static struct hx509_keyset_ops * +_hx509_ks_type(hx509_context context, const char *type) +{ + int i; + + for (i = 0; i < context->ks_num_ops; i++) + if (strcasecmp(type, context->ks_ops[i]->name) == 0) + return context->ks_ops[i]; + + return NULL; +} + +void +_hx509_ks_register(hx509_context context, struct hx509_keyset_ops *ops) +{ + struct hx509_keyset_ops **val; + + if (_hx509_ks_type(context, ops->name)) + return; + + val = realloc(context->ks_ops, + (context->ks_num_ops + 1) * sizeof(context->ks_ops[0])); + if (val == NULL) + return; + val[context->ks_num_ops] = ops; + context->ks_ops = val; + 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, + hx509_lock lock, hx509_certs *certs) +{ + struct hx509_keyset_ops *ops; + const char *residue; + hx509_certs c; + char *type; + int ret; + + *certs = NULL; + + residue = strchr(name, ':'); + if (residue) { + type = malloc(residue - name + 1); + if (type) + strlcpy(type, name, residue - name + 1); + residue++; + if (residue[0] == '\0') + residue = NULL; + } else { + type = strdup("MEMORY"); + residue = name; + } + if (type == NULL) { + hx509_clear_error_string(context); + return ENOMEM; + } + + ops = _hx509_ks_type(context, type); + if (ops == NULL) { + hx509_set_error_string(context, 0, ENOENT, + "Keyset type %s is not supported", type); + free(type); + return ENOENT; + } + free(type); + c = calloc(1, sizeof(*c)); + if (c == NULL) { + hx509_clear_error_string(context); + return ENOMEM; + } + c->ops = ops; + c->ref = 1; + + ret = (*ops->init)(context, c, &c->ops_data, flags, residue, lock); + if (ret) { + free(c); + return ret; + } + + *certs = c; + 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, + int flags, + hx509_lock lock) +{ + if (certs->ops->store == NULL) { + hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION, + "keystore if type %s doesn't support " + "store operation", + certs->ops->name); + return HX509_UNSUPPORTED_OPERATION; + } + + return (*certs->ops->store)(context, certs, certs->ops_data, flags, lock); +} + + +hx509_certs +_hx509_certs_ref(hx509_certs certs) +{ + if (certs == NULL) + return NULL; + if (certs->ref == 0) + _hx509_abort("certs refcount == 0 on ref"); + if (certs->ref == UINT_MAX) + _hx509_abort("certs refcount == UINT_MAX on ref"); + certs->ref++; + return certs; +} + +/** + * Free a certificate store. + * + * @param certs certificate store to free. + * + * @ingroup hx509_keyset + */ + +void +hx509_certs_free(hx509_certs *certs) +{ + if (*certs) { + if ((*certs)->ref == 0) + _hx509_abort("cert refcount == 0 on free"); + if (--(*certs)->ref > 0) + return; + + (*(*certs)->ops->free)(*certs, (*certs)->ops_data); + free(*certs); + *certs = NULL; + } +} + +/** + * 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, + hx509_cursor *cursor) +{ + int ret; + + if (certs->ops->iter_start == NULL) { + hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION, + "Keyset type %s doesn't support iteration", + certs->ops->name); + return HX509_UNSUPPORTED_OPERATION; + } + + ret = (*certs->ops->iter_start)(context, certs, certs->ops_data, cursor); + if (ret) + return ret; + + 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, + hx509_cursor cursor, + hx509_cert *cert) +{ + *cert = NULL; + 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, + hx509_cursor cursor) +{ + (*certs->ops->iter_end)(context, certs, certs->ops_data, cursor); + 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 (*func)(hx509_context, void *, hx509_cert), + void *ctx) +{ + hx509_cursor cursor; + hx509_cert c; + int ret; + + ret = hx509_certs_start_seq(context, certs, &cursor); + if (ret) + return ret; + + while (1) { + ret = hx509_certs_next_cert(context, certs, cursor, &c); + if (ret) + break; + if (c == NULL) { + ret = 0; + break; + } + ret = (*func)(context, ctx, c); + hx509_cert_free(c); + if (ret) + break; + } + + hx509_certs_end_seq(context, certs, cursor); + + 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) +{ + Certificate *cert; + hx509_name n; + char *s, *i; + + cert = _hx509_get_cert(c); + + _hx509_name_from_Name(&cert->tbsCertificate.subject, &n); + hx509_name_to_string(n, &s); + hx509_name_free(&n); + _hx509_name_from_Name(&cert->tbsCertificate.issuer, &n); + hx509_name_to_string(n, &i); + hx509_name_free(&n); + fprintf(ctx, "subject: %s\nissuer: %s\n", s, i); + free(s); + free(i); + return 0; +} + +/** + * 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 +hx509_certs_add(hx509_context context, hx509_certs certs, hx509_cert cert) +{ + if (certs->ops->add == NULL) { + hx509_set_error_string(context, 0, ENOENT, + "Keyset type %s doesn't support add operation", + certs->ops->name); + return ENOENT; + } + + 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, + const hx509_query *q, + hx509_cert *r) +{ + hx509_cursor cursor; + hx509_cert c; + int ret; + + *r = NULL; + + _hx509_query_statistic(context, 0, q); + + if (certs->ops->query) + return (*certs->ops->query)(context, certs, certs->ops_data, q, r); + + ret = hx509_certs_start_seq(context, certs, &cursor); + if (ret) + return ret; + + c = NULL; + while (1) { + ret = hx509_certs_next_cert(context, certs, cursor, &c); + if (ret) + break; + if (c == NULL) + break; + if (_hx509_query_match_cert(context, q, c)) { + *r = c; + break; + } + hx509_cert_free(c); + } + + hx509_certs_end_seq(context, certs, cursor); + if (ret) + return ret; + if (c == NULL) { + hx509_clear_error_string(context); + return HX509_CERT_NOT_FOUND; + } + + return 0; +} + +static int +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) +{ + if (from == NULL) + return 0; + 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, + hx509_lock lock, + const char *name) +{ + hx509_certs s; + int ret; + + ret = hx509_certs_init(context, name, 0, lock, &s); + if (ret) + return ret; + ret = hx509_certs_merge(context, to, s); + hx509_certs_free(&s); + 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) +{ + hx509_cursor cursor; + int ret; + + *c = NULL; + + ret = hx509_certs_start_seq(context, certs, &cursor); + if (ret) + return ret; + + ret = hx509_certs_next_cert(context, certs, cursor, c); + if (ret) + return ret; + + hx509_certs_end_seq(context, certs, cursor); + return 0; +} + +static int +certs_info_stdio(void *ctx, const char *str) +{ + FILE *f = ctx; + fprintf(f, "%s\n", 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, + int (*func)(void *, const char *), + void *ctx) +{ + if (func == NULL) { + func = certs_info_stdio; + if (ctx == NULL) + ctx = stdout; + } + if (certs->ops->printinfo == NULL) { + (*func)(ctx, "No info function for certs"); + return 0; + } + return (*certs->ops->printinfo)(context, certs, certs->ops_data, + func, ctx); +} + +void +_hx509_pi_printf(int (*func)(void *, const char *), void *ctx, + const char *fmt, ...) +{ + va_list ap; + char *str; + + va_start(ap, fmt); + vasprintf(&str, fmt, ap); + va_end(ap); + if (str == NULL) + return; + (*func)(ctx, str); + free(str); +} + +int +_hx509_certs_keys_get(hx509_context context, + hx509_certs certs, + hx509_private_key **keys) +{ + if (certs->ops->getkeys == NULL) { + *keys = NULL; + return 0; + } + return (*certs->ops->getkeys)(context, certs, certs->ops_data, keys); +} + +int +_hx509_certs_keys_add(hx509_context context, + hx509_certs certs, + hx509_private_key key) +{ + if (certs->ops->addkey == NULL) { + hx509_set_error_string(context, 0, EINVAL, + "keystore if type %s doesn't support " + "key add operation", + certs->ops->name); + return EINVAL; + } + return (*certs->ops->addkey)(context, certs, certs->ops_data, key); +} + + +void +_hx509_certs_keys_free(hx509_context context, + hx509_private_key *keys) +{ + int i; + for (i = 0; keys[i]; i++) + _hx509_private_key_free(&keys[i]); + free(keys); +} diff --git a/source4/heimdal/lib/hx509/ks_dir.c b/source4/heimdal/lib/hx509/ks_dir.c new file mode 100644 index 0000000000..17a3ae4745 --- /dev/null +++ b/source4/heimdal/lib/hx509/ks_dir.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2006 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hx_locl.h" +RCSID("$Id$"); +#include <dirent.h> + +/* + * The DIR keyset module is strange compared to the other modules + * since it does lazy evaluation and really doesn't keep any local + * state except for the directory iteration and cert iteration of + * files. DIR ignores most errors so that the consumer doesn't get + * failes for stray files in directories. + */ + +struct dircursor { + DIR *dir; + hx509_certs certs; + void *iter; +}; + +/* + * + */ + +static int +dir_init(hx509_context context, + hx509_certs certs, void **data, int flags, + const char *residue, hx509_lock lock) +{ + *data = NULL; + + { + struct stat sb; + int ret; + + ret = stat(residue, &sb); + if (ret == -1) { + hx509_set_error_string(context, 0, ENOENT, + "No such file %s", residue); + return ENOENT; + } + + if ((sb.st_mode & S_IFDIR) == 0) { + hx509_set_error_string(context, 0, ENOTDIR, + "%s is not a directory", residue); + return ENOTDIR; + } + } + + *data = strdup(residue); + if (*data == NULL) { + hx509_clear_error_string(context); + return ENOMEM; + } + + return 0; +} + +static int +dir_free(hx509_certs certs, void *data) +{ + free(data); + return 0; +} + + + +static int +dir_iter_start(hx509_context context, + hx509_certs certs, void *data, void **cursor) +{ + struct dircursor *d; + + *cursor = NULL; + + d = calloc(1, sizeof(*d)); + if (d == NULL) { + hx509_clear_error_string(context); + return ENOMEM; + } + + d->dir = opendir(data); + if (d->dir == NULL) { + hx509_clear_error_string(context); + free(d); + return errno; + } + rk_cloexec(dirfd(d->dir)); + d->certs = NULL; + d->iter = NULL; + + *cursor = d; + return 0; +} + +static int +dir_iter(hx509_context context, + hx509_certs certs, void *data, void *iter, hx509_cert *cert) +{ + struct dircursor *d = iter; + int ret = 0; + + *cert = NULL; + + do { + struct dirent *dir; + char *fn; + + if (d->certs) { + ret = hx509_certs_next_cert(context, d->certs, d->iter, cert); + if (ret) { + hx509_certs_end_seq(context, d->certs, d->iter); + d->iter = NULL; + hx509_certs_free(&d->certs); + return ret; + } + if (*cert) { + ret = 0; + break; + } + hx509_certs_end_seq(context, d->certs, d->iter); + d->iter = NULL; + hx509_certs_free(&d->certs); + } + + dir = readdir(d->dir); + if (dir == NULL) { + ret = 0; + break; + } + if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) + continue; + + if (asprintf(&fn, "FILE:%s/%s", (char *)data, dir->d_name) == -1) + return ENOMEM; + + ret = hx509_certs_init(context, fn, 0, NULL, &d->certs); + if (ret == 0) { + + ret = hx509_certs_start_seq(context, d->certs, &d->iter); + if (ret) + hx509_certs_free(&d->certs); + } + /* ignore errors */ + if (ret) { + d->certs = NULL; + ret = 0; + } + + free(fn); + } while(ret == 0); + + return ret; +} + + +static int +dir_iter_end(hx509_context context, + hx509_certs certs, + void *data, + void *cursor) +{ + struct dircursor *d = cursor; + + if (d->certs) { + hx509_certs_end_seq(context, d->certs, d->iter); + d->iter = NULL; + hx509_certs_free(&d->certs); + } + closedir(d->dir); + free(d); + return 0; +} + + +static struct hx509_keyset_ops keyset_dir = { + "DIR", + 0, + dir_init, + NULL, + dir_free, + NULL, + NULL, + dir_iter_start, + dir_iter, + dir_iter_end +}; + +void +_hx509_ks_dir_register(hx509_context context) +{ + _hx509_ks_register(context, &keyset_dir); +} diff --git a/source4/heimdal/lib/hx509/ks_file.c b/source4/heimdal/lib/hx509/ks_file.c new file mode 100644 index 0000000000..bb8dce3a4e --- /dev/null +++ b/source4/heimdal/lib/hx509/ks_file.c @@ -0,0 +1,645 @@ +/* + * Copyright (c) 2005 - 2007 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hx_locl.h" +RCSID("$Id$"); + +typedef enum { USE_PEM, USE_DER } outformat; + +struct ks_file { + hx509_certs certs; + char *fn; + outformat format; +}; + +/* + * + */ + +static int +parse_certificate(hx509_context context, const char *fn, + struct hx509_collector *c, + const hx509_pem_header *headers, + const void *data, size_t len) +{ + hx509_cert cert; + int ret; + + ret = hx509_cert_init_data(context, data, len, &cert); + if (ret) + return ret; + + ret = _hx509_collector_certs_add(context, c, cert); + hx509_cert_free(cert); + return ret; +} + +static int +try_decrypt(hx509_context context, + struct hx509_collector *collector, + const AlgorithmIdentifier *alg, + const EVP_CIPHER *c, + const void *ivdata, + const void *password, + size_t passwordlen, + const void *cipher, + size_t len) +{ + heim_octet_string clear; + size_t keylen; + void *key; + int ret; + + keylen = EVP_CIPHER_key_length(c); + + key = malloc(keylen); + if (key == NULL) { + hx509_clear_error_string(context); + return ENOMEM; + } + + ret = EVP_BytesToKey(c, EVP_md5(), ivdata, + password, passwordlen, + 1, key, NULL); + if (ret <= 0) { + hx509_set_error_string(context, 0, HX509_CRYPTO_INTERNAL_ERROR, + "Failed to do string2key for private key"); + return HX509_CRYPTO_INTERNAL_ERROR; + } + + clear.data = malloc(len); + if (clear.data == NULL) { + hx509_set_error_string(context, 0, ENOMEM, + "Out of memory to decrypt for private key"); + ret = ENOMEM; + goto out; + } + clear.length = len; + + { + EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX_init(&ctx); + EVP_CipherInit_ex(&ctx, c, NULL, key, ivdata, 0); + EVP_Cipher(&ctx, clear.data, cipher, len); + EVP_CIPHER_CTX_cleanup(&ctx); + } + + ret = _hx509_collector_private_key_add(context, + collector, + alg, + NULL, + &clear, + NULL); + + memset(clear.data, 0, clear.length); + free(clear.data); +out: + memset(key, 0, keylen); + free(key); + return ret; +} + +static int +parse_rsa_private_key(hx509_context context, const char *fn, + struct hx509_collector *c, + const hx509_pem_header *headers, + const void *data, size_t len) +{ + int ret = 0; + const char *enc; + + enc = hx509_pem_find_header(headers, "Proc-Type"); + if (enc) { + const char *dek; + char *type, *iv; + ssize_t ssize, size; + void *ivdata; + const EVP_CIPHER *cipher; + const struct _hx509_password *pw; + hx509_lock lock; + int i, decrypted = 0; + + lock = _hx509_collector_get_lock(c); + if (lock == NULL) { + hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP, + "Failed to get password for " + "password protected file %s", fn); + return HX509_ALG_NOT_SUPP; + } + + if (strcmp(enc, "4,ENCRYPTED") != 0) { + hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, + "RSA key encrypted in unknown method %s " + "in file", + enc, fn); + hx509_clear_error_string(context); + return HX509_PARSING_KEY_FAILED; + } + + dek = hx509_pem_find_header(headers, "DEK-Info"); + if (dek == NULL) { + hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, + "Encrypted RSA missing DEK-Info"); + return HX509_PARSING_KEY_FAILED; + } + + type = strdup(dek); + if (type == NULL) { + hx509_clear_error_string(context); + return ENOMEM; + } + + iv = strchr(type, ','); + if (iv == NULL) { + free(type); + hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, + "IV missing"); + return HX509_PARSING_KEY_FAILED; + } + + *iv++ = '\0'; + + size = strlen(iv); + ivdata = malloc(size); + if (ivdata == NULL) { + hx509_clear_error_string(context); + free(type); + return ENOMEM; + } + + cipher = EVP_get_cipherbyname(type); + if (cipher == NULL) { + free(ivdata); + hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP, + "RSA key encrypted with " + "unsupported cipher: %s", + type); + free(type); + return HX509_ALG_NOT_SUPP; + } + +#define PKCS5_SALT_LEN 8 + + ssize = hex_decode(iv, ivdata, size); + free(type); + type = NULL; + iv = NULL; + + if (ssize < 0 || ssize < PKCS5_SALT_LEN || ssize < EVP_CIPHER_iv_length(cipher)) { + free(ivdata); + hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, + "Salt have wrong length in RSA key file"); + return HX509_PARSING_KEY_FAILED; + } + + pw = _hx509_lock_get_passwords(lock); + if (pw != NULL) { + const void *password; + size_t passwordlen; + + for (i = 0; i < pw->len; i++) { + password = pw->val[i]; + passwordlen = strlen(password); + + ret = try_decrypt(context, c, hx509_signature_rsa(), + cipher, ivdata, password, passwordlen, + data, len); + if (ret == 0) { + decrypted = 1; + break; + } + } + } + if (!decrypted) { + hx509_prompt prompt; + char password[128]; + + memset(&prompt, 0, sizeof(prompt)); + + prompt.prompt = "Password for keyfile: "; + prompt.type = HX509_PROMPT_TYPE_PASSWORD; + prompt.reply.data = password; + prompt.reply.length = sizeof(password); + + ret = hx509_lock_prompt(lock, &prompt); + if (ret == 0) + ret = try_decrypt(context, c, hx509_signature_rsa(), + cipher, ivdata, password, strlen(password), + data, len); + /* XXX add password to lock password collection ? */ + memset(password, 0, sizeof(password)); + } + free(ivdata); + + } else { + heim_octet_string keydata; + + keydata.data = rk_UNCONST(data); + keydata.length = len; + + ret = _hx509_collector_private_key_add(context, + c, + hx509_signature_rsa(), + NULL, + &keydata, + NULL); + } + + return ret; +} + + +struct pem_formats { + const char *name; + int (*func)(hx509_context, const char *, struct hx509_collector *, + const hx509_pem_header *, const void *, size_t); +} formats[] = { + { "CERTIFICATE", parse_certificate }, + { "RSA PRIVATE KEY", parse_rsa_private_key } +}; + + +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 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, pem_ctx->c, header, data, len); + if (ret == 0) + break; + } + } + if (j == sizeof(formats)/sizeof(formats[0])) { + ret = HX509_UNSUPPORTED_OPERATION; + hx509_set_error_string(context, 0, ret, + "Found no matching PEM format for %s", type); + return ret; + } + if (ret && (pem_ctx->flags & HX509_CERTS_UNPROTECT_ALL)) + return ret; + return 0; +} + +/* + * + */ + +static int +file_init_common(hx509_context context, + hx509_certs certs, void **data, int flags, + const char *residue, hx509_lock lock, outformat format) +{ + char *p, *pnext; + struct ks_file *f = NULL; + hx509_private_key *keys = NULL; + int ret; + struct pem_ctx pem_ctx; + + pem_ctx.flags = flags; + pem_ctx.c = NULL; + + *data = NULL; + + if (lock == NULL) + lock = _hx509_empty_lock; + + f = calloc(1, sizeof(*f)); + if (f == NULL) { + hx509_clear_error_string(context); + return ENOMEM; + } + f->format = format; + + f->fn = strdup(residue); + if (f->fn == NULL) { + hx509_clear_error_string(context); + ret = ENOMEM; + goto out; + } + + /* + * XXX this is broken, the function should parse the file before + * overwriting it + */ + + if (flags & HX509_CERTS_CREATE) { + ret = hx509_certs_init(context, "MEMORY:ks-file-create", + 0, lock, &f->certs); + if (ret) + goto out; + *data = f; + return 0; + } + + ret = _hx509_collector_alloc(context, lock, &pem_ctx.c); + if (ret) + goto out; + + for (p = f->fn; p != NULL; p = pnext) { + FILE *f; + + pnext = strchr(p, ','); + if (pnext) + *pnext++ = '\0'; + + + if ((f = fopen(p, "r")) == NULL) { + ret = ENOENT; + hx509_set_error_string(context, 0, ret, + "Failed to open PEM file \"%s\": %s", + p, strerror(errno)); + goto out; + } + rk_cloexec_file(f); + + ret = hx509_pem_read(context, f, pem_func, &pem_ctx); + fclose(f); + if (ret != 0 && ret != HX509_PARSING_KEY_FAILED) + goto out; + else if (ret == HX509_PARSING_KEY_FAILED) { + size_t length; + void *ptr; + int i; + + ret = rk_undumpdata(p, &ptr, &length); + if (ret) { + hx509_clear_error_string(context); + goto out; + } + + for (i = 0; i < sizeof(formats)/sizeof(formats[0]); i++) { + ret = (*formats[i].func)(context, p, pem_ctx.c, NULL, ptr, length); + if (ret == 0) + break; + } + rk_xfree(ptr); + if (ret) + goto out; + } + } + + ret = _hx509_collector_collect_certs(context, pem_ctx.c, &f->certs); + if (ret) + goto out; + + ret = _hx509_collector_collect_private_keys(context, pem_ctx.c, &keys); + if (ret == 0) { + int i; + + for (i = 0; keys[i]; i++) + _hx509_certs_keys_add(context, f->certs, keys[i]); + _hx509_certs_keys_free(context, keys); + } + +out: + if (ret == 0) + *data = f; + else { + if (f->fn) + free(f->fn); + free(f); + } + if (pem_ctx.c) + _hx509_collector_free(pem_ctx.c); + + return ret; +} + +static int +file_init_pem(hx509_context context, + hx509_certs certs, void **data, int flags, + const char *residue, hx509_lock lock) +{ + return file_init_common(context, certs, data, flags, residue, lock, USE_PEM); +} + +static int +file_init_der(hx509_context context, + hx509_certs certs, void **data, int flags, + const char *residue, hx509_lock lock) +{ + return file_init_common(context, certs, data, flags, residue, lock, USE_DER); +} + +static int +file_free(hx509_certs certs, void *data) +{ + struct ks_file *f = data; + hx509_certs_free(&f->certs); + free(f->fn); + free(f); + return 0; +} + +struct store_ctx { + FILE *f; + outformat format; +}; + +static int +store_func(hx509_context context, void *ctx, hx509_cert c) +{ + struct store_ctx *sc = ctx; + heim_octet_string data; + int ret; + + ret = hx509_cert_binary(context, c, &data); + if (ret) + return ret; + + switch (sc->format) { + case USE_DER: + fwrite(data.data, data.length, 1, sc->f); + free(data.data); + break; + case USE_PEM: + hx509_pem_write(context, "CERTIFICATE", NULL, sc->f, + data.data, data.length); + free(data.data); + if (_hx509_cert_private_key_exportable(c)) { + hx509_private_key key = _hx509_cert_private_key(c); + ret = _hx509_private_key_export(context, key, &data); + if (ret) + break; + hx509_pem_write(context, _hx509_private_pem_name(key), NULL, sc->f, + data.data, data.length); + free(data.data); + } + break; + } + + return 0; +} + +static int +file_store(hx509_context context, + hx509_certs certs, void *data, int flags, hx509_lock lock) +{ + struct ks_file *f = data; + struct store_ctx sc; + int ret; + + sc.f = fopen(f->fn, "w"); + if (sc.f == NULL) { + hx509_set_error_string(context, 0, ENOENT, + "Failed to open file %s for writing"); + return ENOENT; + } + rk_cloexec_file(sc.f); + sc.format = f->format; + + ret = hx509_certs_iter(context, f->certs, store_func, &sc); + fclose(sc.f); + return ret; +} + +static int +file_add(hx509_context context, hx509_certs certs, void *data, hx509_cert c) +{ + struct ks_file *f = data; + return hx509_certs_add(context, f->certs, c); +} + +static int +file_iter_start(hx509_context context, + hx509_certs certs, void *data, void **cursor) +{ + struct ks_file *f = data; + return hx509_certs_start_seq(context, f->certs, cursor); +} + +static int +file_iter(hx509_context context, + hx509_certs certs, void *data, void *iter, hx509_cert *cert) +{ + struct ks_file *f = data; + return hx509_certs_next_cert(context, f->certs, iter, cert); +} + +static int +file_iter_end(hx509_context context, + hx509_certs certs, + void *data, + void *cursor) +{ + struct ks_file *f = data; + return hx509_certs_end_seq(context, f->certs, cursor); +} + +static int +file_getkeys(hx509_context context, + hx509_certs certs, + void *data, + hx509_private_key **keys) +{ + struct ks_file *f = data; + return _hx509_certs_keys_get(context, f->certs, keys); +} + +static int +file_addkey(hx509_context context, + hx509_certs certs, + void *data, + hx509_private_key key) +{ + struct ks_file *f = data; + return _hx509_certs_keys_add(context, f->certs, key); +} + +static struct hx509_keyset_ops keyset_file = { + "FILE", + 0, + file_init_pem, + file_store, + file_free, + file_add, + NULL, + file_iter_start, + file_iter, + file_iter_end, + NULL, + file_getkeys, + file_addkey +}; + +static struct hx509_keyset_ops keyset_pemfile = { + "PEM-FILE", + 0, + file_init_pem, + file_store, + file_free, + file_add, + NULL, + file_iter_start, + file_iter, + file_iter_end, + NULL, + file_getkeys, + file_addkey +}; + +static struct hx509_keyset_ops keyset_derfile = { + "DER-FILE", + 0, + file_init_der, + file_store, + file_free, + file_add, + NULL, + file_iter_start, + file_iter, + file_iter_end, + NULL, + file_getkeys, + file_addkey +}; + + +void +_hx509_ks_file_register(hx509_context context) +{ + _hx509_ks_register(context, &keyset_file); + _hx509_ks_register(context, &keyset_pemfile); + _hx509_ks_register(context, &keyset_derfile); +} diff --git a/source4/heimdal/lib/hx509/ks_keychain.c b/source4/heimdal/lib/hx509/ks_keychain.c new file mode 100644 index 0000000000..e51b0ab6a0 --- /dev/null +++ b/source4/heimdal/lib/hx509/ks_keychain.c @@ -0,0 +1,548 @@ +/* + * Copyright (c) 2007 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hx_locl.h" +RCSID("$Id$"); + +#ifdef HAVE_FRAMEWORK_SECURITY + +#include <Security/Security.h> + +/* 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 +getAttribute(SecKeychainItemRef itemRef, SecItemAttr item, + SecKeychainAttributeList **attrs) +{ + SecKeychainAttributeInfo attrInfo; + UInt32 attrFormat = 0; + OSStatus ret; + + *attrs = NULL; + + attrInfo.count = 1; + attrInfo.tag = &item; + attrInfo.format = &attrFormat; + + ret = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL, + attrs, NULL, NULL); + if (ret) + return EINVAL; + return 0; +} + + +/* + * + */ + +struct kc_rsa { + SecKeychainItemRef item; + size_t keysize; +}; + + +static int +kc_rsa_public_encrypt(int flen, + const unsigned char *from, + unsigned char *to, + RSA *rsa, + int padding) +{ + return -1; +} + +static int +kc_rsa_public_decrypt(int flen, + const unsigned char *from, + unsigned char *to, + RSA *rsa, + int padding) +{ + return -1; +} + + +static int +kc_rsa_private_encrypt(int flen, + const unsigned char *from, + unsigned char *to, + RSA *rsa, + int padding) +{ + struct kc_rsa *kc = RSA_get_app_data(rsa); + + CSSM_RETURN cret; + OSStatus ret; + const CSSM_ACCESS_CREDENTIALS *creds; + SecKeyRef privKeyRef = (SecKeyRef)kc->item; + CSSM_CSP_HANDLE cspHandle; + const CSSM_KEY *cssmKey; + CSSM_CC_HANDLE sigHandle = 0; + CSSM_DATA sig, in; + int fret = 0; + + + cret = SecKeyGetCSSMKey(privKeyRef, &cssmKey); + if(cret) abort(); + + cret = SecKeyGetCSPHandle(privKeyRef, &cspHandle); + if(cret) abort(); + + ret = SecKeyGetCredentials(privKeyRef, CSSM_ACL_AUTHORIZATION_SIGN, + kSecCredentialTypeDefault, &creds); + if(ret) abort(); + + ret = CSSM_CSP_CreateSignatureContext(cspHandle, CSSM_ALGID_RSA, + creds, cssmKey, &sigHandle); + if(ret) abort(); + + in.Data = (uint8 *)from; + in.Length = flen; + + sig.Data = (uint8 *)to; + sig.Length = kc->keysize; + + cret = CSSM_SignData(sigHandle, &in, 1, CSSM_ALGID_NONE, &sig); + if(cret) { + /* cssmErrorString(cret); */ + fret = -1; + } else + fret = sig.Length; + + if(sigHandle) + CSSM_DeleteContext(sigHandle); + + return fret; +} + +static int +kc_rsa_private_decrypt(int flen, const unsigned char *from, unsigned char *to, + RSA * rsa, int padding) +{ + return -1; +} + +static int +kc_rsa_init(RSA *rsa) +{ + return 1; +} + +static int +kc_rsa_finish(RSA *rsa) +{ + struct kc_rsa *kc_rsa = RSA_get_app_data(rsa); + CFRelease(kc_rsa->item); + memset(kc_rsa, 0, sizeof(*kc_rsa)); + free(kc_rsa); + return 1; +} + +static const RSA_METHOD kc_rsa_pkcs1_method = { + "hx509 Keychain PKCS#1 RSA", + kc_rsa_public_encrypt, + kc_rsa_public_decrypt, + kc_rsa_private_encrypt, + kc_rsa_private_decrypt, + NULL, + NULL, + kc_rsa_init, + kc_rsa_finish, + 0, + NULL, + NULL, + NULL +}; + +static int +set_private_key(hx509_context context, + SecKeychainItemRef itemRef, + hx509_cert cert) +{ + struct kc_rsa *kc; + hx509_private_key key; + RSA *rsa; + int ret; + + ret = _hx509_private_key_init(&key, NULL, NULL); + if (ret) + return ret; + + kc = calloc(1, sizeof(*kc)); + if (kc == NULL) + _hx509_abort("out of memory"); + + kc->item = itemRef; + + rsa = RSA_new(); + if (rsa == NULL) + _hx509_abort("out of memory"); + + /* Argh, fake modulus since OpenSSL API is on crack */ + { + SecKeychainAttributeList *attrs = NULL; + uint32_t size; + void *data; + + rsa->n = BN_new(); + if (rsa->n == NULL) abort(); + + ret = getAttribute(itemRef, kSecKeyKeySizeInBits, &attrs); + if (ret) abort(); + + size = *(uint32_t *)attrs->attr[0].data; + SecKeychainItemFreeAttributesAndData(attrs, NULL); + + kc->keysize = (size + 7) / 8; + + data = malloc(kc->keysize); + memset(data, 0xe0, kc->keysize); + BN_bin2bn(data, kc->keysize, rsa->n); + free(data); + } + rsa->e = NULL; + + RSA_set_method(rsa, &kc_rsa_pkcs1_method); + ret = RSA_set_app_data(rsa, kc); + if (ret != 1) + _hx509_abort("RSA_set_app_data"); + + _hx509_private_key_assign_rsa(key, rsa); + _hx509_cert_assign_key(cert, key); + + return 0; +} + +/* + * + */ + +struct ks_keychain { + int anchors; + SecKeychainRef keychain; +}; + +static int +keychain_init(hx509_context context, + hx509_certs certs, void **data, int flags, + const char *residue, hx509_lock lock) +{ + struct ks_keychain *ctx; + + ctx = calloc(1, sizeof(*ctx)); + if (ctx == NULL) { + hx509_clear_error_string(context); + return ENOMEM; + } + + if (residue) { + if (strcasecmp(residue, "system-anchors") == 0) { + ctx->anchors = 1; + } else if (strncasecmp(residue, "FILE:", 5) == 0) { + OSStatus ret; + + ret = SecKeychainOpen(residue + 5, &ctx->keychain); + if (ret != noErr) { + hx509_set_error_string(context, 0, ENOENT, + "Failed to open %s", residue); + return ENOENT; + } + } else { + hx509_set_error_string(context, 0, ENOENT, + "Unknown subtype %s", residue); + return ENOENT; + } + } + + *data = ctx; + return 0; +} + +/* + * + */ + +static int +keychain_free(hx509_certs certs, void *data) +{ + struct ks_keychain *ctx = data; + if (ctx->keychain) + CFRelease(ctx->keychain); + memset(ctx, 0, sizeof(*ctx)); + free(ctx); + return 0; +} + +/* + * + */ + +struct iter { + hx509_certs certs; + void *cursor; + SecKeychainSearchRef searchRef; +}; + +static int +keychain_iter_start(hx509_context context, + hx509_certs certs, void *data, void **cursor) +{ + struct ks_keychain *ctx = data; + struct iter *iter; + + iter = calloc(1, sizeof(*iter)); + if (iter == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + + if (ctx->anchors) { + CFArrayRef anchors; + int ret; + int i; + + ret = hx509_certs_init(context, "MEMORY:ks-file-create", + 0, NULL, &iter->certs); + if (ret) { + free(iter); + return ret; + } + + ret = SecTrustCopyAnchorCertificates(&anchors); + if (ret != 0) { + hx509_certs_free(&iter->certs); + free(iter); + hx509_set_error_string(context, 0, ENOMEM, + "Can't get trust anchors from Keychain"); + return ENOMEM; + } + for (i = 0; i < CFArrayGetCount(anchors); i++) { + SecCertificateRef cr; + hx509_cert cert; + CSSM_DATA cssm; + + cr = (SecCertificateRef)CFArrayGetValueAtIndex(anchors, i); + + SecCertificateGetData(cr, &cssm); + + ret = hx509_cert_init_data(context, cssm.Data, cssm.Length, &cert); + if (ret) + continue; + + ret = hx509_certs_add(context, iter->certs, cert); + hx509_cert_free(cert); + } + CFRelease(anchors); + } + + if (iter->certs) { + int ret; + ret = hx509_certs_start_seq(context, iter->certs, &iter->cursor); + if (ret) { + hx509_certs_free(&iter->certs); + free(iter); + return ret; + } + } else { + OSStatus ret; + + ret = SecKeychainSearchCreateFromAttributes(ctx->keychain, + kSecCertificateItemClass, + NULL, + &iter->searchRef); + if (ret) { + free(iter); + hx509_set_error_string(context, 0, ret, + "Failed to start search for attributes"); + return ENOMEM; + } + } + + *cursor = iter; + return 0; +} + +/* + * + */ + +static int +keychain_iter(hx509_context context, + hx509_certs certs, void *data, void *cursor, hx509_cert *cert) +{ + SecKeychainAttributeList *attrs = NULL; + SecKeychainAttributeInfo attrInfo; + UInt32 attrFormat[1] = { 0 }; + SecKeychainItemRef itemRef; + SecItemAttr item[1]; + struct iter *iter = cursor; + OSStatus ret; + UInt32 len; + void *ptr = NULL; + + if (iter->certs) + return hx509_certs_next_cert(context, iter->certs, iter->cursor, cert); + + *cert = NULL; + + ret = SecKeychainSearchCopyNext(iter->searchRef, &itemRef); + if (ret == errSecItemNotFound) + return 0; + else if (ret != 0) + return EINVAL; + + /* + * Pick out certificate and matching "keyid" + */ + + item[0] = kSecPublicKeyHashItemAttr; + + attrInfo.count = 1; + attrInfo.tag = item; + attrInfo.format = attrFormat; + + ret = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL, + &attrs, &len, &ptr); + if (ret) + return EINVAL; + + ret = hx509_cert_init_data(context, ptr, len, cert); + if (ret) + goto out; + + /* + * Find related private key if there is one by looking at + * kSecPublicKeyHashItemAttr == kSecKeyLabel + */ + { + SecKeychainSearchRef search; + SecKeychainAttribute attrKeyid; + SecKeychainAttributeList attrList; + + attrKeyid.tag = kSecKeyLabel; + attrKeyid.length = attrs->attr[0].length; + attrKeyid.data = attrs->attr[0].data; + + attrList.count = 1; + attrList.attr = &attrKeyid; + + ret = SecKeychainSearchCreateFromAttributes(NULL, + CSSM_DL_DB_RECORD_PRIVATE_KEY, + &attrList, + &search); + if (ret) { + ret = 0; + goto out; + } + + ret = SecKeychainSearchCopyNext(search, &itemRef); + CFRelease(search); + if (ret == errSecItemNotFound) { + ret = 0; + goto out; + } else if (ret) { + ret = EINVAL; + goto out; + } + set_private_key(context, itemRef, *cert); + } + +out: + SecKeychainItemFreeAttributesAndData(attrs, ptr); + + return ret; +} + +/* + * + */ + +static int +keychain_iter_end(hx509_context context, + hx509_certs certs, + void *data, + void *cursor) +{ + struct iter *iter = cursor; + + if (iter->certs) { + int ret; + ret = hx509_certs_end_seq(context, iter->certs, iter->cursor); + hx509_certs_free(&iter->certs); + } else { + CFRelease(iter->searchRef); + } + + memset(iter, 0, sizeof(*iter)); + free(iter); + return 0; +} + +/* + * + */ + +struct hx509_keyset_ops keyset_keychain = { + "KEYCHAIN", + 0, + keychain_init, + NULL, + keychain_free, + NULL, + NULL, + keychain_iter_start, + keychain_iter, + keychain_iter_end +}; + +#endif /* HAVE_FRAMEWORK_SECURITY */ + +/* + * + */ + +void +_hx509_ks_keychain_register(hx509_context context) +{ +#ifdef HAVE_FRAMEWORK_SECURITY + _hx509_ks_register(context, &keyset_keychain); +#endif +} diff --git a/source4/heimdal/lib/hx509/ks_mem.c b/source4/heimdal/lib/hx509/ks_mem.c new file mode 100644 index 0000000000..043f19b3e4 --- /dev/null +++ b/source4/heimdal/lib/hx509/ks_mem.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2005 - 2006 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hx_locl.h" +RCSID("Id$"); + +/* + * Should use two hash/tree certificates intead of a array. Criteria + * should be subject and subjectKeyIdentifier since those two are + * commonly seached on in CMS and path building. + */ + +struct mem_data { + char *name; + struct { + unsigned long len; + hx509_cert *val; + } certs; + hx509_private_key *keys; +}; + +static int +mem_init(hx509_context context, + hx509_certs certs, void **data, int flags, + const char *residue, hx509_lock lock) +{ + struct mem_data *mem; + mem = calloc(1, sizeof(*mem)); + if (mem == NULL) + return ENOMEM; + if (residue == NULL || residue[0] == '\0') + residue = "anonymous"; + mem->name = strdup(residue); + if (mem->name == NULL) { + free(mem); + return ENOMEM; + } + *data = mem; + return 0; +} + +static int +mem_free(hx509_certs certs, void *data) +{ + struct mem_data *mem = data; + unsigned long i; + + for (i = 0; i < mem->certs.len; i++) + hx509_cert_free(mem->certs.val[i]); + free(mem->certs.val); + for (i = 0; mem->keys && mem->keys[i]; i++) + _hx509_private_key_free(&mem->keys[i]); + free(mem->keys); + free(mem->name); + free(mem); + + return 0; +} + +static int +mem_add(hx509_context context, hx509_certs certs, void *data, hx509_cert c) +{ + struct mem_data *mem = data; + hx509_cert *val; + + val = realloc(mem->certs.val, + (mem->certs.len + 1) * sizeof(mem->certs.val[0])); + if (val == NULL) + return ENOMEM; + + mem->certs.val = val; + mem->certs.val[mem->certs.len] = hx509_cert_ref(c); + mem->certs.len++; + + return 0; +} + +static int +mem_iter_start(hx509_context context, + hx509_certs certs, + void *data, + void **cursor) +{ + unsigned long *iter = malloc(sizeof(*iter)); + + if (iter == NULL) + return ENOMEM; + + *iter = 0; + *cursor = iter; + + return 0; +} + +static int +mem_iter(hx509_context contexst, + hx509_certs certs, + void *data, + void *cursor, + hx509_cert *cert) +{ + unsigned long *iter = cursor; + struct mem_data *mem = data; + + if (*iter >= mem->certs.len) { + *cert = NULL; + return 0; + } + + *cert = hx509_cert_ref(mem->certs.val[*iter]); + (*iter)++; + return 0; +} + +static int +mem_iter_end(hx509_context context, + hx509_certs certs, + void *data, + void *cursor) +{ + free(cursor); + return 0; +} + +static int +mem_getkeys(hx509_context context, + hx509_certs certs, + void *data, + hx509_private_key **keys) +{ + struct mem_data *mem = data; + int i; + + for (i = 0; mem->keys && mem->keys[i]; i++) + ; + *keys = calloc(i + 1, sizeof(**keys)); + for (i = 0; mem->keys && mem->keys[i]; i++) { + (*keys)[i] = _hx509_private_key_ref(mem->keys[i]); + if ((*keys)[i] == NULL) { + while (--i >= 0) + _hx509_private_key_free(&(*keys)[i]); + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + } + (*keys)[i] = NULL; + return 0; +} + +static int +mem_addkey(hx509_context context, + hx509_certs certs, + void *data, + hx509_private_key key) +{ + struct mem_data *mem = data; + void *ptr; + int i; + + for (i = 0; mem->keys && mem->keys[i]; i++) + ; + ptr = realloc(mem->keys, (i + 2) * sizeof(*mem->keys)); + if (ptr == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + mem->keys = ptr; + mem->keys[i] = _hx509_private_key_ref(key); + mem->keys[i + 1] = NULL; + return 0; +} + + +static struct hx509_keyset_ops keyset_mem = { + "MEMORY", + 0, + mem_init, + NULL, + mem_free, + mem_add, + NULL, + mem_iter_start, + mem_iter, + mem_iter_end, + NULL, + mem_getkeys, + mem_addkey +}; + +void +_hx509_ks_mem_register(hx509_context context) +{ + _hx509_ks_register(context, &keyset_mem); +} diff --git a/source4/heimdal/lib/hx509/ks_null.c b/source4/heimdal/lib/hx509/ks_null.c new file mode 100644 index 0000000000..0b571c8406 --- /dev/null +++ b/source4/heimdal/lib/hx509/ks_null.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2005 - 2006 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hx_locl.h" +RCSID("$Id$"); + + +static int +null_init(hx509_context context, + hx509_certs certs, void **data, int flags, + const char *residue, hx509_lock lock) +{ + *data = NULL; + return 0; +} + +static int +null_free(hx509_certs certs, void *data) +{ + assert(data == NULL); + return 0; +} + +static int +null_iter_start(hx509_context context, + hx509_certs certs, void *data, void **cursor) +{ + *cursor = NULL; + return 0; +} + +static int +null_iter(hx509_context context, + hx509_certs certs, void *data, void *iter, hx509_cert *cert) +{ + *cert = NULL; + return ENOENT; +} + +static int +null_iter_end(hx509_context context, + hx509_certs certs, + void *data, + void *cursor) +{ + assert(cursor == NULL); + return 0; +} + + +struct hx509_keyset_ops keyset_null = { + "NULL", + 0, + null_init, + NULL, + null_free, + NULL, + NULL, + null_iter_start, + null_iter, + null_iter_end +}; + +void +_hx509_ks_null_register(hx509_context context) +{ + _hx509_ks_register(context, &keyset_null); +} diff --git a/source4/heimdal/lib/hx509/ks_p11.c b/source4/heimdal/lib/hx509/ks_p11.c new file mode 100644 index 0000000000..19db6004ce --- /dev/null +++ b/source4/heimdal/lib/hx509/ks_p11.c @@ -0,0 +1,1196 @@ +/* + * Copyright (c) 2004 - 2006 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hx_locl.h" +RCSID("$Id$"); +#ifdef HAVE_DLFCN_H +#include <dlfcn.h> +#endif + +#ifdef HAVE_DLOPEN + +#include "pkcs11.h" + +struct p11_slot { + int flags; +#define P11_SESSION 1 +#define P11_SESSION_IN_USE 2 +#define P11_LOGIN_REQ 4 +#define P11_LOGIN_DONE 8 +#define P11_TOKEN_PRESENT 16 + CK_SESSION_HANDLE session; + CK_SLOT_ID id; + CK_BBOOL token; + char *name; + hx509_certs certs; + char *pin; + struct { + CK_MECHANISM_TYPE_PTR list; + CK_ULONG num; + CK_MECHANISM_INFO_PTR *infos; + } mechs; +}; + +struct p11_module { + void *dl_handle; + CK_FUNCTION_LIST_PTR funcs; + CK_ULONG num_slots; + unsigned int ref; + struct p11_slot *slot; +}; + +#define P11FUNC(module,f,args) (*(module)->funcs->C_##f)args + +static int p11_get_session(hx509_context, + struct p11_module *, + struct p11_slot *, + hx509_lock, + CK_SESSION_HANDLE *); +static int p11_put_session(struct p11_module *, + struct p11_slot *, + CK_SESSION_HANDLE); +static void p11_release_module(struct p11_module *); + +static int p11_list_keys(hx509_context, + struct p11_module *, + struct p11_slot *, + CK_SESSION_HANDLE, + hx509_lock, + hx509_certs *); + +/* + * + */ + +struct p11_rsa { + struct p11_module *p; + struct p11_slot *slot; + CK_OBJECT_HANDLE private_key; + CK_OBJECT_HANDLE public_key; +}; + +static int +p11_rsa_public_encrypt(int flen, + const unsigned char *from, + unsigned char *to, + RSA *rsa, + int padding) +{ + return -1; +} + +static int +p11_rsa_public_decrypt(int flen, + const unsigned char *from, + unsigned char *to, + RSA *rsa, + int padding) +{ + return -1; +} + + +static int +p11_rsa_private_encrypt(int flen, + const unsigned char *from, + unsigned char *to, + RSA *rsa, + int padding) +{ + struct p11_rsa *p11rsa = RSA_get_app_data(rsa); + CK_OBJECT_HANDLE key = p11rsa->private_key; + CK_SESSION_HANDLE session; + CK_MECHANISM mechanism; + CK_ULONG ck_sigsize; + int ret; + + if (padding != RSA_PKCS1_PADDING) + return -1; + + memset(&mechanism, 0, sizeof(mechanism)); + mechanism.mechanism = CKM_RSA_PKCS; + + ck_sigsize = RSA_size(rsa); + + ret = p11_get_session(NULL, p11rsa->p, p11rsa->slot, NULL, &session); + if (ret) + return -1; + + ret = P11FUNC(p11rsa->p, SignInit, (session, &mechanism, key)); + if (ret != CKR_OK) { + p11_put_session(p11rsa->p, p11rsa->slot, session); + return -1; + } + + ret = P11FUNC(p11rsa->p, Sign, + (session, (CK_BYTE *)from, flen, to, &ck_sigsize)); + p11_put_session(p11rsa->p, p11rsa->slot, session); + if (ret != CKR_OK) + return -1; + + return ck_sigsize; +} + +static int +p11_rsa_private_decrypt(int flen, const unsigned char *from, unsigned char *to, + RSA * rsa, int padding) +{ + struct p11_rsa *p11rsa = RSA_get_app_data(rsa); + CK_OBJECT_HANDLE key = p11rsa->private_key; + CK_SESSION_HANDLE session; + CK_MECHANISM mechanism; + CK_ULONG ck_sigsize; + int ret; + + if (padding != RSA_PKCS1_PADDING) + return -1; + + memset(&mechanism, 0, sizeof(mechanism)); + mechanism.mechanism = CKM_RSA_PKCS; + + ck_sigsize = RSA_size(rsa); + + ret = p11_get_session(NULL, p11rsa->p, p11rsa->slot, NULL, &session); + if (ret) + return -1; + + ret = P11FUNC(p11rsa->p, DecryptInit, (session, &mechanism, key)); + if (ret != CKR_OK) { + p11_put_session(p11rsa->p, p11rsa->slot, session); + return -1; + } + + ret = P11FUNC(p11rsa->p, Decrypt, + (session, (CK_BYTE *)from, flen, to, &ck_sigsize)); + p11_put_session(p11rsa->p, p11rsa->slot, session); + if (ret != CKR_OK) + return -1; + + return ck_sigsize; +} + +static int +p11_rsa_init(RSA *rsa) +{ + return 1; +} + +static int +p11_rsa_finish(RSA *rsa) +{ + struct p11_rsa *p11rsa = RSA_get_app_data(rsa); + p11_release_module(p11rsa->p); + free(p11rsa); + return 1; +} + +static const RSA_METHOD p11_rsa_pkcs1_method = { + "hx509 PKCS11 PKCS#1 RSA", + p11_rsa_public_encrypt, + p11_rsa_public_decrypt, + p11_rsa_private_encrypt, + p11_rsa_private_decrypt, + NULL, + NULL, + p11_rsa_init, + p11_rsa_finish, + 0, + NULL, + NULL, + NULL +}; + +/* + * + */ + +static int +p11_mech_info(hx509_context context, + struct p11_module *p, + struct p11_slot *slot, + int num) +{ + CK_ULONG i; + int ret; + + ret = P11FUNC(p, GetMechanismList, (slot->id, NULL_PTR, &i)); + if (ret) { + hx509_set_error_string(context, 0, HX509_PKCS11_NO_MECH, + "Failed to get mech list count for slot %d", + num); + return HX509_PKCS11_NO_MECH; + } + if (i == 0) { + hx509_set_error_string(context, 0, HX509_PKCS11_NO_MECH, + "no mech supported for slot %d", num); + return HX509_PKCS11_NO_MECH; + } + slot->mechs.list = calloc(i, sizeof(slot->mechs.list[0])); + if (slot->mechs.list == NULL) { + hx509_set_error_string(context, 0, ENOMEM, + "out of memory"); + return ENOMEM; + } + slot->mechs.num = i; + ret = P11FUNC(p, GetMechanismList, (slot->id, slot->mechs.list, &i)); + if (ret) { + hx509_set_error_string(context, 0, HX509_PKCS11_NO_MECH, + "Failed to get mech list for slot %d", + num); + return HX509_PKCS11_NO_MECH; + } + assert(i == slot->mechs.num); + + slot->mechs.infos = calloc(i, sizeof(*slot->mechs.infos)); + if (slot->mechs.list == NULL) { + hx509_set_error_string(context, 0, ENOMEM, + "out of memory"); + return ENOMEM; + } + + for (i = 0; i < slot->mechs.num; i++) { + slot->mechs.infos[i] = calloc(1, sizeof(*(slot->mechs.infos[0]))); + if (slot->mechs.infos[i] == NULL) { + hx509_set_error_string(context, 0, ENOMEM, + "out of memory"); + return ENOMEM; + } + ret = P11FUNC(p, GetMechanismInfo, (slot->id, slot->mechs.list[i], + slot->mechs.infos[i])); + if (ret) { + hx509_set_error_string(context, 0, HX509_PKCS11_NO_MECH, + "Failed to get mech info for slot %d", + num); + return HX509_PKCS11_NO_MECH; + } + } + + return 0; +} + +static int +p11_init_slot(hx509_context context, + struct p11_module *p, + hx509_lock lock, + CK_SLOT_ID id, + int num, + struct p11_slot *slot) +{ + CK_SESSION_HANDLE session; + CK_SLOT_INFO slot_info; + CK_TOKEN_INFO token_info; + size_t i; + int ret; + + slot->certs = NULL; + slot->id = id; + + ret = P11FUNC(p, GetSlotInfo, (slot->id, &slot_info)); + if (ret) { + hx509_set_error_string(context, 0, HX509_PKCS11_TOKEN_CONFUSED, + "Failed to init PKCS11 slot %d", + num); + return HX509_PKCS11_TOKEN_CONFUSED; + } + + for (i = sizeof(slot_info.slotDescription) - 1; i > 0; i--) { + char c = slot_info.slotDescription[i]; + if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\0') + continue; + i++; + break; + } + + asprintf(&slot->name, "%.*s", + i, slot_info.slotDescription); + + if ((slot_info.flags & CKF_TOKEN_PRESENT) == 0) + return 0; + + ret = P11FUNC(p, GetTokenInfo, (slot->id, &token_info)); + if (ret) { + hx509_set_error_string(context, 0, HX509_PKCS11_NO_TOKEN, + "Failed to init PKCS11 slot %d " + "with error 0x08x", + num, ret); + return HX509_PKCS11_NO_TOKEN; + } + slot->flags |= P11_TOKEN_PRESENT; + + if (token_info.flags & CKF_LOGIN_REQUIRED) + slot->flags |= P11_LOGIN_REQ; + + ret = p11_get_session(context, p, slot, lock, &session); + if (ret) + return ret; + + ret = p11_mech_info(context, p, slot, num); + if (ret) + goto out; + + ret = p11_list_keys(context, p, slot, session, lock, &slot->certs); + out: + p11_put_session(p, slot, session); + + return ret; +} + +static int +p11_get_session(hx509_context context, + struct p11_module *p, + struct p11_slot *slot, + hx509_lock lock, + CK_SESSION_HANDLE *psession) +{ + CK_RV ret; + + if (slot->flags & P11_SESSION_IN_USE) + _hx509_abort("slot already in session"); + + if (slot->flags & P11_SESSION) { + slot->flags |= P11_SESSION_IN_USE; + *psession = slot->session; + return 0; + } + + ret = P11FUNC(p, OpenSession, (slot->id, + CKF_SERIAL_SESSION, + NULL, + NULL, + &slot->session)); + if (ret != CKR_OK) { + if (context) + hx509_set_error_string(context, 0, HX509_PKCS11_OPEN_SESSION, + "Failed to OpenSession for slot id %d " + "with error: 0x%08x", + (int)slot->id, ret); + return HX509_PKCS11_OPEN_SESSION; + } + + slot->flags |= P11_SESSION; + + /* + * If we have have to login, and haven't tried before and have a + * 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 it's bad to try many + * passwords on a pkcs11 token, it might lock up and have to be + * unlocked by a administrator. + * + * XXX try harder to not use pin several times on the same card. + */ + + if ( (slot->flags & P11_LOGIN_REQ) + && (slot->flags & P11_LOGIN_DONE) == 0 + && (lock || slot->pin)) + { + hx509_prompt prompt; + char pin[20]; + char *str; + + slot->flags |= P11_LOGIN_DONE; + + if (slot->pin == NULL) { + + memset(&prompt, 0, sizeof(prompt)); + + asprintf(&str, "PIN code for %s: ", slot->name); + prompt.prompt = str; + prompt.type = HX509_PROMPT_TYPE_PASSWORD; + prompt.reply.data = pin; + prompt.reply.length = sizeof(pin); + + ret = hx509_lock_prompt(lock, &prompt); + if (ret) { + free(str); + if (context) + hx509_set_error_string(context, 0, ret, + "Failed to get pin code for slot " + "id %d with error: %d", + (int)slot->id, ret); + return ret; + } + free(str); + } else { + strlcpy(pin, slot->pin, sizeof(pin)); + } + + ret = P11FUNC(p, Login, (slot->session, CKU_USER, + (unsigned char*)pin, strlen(pin))); + if (ret != CKR_OK) { + if (context) + hx509_set_error_string(context, 0, HX509_PKCS11_LOGIN, + "Failed to login on slot id %d " + "with error: 0x%08x", + (int)slot->id, ret); + p11_put_session(p, slot, slot->session); + return HX509_PKCS11_LOGIN; + } + if (slot->pin == NULL) { + slot->pin = strdup(pin); + if (slot->pin == NULL) { + if (context) + hx509_set_error_string(context, 0, ENOMEM, + "out of memory"); + p11_put_session(p, slot, slot->session); + return ENOMEM; + } + } + } else + slot->flags |= P11_LOGIN_DONE; + + slot->flags |= P11_SESSION_IN_USE; + + *psession = slot->session; + + return 0; +} + +static int +p11_put_session(struct p11_module *p, + struct p11_slot *slot, + CK_SESSION_HANDLE session) +{ + if ((slot->flags & P11_SESSION_IN_USE) == 0) + _hx509_abort("slot not in session"); + slot->flags &= ~P11_SESSION_IN_USE; + + return 0; +} + +static int +iterate_entries(hx509_context context, + struct p11_module *p, struct p11_slot *slot, + CK_SESSION_HANDLE session, + CK_ATTRIBUTE *search_data, int num_search_data, + CK_ATTRIBUTE *query, int num_query, + int (*func)(hx509_context, + struct p11_module *, struct p11_slot *, + CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE object, + void *, CK_ATTRIBUTE *, int), void *ptr) +{ + CK_OBJECT_HANDLE object; + CK_ULONG object_count; + int ret, ret2, i; + + ret = P11FUNC(p, FindObjectsInit, (session, search_data, num_search_data)); + if (ret != CKR_OK) { + return -1; + } + while (1) { + ret = P11FUNC(p, FindObjects, (session, &object, 1, &object_count)); + if (ret != CKR_OK) { + return -1; + } + if (object_count == 0) + break; + + for (i = 0; i < num_query; i++) + query[i].pValue = NULL; + + ret = P11FUNC(p, GetAttributeValue, + (session, object, query, num_query)); + if (ret != CKR_OK) { + return -1; + } + for (i = 0; i < num_query; i++) { + query[i].pValue = malloc(query[i].ulValueLen); + if (query[i].pValue == NULL) { + ret = ENOMEM; + goto out; + } + } + ret = P11FUNC(p, GetAttributeValue, + (session, object, query, num_query)); + if (ret != CKR_OK) { + ret = -1; + goto out; + } + + ret = (*func)(context, p, slot, session, object, ptr, query, num_query); + if (ret) + goto out; + + for (i = 0; i < num_query; i++) { + if (query[i].pValue) + free(query[i].pValue); + query[i].pValue = NULL; + } + } + out: + + for (i = 0; i < num_query; i++) { + if (query[i].pValue) + free(query[i].pValue); + query[i].pValue = NULL; + } + + ret2 = P11FUNC(p, FindObjectsFinal, (session)); + if (ret2 != CKR_OK) { + return ret2; + } + + return ret; +} + +static BIGNUM * +getattr_bn(struct p11_module *p, + struct p11_slot *slot, + CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE object, + unsigned int type) +{ + CK_ATTRIBUTE query; + BIGNUM *bn; + int ret; + + query.type = type; + query.pValue = NULL; + query.ulValueLen = 0; + + ret = P11FUNC(p, GetAttributeValue, + (session, object, &query, 1)); + if (ret != CKR_OK) + return NULL; + + query.pValue = malloc(query.ulValueLen); + + ret = P11FUNC(p, GetAttributeValue, + (session, object, &query, 1)); + if (ret != CKR_OK) { + free(query.pValue); + return NULL; + } + bn = BN_bin2bn(query.pValue, query.ulValueLen, NULL); + free(query.pValue); + + return bn; +} + +static int +collect_private_key(hx509_context context, + struct p11_module *p, struct p11_slot *slot, + CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE object, + void *ptr, CK_ATTRIBUTE *query, int num_query) +{ + struct hx509_collector *collector = ptr; + hx509_private_key key; + heim_octet_string localKeyId; + int ret; + RSA *rsa; + struct p11_rsa *p11rsa; + + localKeyId.data = query[0].pValue; + localKeyId.length = query[0].ulValueLen; + + ret = _hx509_private_key_init(&key, NULL, NULL); + if (ret) + return ret; + + rsa = RSA_new(); + if (rsa == NULL) + _hx509_abort("out of memory"); + + /* + * The exponent and modulus should always be present according to + * the pkcs11 specification, but some smartcards leaves it out, + * let ignore any failure to fetch it. + */ + rsa->n = getattr_bn(p, slot, session, object, CKA_MODULUS); + rsa->e = getattr_bn(p, slot, session, object, CKA_PUBLIC_EXPONENT); + + p11rsa = calloc(1, sizeof(*p11rsa)); + if (p11rsa == NULL) + _hx509_abort("out of memory"); + + p11rsa->p = p; + p11rsa->slot = slot; + p11rsa->private_key = object; + + if (p->ref == 0) + _hx509_abort("pkcs11 ref == 0 on alloc"); + p->ref++; + if (p->ref == UINT_MAX) + _hx509_abort("pkcs11 ref == UINT_MAX on alloc"); + + RSA_set_method(rsa, &p11_rsa_pkcs1_method); + ret = RSA_set_app_data(rsa, p11rsa); + if (ret != 1) + _hx509_abort("RSA_set_app_data"); + + _hx509_private_key_assign_rsa(key, rsa); + + ret = _hx509_collector_private_key_add(context, + collector, + hx509_signature_rsa(), + key, + NULL, + &localKeyId); + + if (ret) { + _hx509_private_key_free(&key); + return ret; + } + return 0; +} + +static void +p11_cert_release(hx509_cert cert, void *ctx) +{ + struct p11_module *p = ctx; + p11_release_module(p); +} + + +static int +collect_cert(hx509_context context, + struct p11_module *p, struct p11_slot *slot, + CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE object, + void *ptr, CK_ATTRIBUTE *query, int num_query) +{ + struct hx509_collector *collector = ptr; + hx509_cert cert; + int ret; + + if ((CK_LONG)query[0].ulValueLen == -1 || + (CK_LONG)query[1].ulValueLen == -1) + { + return 0; + } + + ret = hx509_cert_init_data(context, query[1].pValue, + query[1].ulValueLen, &cert); + if (ret) + return ret; + + if (p->ref == 0) + _hx509_abort("pkcs11 ref == 0 on alloc"); + p->ref++; + if (p->ref == UINT_MAX) + _hx509_abort("pkcs11 ref to high"); + + _hx509_cert_set_release(cert, p11_cert_release, p); + + { + heim_octet_string data; + + data.data = query[0].pValue; + data.length = query[0].ulValueLen; + + _hx509_set_cert_attribute(context, + cert, + oid_id_pkcs_9_at_localKeyId(), + &data); + } + + if ((CK_LONG)query[2].ulValueLen != -1) { + char *str; + + asprintf(&str, "%.*s", + (int)query[2].ulValueLen, (char *)query[2].pValue); + if (str) { + hx509_cert_set_friendly_name(cert, str); + free(str); + } + } + + ret = _hx509_collector_certs_add(context, collector, cert); + hx509_cert_free(cert); + + return ret; +} + + +static int +p11_list_keys(hx509_context context, + struct p11_module *p, + struct p11_slot *slot, + CK_SESSION_HANDLE session, + hx509_lock lock, + hx509_certs *certs) +{ + struct hx509_collector *collector; + CK_OBJECT_CLASS key_class; + CK_ATTRIBUTE search_data[] = { + {CKA_CLASS, NULL, 0}, + }; + CK_ATTRIBUTE query_data[3] = { + {CKA_ID, NULL, 0}, + {CKA_VALUE, NULL, 0}, + {CKA_LABEL, NULL, 0} + }; + int ret; + + search_data[0].pValue = &key_class; + search_data[0].ulValueLen = sizeof(key_class); + + if (lock == NULL) + lock = _hx509_empty_lock; + + ret = _hx509_collector_alloc(context, lock, &collector); + if (ret) + return ret; + + key_class = CKO_PRIVATE_KEY; + ret = iterate_entries(context, p, slot, session, + search_data, 1, + query_data, 1, + collect_private_key, collector); + if (ret) + goto out; + + key_class = CKO_CERTIFICATE; + ret = iterate_entries(context, p, slot, session, + search_data, 1, + query_data, 3, + collect_cert, collector); + if (ret) + goto out; + + ret = _hx509_collector_collect_certs(context, collector, &slot->certs); + +out: + _hx509_collector_free(collector); + + return ret; +} + + +static int +p11_init(hx509_context context, + hx509_certs certs, void **data, int flags, + const char *residue, hx509_lock lock) +{ + CK_C_GetFunctionList getFuncs; + struct p11_module *p; + char *list, *str; + int ret; + + *data = NULL; + + list = strdup(residue); + if (list == NULL) + return ENOMEM; + + p = calloc(1, sizeof(*p)); + if (p == NULL) { + free(list); + return ENOMEM; + } + + p->ref = 1; + + str = strchr(list, ','); + if (str) + *str++ = '\0'; + while (str) { + char *strnext; + strnext = strchr(str, ','); + if (strnext) + *strnext++ = '\0'; +#if 0 + if (strncasecmp(str, "slot=", 5) == 0) + p->selected_slot = atoi(str + 5); +#endif + str = strnext; + } + + p->dl_handle = dlopen(list, RTLD_NOW); + free(list); + if (p->dl_handle == NULL) { + ret = HX509_PKCS11_LOAD; + hx509_set_error_string(context, 0, ret, + "Failed to open %s: %s", list, dlerror()); + goto out; + } + + getFuncs = dlsym(p->dl_handle, "C_GetFunctionList"); + if (getFuncs == NULL) { + ret = HX509_PKCS11_LOAD; + hx509_set_error_string(context, 0, ret, + "C_GetFunctionList missing in %s: %s", + list, dlerror()); + goto out; + } + + ret = (*getFuncs)(&p->funcs); + if (ret) { + ret = HX509_PKCS11_LOAD; + hx509_set_error_string(context, 0, ret, + "C_GetFunctionList failed in %s", list); + goto out; + } + + ret = P11FUNC(p, Initialize, (NULL_PTR)); + if (ret != CKR_OK) { + ret = HX509_PKCS11_TOKEN_CONFUSED; + hx509_set_error_string(context, 0, ret, + "Failed initialize the PKCS11 module"); + goto out; + } + + ret = P11FUNC(p, GetSlotList, (FALSE, NULL, &p->num_slots)); + if (ret) { + ret = HX509_PKCS11_TOKEN_CONFUSED; + hx509_set_error_string(context, 0, ret, + "Failed to get number of PKCS11 slots"); + goto out; + } + + if (p->num_slots == 0) { + ret = HX509_PKCS11_NO_SLOT; + hx509_set_error_string(context, 0, ret, + "Selected PKCS11 module have no slots"); + goto out; + } + + + { + CK_SLOT_ID_PTR slot_ids; + int i, num_tokens = 0; + + slot_ids = malloc(p->num_slots * sizeof(*slot_ids)); + if (slot_ids == NULL) { + hx509_clear_error_string(context); + ret = ENOMEM; + goto out; + } + + ret = P11FUNC(p, GetSlotList, (FALSE, slot_ids, &p->num_slots)); + if (ret) { + free(slot_ids); + hx509_set_error_string(context, 0, HX509_PKCS11_TOKEN_CONFUSED, + "Failed getting slot-list from " + "PKCS11 module"); + ret = HX509_PKCS11_TOKEN_CONFUSED; + goto out; + } + + p->slot = calloc(p->num_slots, sizeof(p->slot[0])); + if (p->slot == NULL) { + free(slot_ids); + hx509_set_error_string(context, 0, ENOMEM, + "Failed to get memory for slot-list"); + ret = ENOMEM; + goto out; + } + + for (i = 0; i < p->num_slots; i++) { + ret = p11_init_slot(context, p, lock, slot_ids[i], i, &p->slot[i]); + if (ret) + break; + if (p->slot[i].flags & P11_TOKEN_PRESENT) + num_tokens++; + } + free(slot_ids); + if (ret) + goto out; + if (num_tokens == 0) { + ret = HX509_PKCS11_NO_TOKEN; + goto out; + } + } + + *data = p; + + return 0; + out: + p11_release_module(p); + return ret; +} + +static void +p11_release_module(struct p11_module *p) +{ + int i; + + if (p->ref == 0) + _hx509_abort("pkcs11 ref to low"); + if (--p->ref > 0) + return; + + for (i = 0; i < p->num_slots; i++) { + if (p->slot[i].flags & P11_SESSION_IN_USE) + _hx509_abort("pkcs11 module release while session in use"); + if (p->slot[i].flags & P11_SESSION) { + int ret; + + ret = P11FUNC(p, CloseSession, (p->slot[i].session)); + if (ret != CKR_OK) + ; + } + + if (p->slot[i].name) + free(p->slot[i].name); + if (p->slot[i].pin) { + memset(p->slot[i].pin, 0, strlen(p->slot[i].pin)); + free(p->slot[i].pin); + } + if (p->slot[i].mechs.num) { + free(p->slot[i].mechs.list); + + if (p->slot[i].mechs.infos) { + int j; + + for (j = 0 ; j < p->slot[i].mechs.num ; j++) + free(p->slot[i].mechs.infos[j]); + free(p->slot[i].mechs.infos); + } + } + } + free(p->slot); + + if (p->funcs) + P11FUNC(p, Finalize, (NULL)); + + if (p->dl_handle) + dlclose(p->dl_handle); + + memset(p, 0, sizeof(*p)); + free(p); +} + +static int +p11_free(hx509_certs certs, void *data) +{ + struct p11_module *p = data; + int i; + + for (i = 0; i < p->num_slots; i++) { + if (p->slot[i].certs) + hx509_certs_free(&p->slot[i].certs); + } + p11_release_module(p); + return 0; +} + +struct p11_cursor { + hx509_certs certs; + void *cursor; +}; + +static int +p11_iter_start(hx509_context context, + hx509_certs certs, void *data, void **cursor) +{ + struct p11_module *p = data; + struct p11_cursor *c; + int ret, i; + + c = malloc(sizeof(*c)); + if (c == NULL) { + hx509_clear_error_string(context); + return ENOMEM; + } + ret = hx509_certs_init(context, "MEMORY:pkcs11-iter", 0, NULL, &c->certs); + if (ret) { + free(c); + return ret; + } + + for (i = 0 ; i < p->num_slots; i++) { + if (p->slot[i].certs == NULL) + continue; + ret = hx509_certs_merge(context, c->certs, p->slot[i].certs); + if (ret) { + hx509_certs_free(&c->certs); + free(c); + return ret; + } + } + + ret = hx509_certs_start_seq(context, c->certs, &c->cursor); + if (ret) { + hx509_certs_free(&c->certs); + free(c); + return 0; + } + *cursor = c; + + return 0; +} + +static int +p11_iter(hx509_context context, + hx509_certs certs, void *data, void *cursor, hx509_cert *cert) +{ + struct p11_cursor *c = cursor; + return hx509_certs_next_cert(context, c->certs, c->cursor, cert); +} + +static int +p11_iter_end(hx509_context context, + hx509_certs certs, void *data, void *cursor) +{ + struct p11_cursor *c = cursor; + int ret; + ret = hx509_certs_end_seq(context, c->certs, c->cursor); + hx509_certs_free(&c->certs); + free(c); + return ret; +} + +#define MECHFLAG(x) { "unknown-flag-" #x, x } +static struct units mechflags[] = { + MECHFLAG(0x80000000), + MECHFLAG(0x40000000), + MECHFLAG(0x20000000), + MECHFLAG(0x10000000), + MECHFLAG(0x08000000), + MECHFLAG(0x04000000), + {"ec-compress", 0x2000000 }, + {"ec-uncompress", 0x1000000 }, + {"ec-namedcurve", 0x0800000 }, + {"ec-ecparameters", 0x0400000 }, + {"ec-f-2m", 0x0200000 }, + {"ec-f-p", 0x0100000 }, + {"derive", 0x0080000 }, + {"unwrap", 0x0040000 }, + {"wrap", 0x0020000 }, + {"genereate-key-pair", 0x0010000 }, + {"generate", 0x0008000 }, + {"verify-recover", 0x0004000 }, + {"verify", 0x0002000 }, + {"sign-recover", 0x0001000 }, + {"sign", 0x0000800 }, + {"digest", 0x0000400 }, + {"decrypt", 0x0000200 }, + {"encrypt", 0x0000100 }, + MECHFLAG(0x00080), + MECHFLAG(0x00040), + MECHFLAG(0x00020), + MECHFLAG(0x00010), + MECHFLAG(0x00008), + MECHFLAG(0x00004), + MECHFLAG(0x00002), + {"hw", 0x0000001 }, + { NULL, 0x0000000 } +}; +#undef MECHFLAG + +static int +p11_printinfo(hx509_context context, + hx509_certs certs, + void *data, + int (*func)(void *, const char *), + void *ctx) +{ + struct p11_module *p = data; + int i, j; + + _hx509_pi_printf(func, ctx, "pkcs11 driver with %d slot%s", + p->num_slots, p->num_slots > 1 ? "s" : ""); + + for (i = 0; i < p->num_slots; i++) { + struct p11_slot *s = &p->slot[i]; + + _hx509_pi_printf(func, ctx, "slot %d: id: %d name: %s flags: %08x", + i, (int)s->id, s->name, s->flags); + + _hx509_pi_printf(func, ctx, "number of supported mechanisms: %lu", + (unsigned long)s->mechs.num); + for (j = 0; j < s->mechs.num; j++) { + const char *mechname = "unknown"; + char flags[256], unknownname[40]; +#define MECHNAME(s,n) case s: mechname = n; break + switch(s->mechs.list[j]) { + MECHNAME(CKM_RSA_PKCS_KEY_PAIR_GEN, "rsa-pkcs-key-pair-gen"); + MECHNAME(CKM_RSA_PKCS, "rsa-pkcs"); + MECHNAME(CKM_RSA_X_509, "rsa-x-509"); + MECHNAME(CKM_MD5_RSA_PKCS, "md5-rsa-pkcs"); + MECHNAME(CKM_SHA1_RSA_PKCS, "sha1-rsa-pkcs"); + MECHNAME(CKM_SHA256_RSA_PKCS, "sha256-rsa-pkcs"); + MECHNAME(CKM_SHA384_RSA_PKCS, "sha384-rsa-pkcs"); + MECHNAME(CKM_SHA512_RSA_PKCS, "sha512-rsa-pkcs"); + MECHNAME(CKM_RIPEMD160_RSA_PKCS, "ripemd160-rsa-pkcs"); + MECHNAME(CKM_RSA_PKCS_OAEP, "rsa-pkcs-oaep"); + MECHNAME(CKM_SHA512_HMAC, "sha512-hmac"); + MECHNAME(CKM_SHA512, "sha512"); + MECHNAME(CKM_SHA384_HMAC, "sha384-hmac"); + MECHNAME(CKM_SHA384, "sha384"); + MECHNAME(CKM_SHA256_HMAC, "sha256-hmac"); + MECHNAME(CKM_SHA256, "sha256"); + MECHNAME(CKM_SHA_1, "sha1"); + MECHNAME(CKM_MD5, "md5"); + MECHNAME(CKM_MD2, "md2"); + MECHNAME(CKM_RIPEMD160, "ripemd-160"); + MECHNAME(CKM_DES_ECB, "des-ecb"); + MECHNAME(CKM_DES_CBC, "des-cbc"); + MECHNAME(CKM_AES_ECB, "aes-ecb"); + MECHNAME(CKM_AES_CBC, "aes-cbc"); + MECHNAME(CKM_DH_PKCS_PARAMETER_GEN, "dh-pkcs-parameter-gen"); + default: + snprintf(unknownname, sizeof(unknownname), + "unknown-mech-%lu", + (unsigned long)s->mechs.list[j]); + mechname = unknownname; + break; + } +#undef MECHNAME + unparse_flags(s->mechs.infos[j]->flags, mechflags, + flags, sizeof(flags)); + + _hx509_pi_printf(func, ctx, " %s: %s", mechname, flags); + } + } + + return 0; +} + +static struct hx509_keyset_ops keyset_pkcs11 = { + "PKCS11", + 0, + p11_init, + NULL, + p11_free, + NULL, + NULL, + p11_iter_start, + p11_iter, + p11_iter_end, + p11_printinfo +}; + +#endif /* HAVE_DLOPEN */ + +void +_hx509_ks_pkcs11_register(hx509_context context) +{ +#ifdef HAVE_DLOPEN + _hx509_ks_register(context, &keyset_pkcs11); +#endif +} diff --git a/source4/heimdal/lib/hx509/ks_p12.c b/source4/heimdal/lib/hx509/ks_p12.c new file mode 100644 index 0000000000..53590c768c --- /dev/null +++ b/source4/heimdal/lib/hx509/ks_p12.c @@ -0,0 +1,705 @@ +/* + * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hx_locl.h" +RCSID("$Id$"); + +struct ks_pkcs12 { + hx509_certs certs; + char *fn; +}; + +typedef int (*collector_func)(hx509_context, + struct hx509_collector *, + const void *, size_t, + const PKCS12_Attributes *); + +struct type { + const heim_oid * (*oid)(void); + collector_func func; +}; + +static void +parse_pkcs12_type(hx509_context, struct hx509_collector *, const heim_oid *, + const void *, size_t, const PKCS12_Attributes *); + + +static const PKCS12_Attribute * +find_attribute(const PKCS12_Attributes *attrs, const heim_oid *oid) +{ + int i; + if (attrs == NULL) + return NULL; + for (i = 0; i < attrs->len; i++) + if (der_heim_oid_cmp(oid, &attrs->val[i].attrId) == 0) + return &attrs->val[i]; + return NULL; +} + +static int +keyBag_parser(hx509_context context, + struct hx509_collector *c, + const void *data, size_t length, + const PKCS12_Attributes *attrs) +{ + const PKCS12_Attribute *attr; + PKCS8PrivateKeyInfo ki; + const heim_octet_string *os = NULL; + int ret; + + attr = find_attribute(attrs, oid_id_pkcs_9_at_localKeyId()); + if (attr) + os = &attr->attrValues; + + ret = decode_PKCS8PrivateKeyInfo(data, length, &ki, NULL); + if (ret) + return ret; + + _hx509_collector_private_key_add(context, + c, + &ki.privateKeyAlgorithm, + NULL, + &ki.privateKey, + os); + free_PKCS8PrivateKeyInfo(&ki); + return 0; +} + +static int +ShroudedKeyBag_parser(hx509_context context, + struct hx509_collector *c, + const void *data, size_t length, + const PKCS12_Attributes *attrs) +{ + PKCS8EncryptedPrivateKeyInfo pk; + heim_octet_string content; + int ret; + + memset(&pk, 0, sizeof(pk)); + + ret = decode_PKCS8EncryptedPrivateKeyInfo(data, length, &pk, NULL); + if (ret) + return ret; + + ret = _hx509_pbe_decrypt(context, + _hx509_collector_get_lock(c), + &pk.encryptionAlgorithm, + &pk.encryptedData, + &content); + free_PKCS8EncryptedPrivateKeyInfo(&pk); + if (ret) + return ret; + + ret = keyBag_parser(context, c, content.data, content.length, attrs); + der_free_octet_string(&content); + return ret; +} + +static int +certBag_parser(hx509_context context, + struct hx509_collector *c, + const void *data, size_t length, + const PKCS12_Attributes *attrs) +{ + heim_octet_string os; + hx509_cert cert; + PKCS12_CertBag cb; + int ret; + + ret = decode_PKCS12_CertBag(data, length, &cb, NULL); + if (ret) + return ret; + + if (der_heim_oid_cmp(oid_id_pkcs_9_at_certTypes_x509(), &cb.certType)) { + free_PKCS12_CertBag(&cb); + return 0; + } + + ret = decode_PKCS12_OctetString(cb.certValue.data, + cb.certValue.length, + &os, + NULL); + free_PKCS12_CertBag(&cb); + if (ret) + return ret; + + ret = hx509_cert_init_data(context, os.data, os.length, &cert); + der_free_octet_string(&os); + if (ret) + return ret; + + ret = _hx509_collector_certs_add(context, c, cert); + if (ret) { + hx509_cert_free(cert); + return ret; + } + + { + const PKCS12_Attribute *attr; + const heim_oid * (*oids[])(void) = { + oid_id_pkcs_9_at_localKeyId, oid_id_pkcs_9_at_friendlyName + }; + int i; + + for (i = 0; i < sizeof(oids)/sizeof(oids[0]); i++) { + const heim_oid *oid = (*(oids[i]))(); + attr = find_attribute(attrs, oid); + if (attr) + _hx509_set_cert_attribute(context, cert, oid, + &attr->attrValues); + } + } + + hx509_cert_free(cert); + + return 0; +} + +static int +parse_safe_content(hx509_context context, + struct hx509_collector *c, + const unsigned char *p, size_t len) +{ + PKCS12_SafeContents sc; + int ret, i; + + memset(&sc, 0, sizeof(sc)); + + ret = decode_PKCS12_SafeContents(p, len, &sc, NULL); + if (ret) + return ret; + + for (i = 0; i < sc.len ; i++) + parse_pkcs12_type(context, + c, + &sc.val[i].bagId, + sc.val[i].bagValue.data, + sc.val[i].bagValue.length, + sc.val[i].bagAttributes); + + free_PKCS12_SafeContents(&sc); + return 0; +} + +static int +safeContent_parser(hx509_context context, + struct hx509_collector *c, + const void *data, size_t length, + const PKCS12_Attributes *attrs) +{ + heim_octet_string os; + int ret; + + ret = decode_PKCS12_OctetString(data, length, &os, NULL); + if (ret) + return ret; + ret = parse_safe_content(context, c, os.data, os.length); + der_free_octet_string(&os); + return ret; +} + +static int +encryptedData_parser(hx509_context context, + struct hx509_collector *c, + const void *data, size_t length, + const PKCS12_Attributes *attrs) +{ + heim_octet_string content; + heim_oid contentType; + int ret; + + memset(&contentType, 0, sizeof(contentType)); + + ret = hx509_cms_decrypt_encrypted(context, + _hx509_collector_get_lock(c), + data, length, + &contentType, + &content); + if (ret) + return ret; + + if (der_heim_oid_cmp(&contentType, oid_id_pkcs7_data()) == 0) + ret = parse_safe_content(context, c, content.data, content.length); + + der_free_octet_string(&content); + der_free_oid(&contentType); + return ret; +} + +static int +envelopedData_parser(hx509_context context, + struct hx509_collector *c, + const void *data, size_t length, + const PKCS12_Attributes *attrs) +{ + heim_octet_string content; + heim_oid contentType; + hx509_lock lock; + int ret; + + memset(&contentType, 0, sizeof(contentType)); + + lock = _hx509_collector_get_lock(c); + + ret = hx509_cms_unenvelope(context, + _hx509_lock_unlock_certs(lock), + 0, + data, length, + NULL, + 0, + &contentType, + &content); + if (ret) { + hx509_set_error_string(context, HX509_ERROR_APPEND, ret, + "PKCS12 failed to unenvelope"); + return ret; + } + + if (der_heim_oid_cmp(&contentType, oid_id_pkcs7_data()) == 0) + ret = parse_safe_content(context, c, content.data, content.length); + + der_free_octet_string(&content); + der_free_oid(&contentType); + + return ret; +} + + +struct type bagtypes[] = { + { oid_id_pkcs12_keyBag, keyBag_parser }, + { oid_id_pkcs12_pkcs8ShroudedKeyBag, ShroudedKeyBag_parser }, + { oid_id_pkcs12_certBag, certBag_parser }, + { oid_id_pkcs7_data, safeContent_parser }, + { oid_id_pkcs7_encryptedData, encryptedData_parser }, + { oid_id_pkcs7_envelopedData, envelopedData_parser } +}; + +static void +parse_pkcs12_type(hx509_context context, + struct hx509_collector *c, + const heim_oid *oid, + const void *data, size_t length, + const PKCS12_Attributes *attrs) +{ + int i; + + for (i = 0; i < sizeof(bagtypes)/sizeof(bagtypes[0]); i++) + if (der_heim_oid_cmp((*bagtypes[i].oid)(), oid) == 0) + (*bagtypes[i].func)(context, c, data, length, attrs); +} + +static int +p12_init(hx509_context context, + hx509_certs certs, void **data, int flags, + const char *residue, hx509_lock lock) +{ + struct ks_pkcs12 *p12; + size_t len; + void *buf; + PKCS12_PFX pfx; + PKCS12_AuthenticatedSafe as; + int ret, i; + struct hx509_collector *c; + + *data = NULL; + + if (lock == NULL) + lock = _hx509_empty_lock; + + ret = _hx509_collector_alloc(context, lock, &c); + if (ret) + return ret; + + p12 = calloc(1, sizeof(*p12)); + if (p12 == NULL) { + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, "out of memory"); + goto out; + } + + p12->fn = strdup(residue); + if (p12->fn == NULL) { + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, "out of memory"); + goto out; + } + + if (flags & HX509_CERTS_CREATE) { + ret = hx509_certs_init(context, "MEMORY:ks-file-create", + 0, lock, &p12->certs); + if (ret == 0) + *data = p12; + goto out; + } + + ret = rk_undumpdata(residue, &buf, &len); + if (ret) { + hx509_clear_error_string(context); + goto out; + } + + ret = decode_PKCS12_PFX(buf, len, &pfx, NULL); + rk_xfree(buf); + if (ret) { + hx509_set_error_string(context, 0, ret, + "Failed to decode the PFX in %s", residue); + goto out; + } + + if (der_heim_oid_cmp(&pfx.authSafe.contentType, oid_id_pkcs7_data()) != 0) { + free_PKCS12_PFX(&pfx); + ret = EINVAL; + hx509_set_error_string(context, 0, ret, + "PKCS PFX isn't a pkcs7-data container"); + goto out; + } + + if (pfx.authSafe.content == NULL) { + free_PKCS12_PFX(&pfx); + ret = EINVAL; + hx509_set_error_string(context, 0, ret, + "PKCS PFX missing data"); + goto out; + } + + { + heim_octet_string asdata; + + ret = decode_PKCS12_OctetString(pfx.authSafe.content->data, + pfx.authSafe.content->length, + &asdata, + NULL); + free_PKCS12_PFX(&pfx); + if (ret) { + hx509_clear_error_string(context); + goto out; + } + ret = decode_PKCS12_AuthenticatedSafe(asdata.data, + asdata.length, + &as, + NULL); + der_free_octet_string(&asdata); + if (ret) { + hx509_clear_error_string(context); + goto out; + } + } + + for (i = 0; i < as.len; i++) + parse_pkcs12_type(context, + c, + &as.val[i].contentType, + as.val[i].content->data, + as.val[i].content->length, + NULL); + + free_PKCS12_AuthenticatedSafe(&as); + + ret = _hx509_collector_collect_certs(context, c, &p12->certs); + if (ret == 0) + *data = p12; + +out: + _hx509_collector_free(c); + + if (ret && p12) { + if (p12->fn) + free(p12->fn); + if (p12->certs) + hx509_certs_free(&p12->certs); + free(p12); + } + + return ret; +} + +static int +addBag(hx509_context context, + PKCS12_AuthenticatedSafe *as, + const heim_oid *oid, + void *data, + size_t length) +{ + void *ptr; + int ret; + + ptr = realloc(as->val, sizeof(as->val[0]) * (as->len + 1)); + if (ptr == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + as->val = ptr; + + ret = der_copy_oid(oid, &as->val[as->len].contentType); + if (ret) { + hx509_set_error_string(context, 0, ret, "out of memory"); + return ret; + } + + as->val[as->len].content = calloc(1, sizeof(*as->val[0].content)); + if (as->val[as->len].content == NULL) { + der_free_oid(&as->val[as->len].contentType); + hx509_set_error_string(context, 0, ENOMEM, "malloc out of memory"); + return ENOMEM; + } + + as->val[as->len].content->data = data; + as->val[as->len].content->length = length; + + as->len++; + + return 0; +} + +static int +store_func(hx509_context context, void *ctx, hx509_cert c) +{ + PKCS12_AuthenticatedSafe *as = ctx; + PKCS12_OctetString os; + PKCS12_CertBag cb; + size_t size; + int ret; + + memset(&os, 0, sizeof(os)); + memset(&cb, 0, sizeof(cb)); + + os.data = NULL; + os.length = 0; + + ret = hx509_cert_binary(context, c, &os); + if (ret) + return ret; + + ASN1_MALLOC_ENCODE(PKCS12_OctetString, + cb.certValue.data,cb.certValue.length, + &os, &size, ret); + free(os.data); + if (ret) + goto out; + ret = der_copy_oid(oid_id_pkcs_9_at_certTypes_x509(), &cb.certType); + if (ret) { + free_PKCS12_CertBag(&cb); + goto out; + } + ASN1_MALLOC_ENCODE(PKCS12_CertBag, os.data, os.length, + &cb, &size, ret); + free_PKCS12_CertBag(&cb); + if (ret) + goto out; + + ret = addBag(context, as, oid_id_pkcs12_certBag(), os.data, os.length); + + if (_hx509_cert_private_key_exportable(c)) { + hx509_private_key key = _hx509_cert_private_key(c); + PKCS8PrivateKeyInfo pki; + + memset(&pki, 0, sizeof(pki)); + + ret = der_parse_hex_heim_integer("00", &pki.version); + if (ret) + return ret; + ret = _hx509_private_key_oid(context, key, + &pki.privateKeyAlgorithm.algorithm); + if (ret) { + free_PKCS8PrivateKeyInfo(&pki); + return ret; + } + ret = _hx509_private_key_export(context, + _hx509_cert_private_key(c), + &pki.privateKey); + if (ret) { + free_PKCS8PrivateKeyInfo(&pki); + return ret; + } + /* set attribute, oid_id_pkcs_9_at_localKeyId() */ + + ASN1_MALLOC_ENCODE(PKCS8PrivateKeyInfo, os.data, os.length, + &pki, &size, ret); + free_PKCS8PrivateKeyInfo(&pki); + if (ret) + return ret; + + ret = addBag(context, as, oid_id_pkcs12_keyBag(), os.data, os.length); + if (ret) + return ret; + } + +out: + return ret; +} + +static int +p12_store(hx509_context context, + hx509_certs certs, void *data, int flags, hx509_lock lock) +{ + struct ks_pkcs12 *p12 = data; + PKCS12_PFX pfx; + PKCS12_AuthenticatedSafe as; + PKCS12_OctetString asdata; + size_t size; + int ret; + + memset(&as, 0, sizeof(as)); + memset(&pfx, 0, sizeof(pfx)); + + ret = hx509_certs_iter(context, p12->certs, store_func, &as); + if (ret) + goto out; + + ASN1_MALLOC_ENCODE(PKCS12_AuthenticatedSafe, asdata.data, asdata.length, + &as, &size, ret); + free_PKCS12_AuthenticatedSafe(&as); + if (ret) + return ret; + + ret = der_parse_hex_heim_integer("03", &pfx.version); + if (ret) { + free(asdata.data); + goto out; + } + + pfx.authSafe.content = calloc(1, sizeof(*pfx.authSafe.content)); + + ASN1_MALLOC_ENCODE(PKCS12_OctetString, + pfx.authSafe.content->data, + pfx.authSafe.content->length, + &asdata, &size, ret); + free(asdata.data); + if (ret) + goto out; + + ret = der_copy_oid(oid_id_pkcs7_data(), &pfx.authSafe.contentType); + if (ret) + goto out; + + ASN1_MALLOC_ENCODE(PKCS12_PFX, asdata.data, asdata.length, + &pfx, &size, ret); + if (ret) + goto out; + +#if 0 + const struct _hx509_password *pw; + + pw = _hx509_lock_get_passwords(lock); + if (pw != NULL) { + pfx.macData = calloc(1, sizeof(*pfx.macData)); + if (pfx.macData == NULL) { + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, "malloc out of memory"); + return ret; + } + if (pfx.macData == NULL) { + free(asdata.data); + goto out; + } + } + ret = calculate_hash(&aspath, pw, pfx.macData); +#endif + + rk_dumpdata(p12->fn, asdata.data, asdata.length); + free(asdata.data); + +out: + free_PKCS12_AuthenticatedSafe(&as); + free_PKCS12_PFX(&pfx); + + return ret; +} + + +static int +p12_free(hx509_certs certs, void *data) +{ + struct ks_pkcs12 *p12 = data; + hx509_certs_free(&p12->certs); + free(p12->fn); + free(p12); + return 0; +} + +static int +p12_add(hx509_context context, hx509_certs certs, void *data, hx509_cert c) +{ + struct ks_pkcs12 *p12 = data; + return hx509_certs_add(context, p12->certs, c); +} + +static int +p12_iter_start(hx509_context context, + hx509_certs certs, + void *data, + void **cursor) +{ + struct ks_pkcs12 *p12 = data; + return hx509_certs_start_seq(context, p12->certs, cursor); +} + +static int +p12_iter(hx509_context context, + hx509_certs certs, + void *data, + void *cursor, + hx509_cert *cert) +{ + struct ks_pkcs12 *p12 = data; + return hx509_certs_next_cert(context, p12->certs, cursor, cert); +} + +static int +p12_iter_end(hx509_context context, + hx509_certs certs, + void *data, + void *cursor) +{ + struct ks_pkcs12 *p12 = data; + return hx509_certs_end_seq(context, p12->certs, cursor); +} + +static struct hx509_keyset_ops keyset_pkcs12 = { + "PKCS12", + 0, + p12_init, + p12_store, + p12_free, + p12_add, + NULL, + p12_iter_start, + p12_iter, + p12_iter_end +}; + +void +_hx509_ks_pkcs12_register(hx509_context context) +{ + _hx509_ks_register(context, &keyset_pkcs12); +} diff --git a/source4/heimdal/lib/hx509/lock.c b/source4/heimdal/lib/hx509/lock.c new file mode 100644 index 0000000000..df1acea042 --- /dev/null +++ b/source4/heimdal/lib/hx509/lock.c @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2005 - 2006 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hx_locl.h" +RCSID("$Id$"); + +/** + * @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; + hx509_certs certs; + hx509_prompter_fct prompt; + void *prompt_data; +}; + +static struct hx509_lock_data empty_lock_data = { + { 0, NULL } +}; + +hx509_lock _hx509_empty_lock = &empty_lock_data; + +/* + * + */ + +int +hx509_lock_init(hx509_context context, hx509_lock *lock) +{ + hx509_lock l; + int ret; + + *lock = NULL; + + l = calloc(1, sizeof(*l)); + if (l == NULL) + return ENOMEM; + + ret = hx509_certs_init(context, + "MEMORY:locks-internal", + 0, + NULL, + &l->certs); + if (ret) { + free(l); + return ret; + } + + *lock = l; + + return 0; +} + +int +hx509_lock_add_password(hx509_lock lock, const char *password) +{ + void *d; + char *s; + + s = strdup(password); + if (s == NULL) + return ENOMEM; + + d = realloc(lock->password.val, + (lock->password.len + 1) * sizeof(lock->password.val[0])); + if (d == NULL) { + free(s); + return ENOMEM; + } + lock->password.val = d; + lock->password.val[lock->password.len] = s; + lock->password.len++; + + return 0; +} + +const struct _hx509_password * +_hx509_lock_get_passwords(hx509_lock lock) +{ + return &lock->password; +} + +hx509_certs +_hx509_lock_unlock_certs(hx509_lock lock) +{ + return lock->certs; +} + +void +hx509_lock_reset_passwords(hx509_lock lock) +{ + int i; + for (i = 0; i < lock->password.len; i++) + free(lock->password.val[i]); + free(lock->password.val); + lock->password.val = NULL; + lock->password.len = 0; +} + +int +hx509_lock_add_cert(hx509_context context, hx509_lock lock, hx509_cert cert) +{ + return hx509_certs_add(context, lock->certs, cert); +} + +int +hx509_lock_add_certs(hx509_context context, hx509_lock lock, hx509_certs certs) +{ + return hx509_certs_merge(context, lock->certs, certs); +} + +void +hx509_lock_reset_certs(hx509_context context, hx509_lock lock) +{ + hx509_certs certs = lock->certs; + int ret; + + ret = hx509_certs_init(context, + "MEMORY:locks-internal", + 0, + NULL, + &lock->certs); + if (ret == 0) + hx509_certs_free(&certs); + else + lock->certs = certs; +} + +int +_hx509_lock_find_cert(hx509_lock lock, const hx509_query *q, hx509_cert *c) +{ + *c = NULL; + return 0; +} + +int +hx509_lock_set_prompter(hx509_lock lock, hx509_prompter_fct prompt, void *data) +{ + lock->prompt = prompt; + lock->prompt_data = data; + return 0; +} + +void +hx509_lock_reset_promper(hx509_lock lock) +{ + lock->prompt = NULL; + lock->prompt_data = NULL; +} + +static int +default_prompter(void *data, const hx509_prompt *prompter) +{ + if (hx509_prompt_hidden(prompter->type)) { + if(UI_UTIL_read_pw_string(prompter->reply.data, + prompter->reply.length, + prompter->prompt, + 0)) + return 1; + } else { + char *s = prompter->reply.data; + + fputs (prompter->prompt, stdout); + fflush (stdout); + if(fgets(prompter->reply.data, + prompter->reply.length, + stdin) == NULL) + return 1; + s[strcspn(s, "\n")] = '\0'; + } + return 0; +} + +int +hx509_lock_prompt(hx509_lock lock, hx509_prompt *prompt) +{ + if (lock->prompt == NULL) + return HX509_CRYPTO_NO_PROMPTER; + return (*lock->prompt)(lock->prompt_data, prompt); +} + +void +hx509_lock_free(hx509_lock lock) +{ + hx509_certs_free(&lock->certs); + hx509_lock_reset_passwords(lock); + memset(lock, 0, sizeof(*lock)); + free(lock); +} + +int +hx509_prompt_hidden(hx509_prompt_type type) +{ + /* default to hidden if unknown */ + + switch (type) { + case HX509_PROMPT_TYPE_QUESTION: + case HX509_PROMPT_TYPE_INFO: + return 0; + default: + return 1; + } +} + +int +hx509_lock_command_string(hx509_lock lock, const char *string) +{ + if (strncasecmp(string, "PASS:", 5) == 0) { + hx509_lock_add_password(lock, string + 5); + } else if (strcasecmp(string, "PROMPT") == 0) { + hx509_lock_set_prompter(lock, default_prompter, NULL); + } else + return HX509_UNKNOWN_LOCK_COMMAND; + return 0; +} diff --git a/source4/heimdal/lib/hx509/name.c b/source4/heimdal/lib/hx509/name.c new file mode 100644 index 0000000000..a34e09e847 --- /dev/null +++ b/source4/heimdal/lib/hx509/name.c @@ -0,0 +1,991 @@ +/* + * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hx_locl.h" +#include <wind.h> +RCSID("$Id$"); + +/** + * @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 }, + { "DC", oid_id_domainComponent }, + { "L", oid_id_at_localityName }, + { "O", oid_id_at_organizationName }, + { "OU", oid_id_at_organizationalUnitName }, + { "S", oid_id_at_stateOrProvinceName }, + { "STREET", oid_id_at_streetAddress }, + { "UID", oid_id_Userid }, + { "emailAddress", oid_id_pkcs9_emailAddress }, + { "serialNumber", oid_id_at_serialNumber } +}; + +static char * +quote_string(const char *f, size_t len, size_t *rlen) +{ + size_t i, j, tolen; + const char *from = f; + char *to; + + tolen = len * 3 + 1; + to = malloc(tolen); + if (to == NULL) + return NULL; + + for (i = 0, j = 0; i < len; i++) { + if (from[i] == ' ' && i + 1 < len) + to[j++] = from[i]; + else if (from[i] == ',' || from[i] == '=' || from[i] == '+' || + from[i] == '<' || from[i] == '>' || from[i] == '#' || + from[i] == ';' || from[i] == ' ') + { + to[j++] = '\\'; + to[j++] = from[i]; + } else if (((unsigned char)from[i]) >= 32 && ((unsigned char)from[i]) <= 127) { + to[j++] = from[i]; + } else { + int l = snprintf(&to[j], tolen - j - 1, + "#%02x", (unsigned char)from[i]); + j += l; + } + } + to[j] = '\0'; + assert(j < tolen); + *rlen = j; + return to; +} + + +static int +append_string(char **str, size_t *total_len, const char *ss, + size_t len, int quote) +{ + char *s, *qs; + + if (quote) + qs = quote_string(ss, len, &len); + else + qs = rk_UNCONST(ss); + + s = realloc(*str, len + *total_len + 1); + if (s == NULL) + _hx509_abort("allocation failure"); /* XXX */ + memcpy(s + *total_len, qs, len); + if (qs != ss) + free(qs); + s[*total_len + len] = '\0'; + *str = s; + *total_len += len; + return 0; +} + +static char * +oidtostring(const heim_oid *type) +{ + char *s; + size_t i; + + for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) { + if (der_heim_oid_cmp((*no[i].o)(), type) == 0) + return strdup(no[i].n); + } + if (der_print_heim_oid(type, '.', &s) != 0) + return NULL; + return s; +} + +static int +stringtooid(const char *name, size_t len, heim_oid *oid) +{ + int i, ret; + char *s; + + memset(oid, 0, sizeof(*oid)); + + for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) { + if (strncasecmp(no[i].n, name, len) == 0) + return der_copy_oid((*no[i].o)(), oid); + } + s = malloc(len + 1); + if (s == NULL) + return ENOMEM; + memcpy(s, name, len); + s[len] = '\0'; + ret = der_parse_heim_oid(s, ".", oid); + free(s); + 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) +{ + return _hx509_Name_to_string(&name->der_name, str); +} + +int +_hx509_Name_to_string(const Name *n, char **str) +{ + size_t total_len = 0; + int i, j; + + *str = strdup(""); + if (*str == NULL) + return ENOMEM; + + for (i = n->u.rdnSequence.len - 1 ; i >= 0 ; i--) { + int len; + + for (j = 0; j < n->u.rdnSequence.val[i].len; j++) { + DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value; + char *oidname; + char *ss; + + oidname = oidtostring(&n->u.rdnSequence.val[i].val[j].type); + + switch(ds->element) { + case choice_DirectoryString_ia5String: + ss = ds->u.ia5String; + break; + case choice_DirectoryString_printableString: + ss = ds->u.printableString; + break; + case choice_DirectoryString_utf8String: + ss = ds->u.utf8String; + break; + case choice_DirectoryString_bmpString: { + uint16_t *bmp = ds->u.bmpString.data; + size_t bmplen = ds->u.bmpString.length; + size_t k; + + ss = malloc(bmplen + 1); + if (ss == NULL) + _hx509_abort("allocation failure"); /* XXX */ + for (k = 0; k < bmplen; k++) + ss[k] = bmp[k] & 0xff; /* XXX */ + ss[k] = '\0'; + break; + } + case choice_DirectoryString_teletexString: + ss = malloc(ds->u.teletexString.length + 1); + if (ss == NULL) + _hx509_abort("allocation failure"); /* XXX */ + memcpy(ss, ds->u.teletexString.data, ds->u.teletexString.length); + ss[ds->u.teletexString.length] = '\0'; + break; + case choice_DirectoryString_universalString: { + uint32_t *uni = ds->u.universalString.data; + size_t unilen = ds->u.universalString.length; + size_t k; + + ss = malloc(unilen + 1); + if (ss == NULL) + _hx509_abort("allocation failure"); /* XXX */ + for (k = 0; k < unilen; k++) + ss[k] = uni[k] & 0xff; /* XXX */ + ss[k] = '\0'; + break; + } + default: + _hx509_abort("unknown directory type: %d", ds->element); + exit(1); + } + append_string(str, &total_len, oidname, strlen(oidname), 0); + free(oidname); + append_string(str, &total_len, "=", 1, 0); + len = strlen(ss); + append_string(str, &total_len, ss, len, 1); + if (ds->element == choice_DirectoryString_universalString || + ds->element == choice_DirectoryString_bmpString || + ds->element == choice_DirectoryString_teletexString) + { + free(ss); + } + if (j + 1 < n->u.rdnSequence.val[i].len) + append_string(str, &total_len, "+", 1, 0); + } + + if (i > 0) + append_string(str, &total_len, ",", 1, 0); + } + return 0; +} + +#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] + + +#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) +{ + wind_profile_flags flags = 0; + size_t i, len; + int ret; + uint32_t *name; + + *rname = NULL; + *rlen = 0; + + switch(ds->element) { + case choice_DirectoryString_ia5String: + COPYCHARARRAY(ds, ia5String, len, name); + break; + case choice_DirectoryString_printableString: + flags = WIND_PROFILE_LDAP_CASE_EXACT_ATTRIBUTE; + COPYCHARARRAY(ds, printableString, len, name); + break; + case choice_DirectoryString_teletexString: + COPYVOIDARRAY(ds, teletexString, len, name); + break; + case choice_DirectoryString_bmpString: + COPYVALARRAY(ds, bmpString, len, name); + break; + case choice_DirectoryString_universalString: + COPYVALARRAY(ds, universalString, len, name); + break; + 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: + _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; + } + + 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, int *c) +{ + int ret, i, j; + + *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 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 0; + + 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) +{ + int ret, diff; + ret = _hx509_name_cmp(&n1->der_name, &n2->der_name, &diff); + if (ret) + return ret; + return diff; +} + + +int +_hx509_name_from_Name(const Name *n, hx509_name *name) +{ + int ret; + *name = calloc(1, sizeof(**name)); + if (*name == NULL) + return ENOMEM; + ret = copy_Name(n, &(*name)->der_name); + if (ret) { + free(*name); + *name = NULL; + } + return ret; +} + +int +_hx509_name_modify(hx509_context context, + Name *name, + int append, + const heim_oid *oid, + const char *str) +{ + RelativeDistinguishedName *rdn; + int ret; + void *ptr; + + ptr = realloc(name->u.rdnSequence.val, + sizeof(name->u.rdnSequence.val[0]) * + (name->u.rdnSequence.len + 1)); + if (ptr == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "Out of memory"); + return ENOMEM; + } + name->u.rdnSequence.val = ptr; + + if (append) { + rdn = &name->u.rdnSequence.val[name->u.rdnSequence.len]; + } else { + memmove(&name->u.rdnSequence.val[1], + &name->u.rdnSequence.val[0], + name->u.rdnSequence.len * + sizeof(name->u.rdnSequence.val[0])); + + rdn = &name->u.rdnSequence.val[0]; + } + rdn->val = malloc(sizeof(rdn->val[0])); + if (rdn->val == NULL) + return ENOMEM; + rdn->len = 1; + ret = der_copy_oid(oid, &rdn->val[0].type); + if (ret) + return ret; + rdn->val[0].value.element = choice_DirectoryString_utf8String; + rdn->val[0].value.u.utf8String = strdup(str); + if (rdn->val[0].value.u.utf8String == NULL) + return ENOMEM; + name->u.rdnSequence.len += 1; + + 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) +{ + const char *p, *q; + size_t len; + hx509_name n; + int ret; + + *name = NULL; + + n = calloc(1, sizeof(*n)); + if (n == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + + n->der_name.element = choice_Name_rdnSequence; + + p = str; + + while (p != NULL && *p != '\0') { + heim_oid oid; + int last; + + q = strchr(p, ','); + if (q) { + len = (q - p); + last = 1; + } else { + len = strlen(p); + last = 0; + } + + q = strchr(p, '='); + if (q == NULL) { + ret = HX509_PARSING_NAME_FAILED; + hx509_set_error_string(context, 0, ret, "missing = in %s", p); + goto out; + } + if (q == p) { + ret = HX509_PARSING_NAME_FAILED; + hx509_set_error_string(context, 0, ret, + "missing name before = in %s", p); + goto out; + } + + if ((q - p) > len) { + ret = HX509_PARSING_NAME_FAILED; + hx509_set_error_string(context, 0, ret, " = after , in %s", p); + goto out; + } + + ret = stringtooid(p, q - p, &oid); + if (ret) { + ret = HX509_PARSING_NAME_FAILED; + hx509_set_error_string(context, 0, ret, + "unknown type: %.*s", (int)(q - p), p); + goto out; + } + + { + size_t pstr_len = len - (q - p) - 1; + const char *pstr = p + (q - p) + 1; + char *r; + + r = malloc(pstr_len + 1); + if (r == NULL) { + der_free_oid(&oid); + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, "out of memory"); + goto out; + } + memcpy(r, pstr, pstr_len); + r[pstr_len] = '\0'; + + ret = _hx509_name_modify(context, &n->der_name, 0, &oid, r); + free(r); + der_free_oid(&oid); + if(ret) + goto out; + } + p += len + last; + } + + *name = n; + + return 0; +out: + hx509_name_free(&n); + 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) +{ + int ret; + + *to = calloc(1, sizeof(**to)); + if (*to == NULL) + return ENOMEM; + ret = copy_Name(&from->der_name, &(*to)->der_name); + if (ret) { + free(*to); + *to = NULL; + return ENOMEM; + } + 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) +{ + return copy_Name(&from->der_name, to); +} + +int +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, + hx509_env env) +{ + Name *n = &name->der_name; + int i, j; + + if (env == NULL) + return 0; + + if (n->element != choice_Name_rdnSequence) { + hx509_set_error_string(context, 0, EINVAL, "RDN not of supported type"); + return EINVAL; + } + + 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]; + normalize COMP to utf8 + check if there are variables + expand variables + convert back to orignal format, store in COMP + free normalized utf8 string + */ + DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value; + char *p, *p2; + struct rk_strpool *strpool = NULL; + + if (ds->element != choice_DirectoryString_utf8String) { + hx509_set_error_string(context, 0, EINVAL, "unsupported type"); + return EINVAL; + } + p = strstr(ds->u.utf8String, "${"); + if (p) { + strpool = rk_strpoolprintf(strpool, "%.*s", + (int)(p - ds->u.utf8String), + ds->u.utf8String); + if (strpool == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + } + while (p != NULL) { + /* expand variables */ + const char *value; + p2 = strchr(p, '}'); + if (p2 == NULL) { + hx509_set_error_string(context, 0, EINVAL, "missing }"); + rk_strpoolfree(strpool); + return EINVAL; + } + p += 2; + value = hx509_env_lfind(context, env, p, p2 - p); + if (value == NULL) { + hx509_set_error_string(context, 0, EINVAL, + "variable %.*s missing", + (int)(p2 - p), p); + rk_strpoolfree(strpool); + return EINVAL; + } + strpool = rk_strpoolprintf(strpool, "%s", value); + if (strpool == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + p2++; + + p = strstr(p2, "${"); + if (p) + strpool = rk_strpoolprintf(strpool, "%.*s", + (int)(p - p2), p2); + else + strpool = rk_strpoolprintf(strpool, "%s", p2); + if (strpool == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + } + if (strpool) { + free(ds->u.utf8String); + ds->u.utf8String = rk_strpoolcollect(strpool); + if (ds->u.utf8String == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + } + } + } + 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) +{ + free_Name(&(*name)->der_name); + memset(*name, 0, sizeof(**name)); + free(*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) +{ + Name name; + int ret; + + *str = NULL; + + ret = decode_Name(data, length, &name, NULL); + if (ret) + return ret; + 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_binary(const hx509_name name, heim_octet_string *os) +{ + size_t size; + int ret; + + ASN1_MALLOC_ENCODE(Name, os->data, os->length, &name->der_name, &size, ret); + if (ret) + return ret; + if (os->length != size) + _hx509_abort("internal ASN.1 encoder error"); + + return 0; +} + +int +_hx509_unparse_Name(const Name *aname, char **str) +{ + hx509_name name; + int ret; + + ret = _hx509_name_from_Name(aname, &name); + if (ret) + return ret; + + ret = hx509_name_to_string(name, str); + hx509_name_free(&name); + 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 hx509_get_error_string(). + * + * @ingroup hx509_name + */ + +int +hx509_general_name_unparse(GeneralName *name, char **str) +{ + struct rk_strpool *strpool = NULL; + + *str = NULL; + + switch (name->element) { + case choice_GeneralName_otherName: { + char *str; + hx509_oid_sprint(&name->u.otherName.type_id, &str); + if (str == NULL) + return ENOMEM; + strpool = rk_strpoolprintf(strpool, "otherName: %s", str); + free(str); + break; + } + case choice_GeneralName_rfc822Name: + strpool = rk_strpoolprintf(strpool, "rfc822Name: %s\n", + name->u.rfc822Name); + break; + case choice_GeneralName_dNSName: + strpool = rk_strpoolprintf(strpool, "dNSName: %s\n", + name->u.dNSName); + break; + case choice_GeneralName_directoryName: { + Name dir; + char *s; + int ret; + memset(&dir, 0, sizeof(dir)); + dir.element = name->u.directoryName.element; + dir.u.rdnSequence = name->u.directoryName.u.rdnSequence; + ret = _hx509_unparse_Name(&dir, &s); + if (ret) + return ret; + strpool = rk_strpoolprintf(strpool, "directoryName: %s", s); + free(s); + break; + } + case choice_GeneralName_uniformResourceIdentifier: + strpool = rk_strpoolprintf(strpool, "URI: %s", + name->u.uniformResourceIdentifier); + break; + case choice_GeneralName_iPAddress: { + unsigned char *a = name->u.iPAddress.data; + + strpool = rk_strpoolprintf(strpool, "IPAddress: "); + if (strpool == NULL) + break; + if (name->u.iPAddress.length == 4) + strpool = rk_strpoolprintf(strpool, "%d.%d.%d.%d", + a[0], a[1], a[2], a[3]); + else if (name->u.iPAddress.length == 16) + strpool = rk_strpoolprintf(strpool, + "%02X:%02X:%02X:%02X:" + "%02X:%02X:%02X:%02X:" + "%02X:%02X:%02X:%02X:" + "%02X:%02X:%02X:%02X", + a[0], a[1], a[2], a[3], + a[4], a[5], a[6], a[7], + a[8], a[9], a[10], a[11], + a[12], a[13], a[14], a[15]); + else + strpool = rk_strpoolprintf(strpool, + "unknown IP address of length %lu", + (unsigned long)name->u.iPAddress.length); + break; + } + case choice_GeneralName_registeredID: { + char *str; + hx509_oid_sprint(&name->u.registeredID, &str); + if (str == NULL) + return ENOMEM; + strpool = rk_strpoolprintf(strpool, "registeredID: %s", str); + free(str); + break; + } + default: + return EINVAL; + } + if (strpool == NULL) + return ENOMEM; + + *str = rk_strpoolcollect(strpool); + + return 0; +} diff --git a/source4/heimdal/lib/hx509/ocsp.asn1 b/source4/heimdal/lib/hx509/ocsp.asn1 new file mode 100644 index 0000000000..eb090a4cc7 --- /dev/null +++ b/source4/heimdal/lib/hx509/ocsp.asn1 @@ -0,0 +1,113 @@ +-- From rfc2560 +-- $Id$ +OCSP DEFINITIONS EXPLICIT TAGS::= + +BEGIN + +IMPORTS + Certificate, AlgorithmIdentifier, CRLReason, + Name, GeneralName, CertificateSerialNumber, Extensions + FROM rfc2459; + +OCSPVersion ::= INTEGER { ocsp-v1(0) } + +OCSPCertStatus ::= CHOICE { + good [0] IMPLICIT NULL, + revoked [1] IMPLICIT -- OCSPRevokedInfo -- SEQUENCE { + revocationTime GeneralizedTime, + revocationReason[0] EXPLICIT CRLReason OPTIONAL + }, + unknown [2] IMPLICIT NULL } + +OCSPCertID ::= SEQUENCE { + hashAlgorithm AlgorithmIdentifier, + issuerNameHash OCTET STRING, -- Hash of Issuer's DN + issuerKeyHash OCTET STRING, -- Hash of Issuers public key + serialNumber CertificateSerialNumber } + +OCSPSingleResponse ::= SEQUENCE { + certID OCSPCertID, + certStatus OCSPCertStatus, + thisUpdate GeneralizedTime, + nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, + singleExtensions [1] EXPLICIT Extensions OPTIONAL } + +OCSPInnerRequest ::= SEQUENCE { + reqCert OCSPCertID, + singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL } + +OCSPTBSRequest ::= SEQUENCE { + version [0] EXPLICIT OCSPVersion -- DEFAULT v1 -- OPTIONAL, + requestorName [1] EXPLICIT GeneralName OPTIONAL, + requestList SEQUENCE OF OCSPInnerRequest, + requestExtensions [2] EXPLICIT Extensions OPTIONAL } + +OCSPSignature ::= SEQUENCE { + signatureAlgorithm AlgorithmIdentifier, + signature BIT STRING, + certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } + +OCSPRequest ::= SEQUENCE { + tbsRequest OCSPTBSRequest, + optionalSignature [0] EXPLICIT OCSPSignature OPTIONAL } + +OCSPResponseBytes ::= SEQUENCE { + responseType OBJECT IDENTIFIER, + response OCTET STRING } + +OCSPResponseStatus ::= ENUMERATED { + successful (0), --Response has valid confirmations + malformedRequest (1), --Illegal confirmation request + internalError (2), --Internal error in issuer + tryLater (3), --Try again later + --(4) is not used + sigRequired (5), --Must sign the request + unauthorized (6) --Request unauthorized +} + +OCSPResponse ::= SEQUENCE { + responseStatus OCSPResponseStatus, + responseBytes [0] EXPLICIT OCSPResponseBytes OPTIONAL } + +OCSPKeyHash ::= OCTET STRING --SHA-1 hash of responder's public key + --(excluding the tag and length fields) + +OCSPResponderID ::= CHOICE { + byName [1] Name, + byKey [2] OCSPKeyHash } + +OCSPResponseData ::= SEQUENCE { + version [0] EXPLICIT OCSPVersion -- DEFAULT v1 -- OPTIONAL, + responderID OCSPResponderID, + producedAt GeneralizedTime, + responses SEQUENCE OF OCSPSingleResponse, + responseExtensions [1] EXPLICIT Extensions OPTIONAL } + +OCSPBasicOCSPResponse ::= SEQUENCE { + tbsResponseData OCSPResponseData, + signatureAlgorithm AlgorithmIdentifier, + signature BIT STRING, + certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } + +-- ArchiveCutoff ::= GeneralizedTime + +-- AcceptableResponses ::= SEQUENCE OF OBJECT IDENTIFIER + +-- Object Identifiers + +id-pkix-ocsp OBJECT IDENTIFIER ::= { + iso(1) identified-organization(3) dod(6) internet(1) + security(5) mechanisms(5) pkix(7) pkix-ad(48) 1 +} + +id-pkix-ocsp-basic OBJECT IDENTIFIER ::= { id-pkix-ocsp 1 } +id-pkix-ocsp-nonce OBJECT IDENTIFIER ::= { id-pkix-ocsp 2 } +-- id-pkix-ocsp-crl OBJECT IDENTIFIER ::= { id-pkix-ocsp 3 } +-- id-pkix-ocsp-response OBJECT IDENTIFIER ::= { id-pkix-ocsp 4 } +-- id-pkix-ocsp-nocheck OBJECT IDENTIFIER ::= { id-pkix-ocsp 5 } +-- id-pkix-ocsp-archive-cutoff OBJECT IDENTIFIER ::= { id-pkix-ocsp 6 } +-- id-pkix-ocsp-service-locator OBJECT IDENTIFIER ::= { id-pkix-ocsp 7 } + + +END + diff --git a/source4/heimdal/lib/hx509/peer.c b/source4/heimdal/lib/hx509/peer.c new file mode 100644 index 0000000000..9845ce051f --- /dev/null +++ b/source4/heimdal/lib/hx509/peer.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hx_locl.h" +RCSID("$Id$"); + +/** + * @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) +{ + *peer = calloc(1, sizeof(**peer)); + if (*peer == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + return 0; +} + + +static void +free_cms_alg(hx509_peer_info peer) +{ + if (peer->val) { + size_t i; + for (i = 0; i < peer->len; i++) + free_AlgorithmIdentifier(&peer->val[i]); + free(peer->val); + peer->val = NULL; + peer->len = 0; + } +} + +/** + * Free a peer info structure. + * + * @param peer peer info to be freed. + * + * @ingroup hx509_peer + */ + +void +hx509_peer_info_free(hx509_peer_info peer) +{ + if (peer == NULL) + return; + if (peer->cert) + hx509_cert_free(peer->cert); + free_cms_alg(peer); + memset(peer, 0, sizeof(*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) +{ + if (peer->cert) + hx509_cert_free(peer->cert); + peer->cert = hx509_cert_ref(cert); + 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, + const AlgorithmIdentifier *val, + size_t len) +{ + size_t i; + + free_cms_alg(peer); + + peer->val = calloc(len, sizeof(*peer->val)); + if (peer->val == NULL) { + peer->len = 0; + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + peer->len = len; + for (i = 0; i < len; i++) { + int ret; + ret = copy_AlgorithmIdentifier(&val[i], &peer->val[i]); + if (ret) { + hx509_clear_error_string(context); + free_cms_alg(peer); + return ret; + } + } + return 0; +} + +#if 0 + +/* + * S/MIME + */ + +int +hx509_peer_info_parse_smime(hx509_peer_info peer, + const heim_octet_string *data) +{ + return 0; +} + +int +hx509_peer_info_unparse_smime(hx509_peer_info peer, + heim_octet_string *data) +{ + return 0; +} + +/* + * For storing hx509_peer_info to be able to cache them. + */ + +int +hx509_peer_info_parse(hx509_peer_info peer, + const heim_octet_string *data) +{ + return 0; +} + +int +hx509_peer_info_unparse(hx509_peer_info peer, + heim_octet_string *data) +{ + return 0; +} +#endif diff --git a/source4/heimdal/lib/hx509/pkcs10.asn1 b/source4/heimdal/lib/hx509/pkcs10.asn1 new file mode 100644 index 0000000000..f3fe37b1bf --- /dev/null +++ b/source4/heimdal/lib/hx509/pkcs10.asn1 @@ -0,0 +1,25 @@ +-- $Id$ +PKCS10 DEFINITIONS ::= + +BEGIN + +IMPORTS + Name, SubjectPublicKeyInfo, Attribute, AlgorithmIdentifier + FROM rfc2459; + + +CertificationRequestInfo ::= SEQUENCE { + version INTEGER { pkcs10-v1(0) }, + subject Name, + subjectPKInfo SubjectPublicKeyInfo, + attributes [0] IMPLICIT SET OF Attribute OPTIONAL +} + +CertificationRequest ::= SEQUENCE { + certificationRequestInfo CertificationRequestInfo, + signatureAlgorithm AlgorithmIdentifier, + signature BIT STRING +} + +END + diff --git a/source4/heimdal/lib/hx509/print.c b/source4/heimdal/lib/hx509/print.c new file mode 100644 index 0000000000..92d7811909 --- /dev/null +++ b/source4/heimdal/lib/hx509/print.c @@ -0,0 +1,1037 @@ +/* + * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hx_locl.h" +RCSID("$Id$"); + +/** + * @page page_print Hx509 printing functions + * + * See the library functions here: @ref hx509_print + */ + +struct hx509_validate_ctx_data { + int flags; + hx509_vprint_func vprint_func; + void *ctx; +}; + +struct cert_status { + unsigned int selfsigned:1; + unsigned int isca:1; + unsigned int isproxy:1; + unsigned int haveSAN:1; + unsigned int haveIAN:1; + unsigned int haveSKI:1; + unsigned int haveAKI:1; + unsigned int haveCRLDP:1; +}; + + +/* + * + */ + +static int +Time2string(const Time *T, char **str) +{ + time_t t; + char *s; + struct tm *tm; + + *str = NULL; + t = _hx509_Time2time_t(T); + tm = gmtime (&t); + s = malloc(30); + if (s == NULL) + return ENOMEM; + strftime(s, 30, "%Y-%m-%d %H:%M:%S", tm); + *str = s; + 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); +} + +static void +print_func(hx509_vprint_func func, void *ctx, const char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + (*func)(ctx, fmt, va); + 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); + 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; + print_func(func, ctx, "\tlength: %d\n\t", b->length); + for (i = 0; i < (b->length + 7) / 8; i++) + 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) +{ + KeyUsage ku; + char buf[256]; + int ret; + + *s = NULL; + + ret = _hx509_cert_get_keyusage(context, c, &ku); + if (ret) + return ret; + unparse_flags(KeyUsage2int(ku), asn1_KeyUsage_units(), buf, sizeof(buf)); + *s = strdup(buf); + if (*s == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + + return 0; +} + +/* + * + */ + +static void +validate_vprint(void *c, const char *fmt, va_list va) +{ + hx509_validate_ctx ctx = c; + if (ctx->vprint_func == NULL) + return; + (ctx->vprint_func)(ctx->ctx, fmt, va); +} + +static void +validate_print(hx509_validate_ctx ctx, int flags, const char *fmt, ...) +{ + va_list va; + if ((ctx->flags & flags) == 0) + return; + va_start(va, fmt); + validate_vprint(ctx, fmt, va); + va_end(va); +} + +/* + * Dont Care, SHOULD critical, SHOULD NOT critical, MUST critical, + * MUST NOT critical + */ +enum critical_flag { D_C = 0, S_C, S_N_C, M_C, M_N_C }; + +static int +check_Null(hx509_validate_ctx ctx, + struct cert_status *status, + enum critical_flag cf, const Extension *e) +{ + switch(cf) { + case D_C: + break; + case S_C: + if (!e->critical) + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "\tCritical not set on SHOULD\n"); + break; + case S_N_C: + if (e->critical) + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "\tCritical set on SHOULD NOT\n"); + break; + case M_C: + if (!e->critical) + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "\tCritical not set on MUST\n"); + break; + case M_N_C: + if (e->critical) + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "\tCritical set on MUST NOT\n"); + break; + default: + _hx509_abort("internal check_Null state error"); + } + return 0; +} + +static int +check_subjectKeyIdentifier(hx509_validate_ctx ctx, + struct cert_status *status, + enum critical_flag cf, + const Extension *e) +{ + SubjectKeyIdentifier si; + size_t size; + int ret; + + status->haveSKI = 1; + check_Null(ctx, status, cf, e); + + ret = decode_SubjectKeyIdentifier(e->extnValue.data, + e->extnValue.length, + &si, &size); + if (ret) { + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "Decoding SubjectKeyIdentifier failed: %d", ret); + return 1; + } + if (size != e->extnValue.length) { + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "Decoding SKI ahve extra bits on the end"); + return 1; + } + if (si.length == 0) + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "SKI is too short (0 bytes)"); + if (si.length > 20) + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "SKI is too long"); + + { + char *id; + hex_encode(si.data, si.length, &id); + if (id) { + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, + "\tsubject key id: %s\n", id); + free(id); + } + } + + free_SubjectKeyIdentifier(&si); + + return 0; +} + +static int +check_authorityKeyIdentifier(hx509_validate_ctx ctx, + struct cert_status *status, + enum critical_flag cf, + const Extension *e) +{ + AuthorityKeyIdentifier ai; + size_t size; + int ret; + + status->haveAKI = 1; + check_Null(ctx, status, cf, e); + + ret = decode_AuthorityKeyIdentifier(e->extnValue.data, + e->extnValue.length, + &ai, &size); + if (ret) { + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "Decoding AuthorityKeyIdentifier failed: %d", ret); + return 1; + } + if (size != e->extnValue.length) { + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "Decoding SKI ahve extra bits on the end"); + return 1; + } + + if (ai.keyIdentifier) { + char *id; + hex_encode(ai.keyIdentifier->data, ai.keyIdentifier->length, &id); + if (id) { + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, + "\tauthority key id: %s\n", id); + free(id); + } + } + + 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) +{ + KRB5PrincipalName kn; + unsigned i; + size_t size; + int ret; + + ret = decode_KRB5PrincipalName(a->data, a->length, &kn, &size); + if (ret) { + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "Decoding kerberos name in SAN failed: %d", ret); + return 1; + } + + if (size != a->length) { + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "Decoding kerberos name have extra bits on the end"); + return 1; + } + + /* print kerberos principal, add code to quote / within components */ + for (i = 0; i < kn.principalName.name_string.len; i++) { + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "%s", + kn.principalName.name_string.val[i]); + if (i + 1 < kn.principalName.name_string.len) + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "/"); + } + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "@"); + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "%s", kn.realm); + + free_KRB5PrincipalName(&kn); + return 0; +} + +static int +check_utf8_string_san(hx509_validate_ctx ctx, heim_any *a) +{ + PKIXXmppAddr jid; + size_t size; + int ret; + + ret = decode_PKIXXmppAddr(a->data, a->length, &jid, &size); + if (ret) { + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "Decoding JID in SAN failed: %d", ret); + return 1; + } + + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "%s", jid); + free_PKIXXmppAddr(&jid); + + return 0; +} + +static int +check_altnull(hx509_validate_ctx ctx, heim_any *a) +{ + return 0; +} + +static int +check_CRLDistributionPoints(hx509_validate_ctx ctx, + struct cert_status *status, + enum critical_flag cf, + const Extension *e) +{ + CRLDistributionPoints dp; + size_t size; + int ret, i; + + check_Null(ctx, status, cf, e); + + ret = decode_CRLDistributionPoints(e->extnValue.data, + e->extnValue.length, + &dp, &size); + if (ret) { + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "Decoding CRL Distribution Points failed: %d\n", ret); + return 1; + } + + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "CRL Distribution Points:\n"); + for (i = 0 ; i < dp.len; i++) { + if (dp.val[i].distributionPoint) { + DistributionPointName dpname; + heim_any *data = dp.val[i].distributionPoint; + int j; + + ret = decode_DistributionPointName(data->data, data->length, + &dpname, NULL); + if (ret) { + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "Failed to parse CRL Distribution Point Name: %d\n", ret); + continue; + } + + switch (dpname.element) { + case choice_DistributionPointName_fullName: + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "Fullname:\n"); + + for (j = 0 ; j < dpname.u.fullName.len; j++) { + char *s; + GeneralName *name = &dpname.u.fullName.val[j]; + + ret = hx509_general_name_unparse(name, &s); + if (ret == 0 && s != NULL) { + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, " %s\n", s); + free(s); + } + } + break; + case choice_DistributionPointName_nameRelativeToCRLIssuer: + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, + "Unknown nameRelativeToCRLIssuer"); + break; + default: + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "Unknown DistributionPointName"); + break; + } + free_DistributionPointName(&dpname); + } + } + free_CRLDistributionPoints(&dp); + + status->haveCRLDP = 1; + + return 0; +} + + +struct { + const char *name; + const heim_oid *(*oid)(void); + int (*func)(hx509_validate_ctx, heim_any *); +} check_altname[] = { + { "pk-init", oid_id_pkinit_san, check_pkinit_san }, + { "jabber", oid_id_pkix_on_xmppAddr, check_utf8_string_san }, + { "dns-srv", oid_id_pkix_on_dnsSRV, check_altnull }, + { "card-id", oid_id_uspkicommon_card_id, check_altnull }, + { "Microsoft NT-PRINCIPAL-NAME", oid_id_pkinit_ms_san, check_utf8_string_san } +}; + +static int +check_altName(hx509_validate_ctx ctx, + struct cert_status *status, + const char *name, + enum critical_flag cf, + const Extension *e) +{ + GeneralNames gn; + size_t size; + int ret, i; + + check_Null(ctx, status, cf, e); + + if (e->extnValue.length == 0) { + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "%sAltName empty, not allowed", name); + return 1; + } + ret = decode_GeneralNames(e->extnValue.data, e->extnValue.length, + &gn, &size); + if (ret) { + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "\tret = %d while decoding %s GeneralNames\n", + ret, name); + return 1; + } + if (gn.len == 0) { + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "%sAltName generalName empty, not allowed\n", name); + return 1; + } + + for (i = 0; i < gn.len; i++) { + switch (gn.val[i].element) { + case choice_GeneralName_otherName: { + unsigned j; + + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, + "%sAltName otherName ", name); + + for (j = 0; j < sizeof(check_altname)/sizeof(check_altname[0]); j++) { + if (der_heim_oid_cmp((*check_altname[j].oid)(), + &gn.val[i].u.otherName.type_id) != 0) + continue; + + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "%s: ", + check_altname[j].name); + (*check_altname[j].func)(ctx, &gn.val[i].u.otherName.value); + break; + } + if (j == sizeof(check_altname)/sizeof(check_altname[0])) { + hx509_oid_print(&gn.val[i].u.otherName.type_id, + validate_vprint, ctx); + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, " unknown"); + } + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "\n"); + break; + } + default: { + char *s; + ret = hx509_general_name_unparse(&gn.val[i], &s); + if (ret) { + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "ret = %d unparsing GeneralName\n", ret); + return 1; + } + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "%s\n", s); + free(s); + break; + } + } + } + + free_GeneralNames(&gn); + + return 0; +} + +static int +check_subjectAltName(hx509_validate_ctx ctx, + struct cert_status *status, + enum critical_flag cf, + const Extension *e) +{ + status->haveSAN = 1; + return check_altName(ctx, status, "subject", cf, e); +} + +static int +check_issuerAltName(hx509_validate_ctx ctx, + struct cert_status *status, + enum critical_flag cf, + const Extension *e) +{ + status->haveIAN = 1; + return check_altName(ctx, status, "issuer", cf, e); +} + + +static int +check_basicConstraints(hx509_validate_ctx ctx, + struct cert_status *status, + enum critical_flag cf, + const Extension *e) +{ + BasicConstraints b; + size_t size; + int ret; + + check_Null(ctx, status, cf, e); + + ret = decode_BasicConstraints(e->extnValue.data, e->extnValue.length, + &b, &size); + if (ret) { + printf("\tret = %d while decoding BasicConstraints\n", ret); + return 0; + } + if (size != e->extnValue.length) + printf("\tlength of der data isn't same as extension\n"); + + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, + "\tis %sa CA\n", b.cA && *b.cA ? "" : "NOT "); + if (b.pathLenConstraint) + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, + "\tpathLenConstraint: %d\n", *b.pathLenConstraint); + + if (b.cA) { + if (*b.cA) { + if (!e->critical) + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "Is a CA and not BasicConstraints CRITICAL\n"); + status->isca = 1; + } + else + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "cA is FALSE, not allowed to be\n"); + } + free_BasicConstraints(&b); + + return 0; +} + +static int +check_proxyCertInfo(hx509_validate_ctx ctx, + struct cert_status *status, + enum critical_flag cf, + const Extension *e) +{ + check_Null(ctx, status, cf, e); + status->isproxy = 1; + return 0; +} + +static int +check_authorityInfoAccess(hx509_validate_ctx ctx, + struct cert_status *status, + enum critical_flag cf, + const Extension *e) +{ + AuthorityInfoAccessSyntax aia; + size_t size; + int ret, i; + + check_Null(ctx, status, cf, e); + + ret = decode_AuthorityInfoAccessSyntax(e->extnValue.data, + e->extnValue.length, + &aia, &size); + if (ret) { + printf("\tret = %d while decoding AuthorityInfoAccessSyntax\n", ret); + return 0; + } + + for (i = 0; i < aia.len; i++) { + char *str; + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, + "\ttype: "); + hx509_oid_print(&aia.val[i].accessMethod, validate_vprint, ctx); + hx509_general_name_unparse(&aia.val[i].accessLocation, &str); + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, + "\n\tdirname: %s\n", str); + free(str); + } + free_AuthorityInfoAccessSyntax(&aia); + + return 0; +} + +/* + * + */ + +struct { + const char *name; + const heim_oid *(*oid)(void); + int (*func)(hx509_validate_ctx ctx, + struct cert_status *status, + enum critical_flag cf, + const Extension *); + enum critical_flag cf; +} check_extension[] = { +#define ext(name, checkname) #name, &oid_id_x509_ce_##name, check_##checkname + { ext(subjectDirectoryAttributes, Null), M_N_C }, + { ext(subjectKeyIdentifier, subjectKeyIdentifier), M_N_C }, + { ext(keyUsage, Null), S_C }, + { ext(subjectAltName, subjectAltName), M_N_C }, + { ext(issuerAltName, issuerAltName), S_N_C }, + { ext(basicConstraints, basicConstraints), D_C }, + { ext(cRLNumber, Null), M_N_C }, + { ext(cRLReason, Null), M_N_C }, + { ext(holdInstructionCode, Null), M_N_C }, + { ext(invalidityDate, Null), M_N_C }, + { ext(deltaCRLIndicator, Null), M_C }, + { ext(issuingDistributionPoint, Null), M_C }, + { ext(certificateIssuer, Null), M_C }, + { ext(nameConstraints, Null), M_C }, + { ext(cRLDistributionPoints, CRLDistributionPoints), S_N_C }, + { ext(certificatePolicies, Null) }, + { ext(policyMappings, Null), M_N_C }, + { ext(authorityKeyIdentifier, authorityKeyIdentifier), M_N_C }, + { ext(policyConstraints, Null), D_C }, + { ext(extKeyUsage, extKeyUsage), D_C }, + { ext(freshestCRL, Null), M_N_C }, + { ext(inhibitAnyPolicy, Null), M_C }, +#undef ext +#define ext(name, checkname) #name, &oid_id_pkix_pe_##name, check_##checkname + { ext(proxyCertInfo, proxyCertInfo), M_C }, + { ext(authorityInfoAccess, authorityInfoAccess), M_C }, +#undef ext + { "US Fed PKI - PIV Interim", oid_id_uspkicommon_piv_interim, + check_Null, D_C }, + { "Netscape cert comment", oid_id_netscape_cert_comment, + check_Null, D_C }, + { 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) +{ + *ctx = malloc(sizeof(**ctx)); + if (*ctx == NULL) + return ENOMEM; + memset(*ctx, 0, sizeof(**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, + void *c) +{ + ctx->vprint_func = func; + 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, + hx509_cert cert) +{ + Certificate *c = _hx509_get_cert(cert); + TBSCertificate *t = &c->tbsCertificate; + hx509_name issuer, subject; + char *str; + struct cert_status status; + int ret; + + memset(&status, 0, sizeof(status)); + + if (_hx509_cert_get_version(c) != 3) + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, + "Not version 3 certificate\n"); + + if ((t->version == NULL || *t->version < 2) && t->extensions) + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "Not version 3 certificate with extensions\n"); + + if (_hx509_cert_get_version(c) >= 3 && t->extensions == NULL) + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "Version 3 certificate without extensions\n"); + + ret = hx509_cert_get_subject(cert, &subject); + if (ret) abort(); + hx509_name_to_string(subject, &str); + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, + "subject name: %s\n", str); + free(str); + + ret = hx509_cert_get_issuer(cert, &issuer); + if (ret) abort(); + hx509_name_to_string(issuer, &str); + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, + "issuer name: %s\n", str); + free(str); + + if (hx509_name_cmp(subject, issuer) == 0) { + status.selfsigned = 1; + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, + "\tis a self-signed certificate\n"); + } + + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, + "Validity:\n"); + + Time2string(&t->validity.notBefore, &str); + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "\tnotBefore %s\n", str); + free(str); + Time2string(&t->validity.notAfter, &str); + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "\tnotAfter %s\n", str); + free(str); + + if (t->extensions) { + int i, j; + + if (t->extensions->len == 0) { + validate_print(ctx, + HX509_VALIDATE_F_VALIDATE|HX509_VALIDATE_F_VERBOSE, + "The empty extensions list is not " + "allowed by PKIX\n"); + } + + for (i = 0; i < t->extensions->len; i++) { + + for (j = 0; check_extension[j].name; j++) + if (der_heim_oid_cmp((*check_extension[j].oid)(), + &t->extensions->val[i].extnID) == 0) + break; + if (check_extension[j].name == NULL) { + int flags = HX509_VALIDATE_F_VERBOSE; + if (t->extensions->val[i].critical) + flags |= HX509_VALIDATE_F_VALIDATE; + validate_print(ctx, flags, "don't know what "); + if (t->extensions->val[i].critical) + validate_print(ctx, flags, "and is CRITICAL "); + if (ctx->flags & flags) + hx509_oid_print(&t->extensions->val[i].extnID, + validate_vprint, ctx); + validate_print(ctx, flags, " is\n"); + continue; + } + validate_print(ctx, + HX509_VALIDATE_F_VALIDATE|HX509_VALIDATE_F_VERBOSE, + "checking extention: %s\n", + check_extension[j].name); + (*check_extension[j].func)(ctx, + &status, + check_extension[j].cf, + &t->extensions->val[i]); + } + } else + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "no extentions\n"); + + if (status.isca) { + if (!status.haveSKI) + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "CA certificate have no SubjectKeyIdentifier\n"); + + } else { + if (!status.haveAKI) + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "Is not CA and doesn't have " + "AuthorityKeyIdentifier\n"); + } + + + if (!status.haveSKI) + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "Doesn't have SubjectKeyIdentifier\n"); + + if (status.isproxy && status.isca) + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "Proxy and CA at the same time!\n"); + + if (status.isproxy) { + if (status.haveSAN) + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "Proxy and have SAN\n"); + if (status.haveIAN) + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "Proxy and have IAN\n"); + } + + if (hx509_name_is_null_p(subject) && !status.haveSAN) + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "NULL subject DN and doesn't have a SAN\n"); + + if (!status.selfsigned && !status.haveCRLDP) + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "Not a CA nor PROXY and doesn't have" + "CRL Dist Point\n"); + + if (status.selfsigned) { + ret = _hx509_verify_signature_bitstring(context, + c, + &c->signatureAlgorithm, + &c->tbsCertificate._save, + &c->signatureValue); + if (ret == 0) + validate_print(ctx, HX509_VALIDATE_F_VERBOSE, + "Self-signed certificate was self-signed\n"); + else + validate_print(ctx, HX509_VALIDATE_F_VALIDATE, + "Self-signed certificate NOT really self-signed!\n"); + } + + hx509_name_free(&subject); + hx509_name_free(&issuer); + + return 0; +} diff --git a/source4/heimdal/lib/hx509/req.c b/source4/heimdal/lib/hx509/req.c new file mode 100644 index 0000000000..1ffa0a53cf --- /dev/null +++ b/source4/heimdal/lib/hx509/req.c @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2006 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hx_locl.h" +#include <pkcs10_asn1.h> +RCSID("$Id$"); + +struct hx509_request_data { + hx509_name name; + SubjectPublicKeyInfo key; + ExtKeyUsage eku; + GeneralNames san; +}; + +/* + * + */ + +int +_hx509_request_init(hx509_context context, hx509_request *req) +{ + *req = calloc(1, sizeof(**req)); + if (*req == NULL) + return ENOMEM; + + return 0; +} + +void +_hx509_request_free(hx509_request *req) +{ + if ((*req)->name) + hx509_name_free(&(*req)->name); + free_SubjectPublicKeyInfo(&(*req)->key); + free_ExtKeyUsage(&(*req)->eku); + free_GeneralNames(&(*req)->san); + memset(*req, 0, sizeof(**req)); + free(*req); + *req = NULL; +} + +int +_hx509_request_set_name(hx509_context context, + hx509_request req, + hx509_name name) +{ + if (req->name) + hx509_name_free(&req->name); + if (name) { + int ret = hx509_name_copy(context, name, &req->name); + if (ret) + return ret; + } + return 0; +} + +int +_hx509_request_get_name(hx509_context context, + hx509_request req, + hx509_name *name) +{ + if (req->name == NULL) { + hx509_set_error_string(context, 0, EINVAL, "Request have no name"); + return EINVAL; + } + return hx509_name_copy(context, req->name, name); +} + +int +_hx509_request_set_SubjectPublicKeyInfo(hx509_context context, + hx509_request req, + const SubjectPublicKeyInfo *key) +{ + free_SubjectPublicKeyInfo(&req->key); + return copy_SubjectPublicKeyInfo(key, &req->key); +} + +int +_hx509_request_get_SubjectPublicKeyInfo(hx509_context context, + hx509_request req, + SubjectPublicKeyInfo *key) +{ + return copy_SubjectPublicKeyInfo(&req->key, key); +} + +int +_hx509_request_add_eku(hx509_context context, + hx509_request req, + const heim_oid *oid) +{ + void *val; + int ret; + + val = realloc(req->eku.val, sizeof(req->eku.val[0]) * (req->eku.len + 1)); + if (val == NULL) + return ENOMEM; + req->eku.val = val; + + ret = der_copy_oid(oid, &req->eku.val[req->eku.len]); + if (ret) + return ret; + + req->eku.len += 1; + + return 0; +} + +int +_hx509_request_add_dns_name(hx509_context context, + hx509_request req, + const char *hostname) +{ + GeneralName name; + + memset(&name, 0, sizeof(name)); + name.element = choice_GeneralName_dNSName; + name.u.dNSName = rk_UNCONST(hostname); + + return add_GeneralNames(&req->san, &name); +} + +int +_hx509_request_add_email(hx509_context context, + hx509_request req, + const char *email) +{ + GeneralName name; + + memset(&name, 0, sizeof(name)); + name.element = choice_GeneralName_rfc822Name; + name.u.dNSName = rk_UNCONST(email); + + return add_GeneralNames(&req->san, &name); +} + + + +int +_hx509_request_to_pkcs10(hx509_context context, + const hx509_request req, + const hx509_private_key signer, + heim_octet_string *request) +{ + CertificationRequest r; + heim_octet_string data, os; + int ret; + size_t size; + + if (req->name == NULL) { + hx509_set_error_string(context, 0, EINVAL, + "PKCS10 needs to have a subject"); + return EINVAL; + } + + memset(&r, 0, sizeof(r)); + memset(request, 0, sizeof(*request)); + + r.certificationRequestInfo.version = pkcs10_v1; + + ret = copy_Name(&req->name->der_name, + &r.certificationRequestInfo.subject); + if (ret) + goto out; + ret = copy_SubjectPublicKeyInfo(&req->key, + &r.certificationRequestInfo.subjectPKInfo); + if (ret) + goto out; + r.certificationRequestInfo.attributes = + calloc(1, sizeof(*r.certificationRequestInfo.attributes)); + if (r.certificationRequestInfo.attributes == NULL) { + ret = ENOMEM; + goto out; + } + + ASN1_MALLOC_ENCODE(CertificationRequestInfo, data.data, data.length, + &r.certificationRequestInfo, &size, ret); + if (ret) + goto out; + if (data.length != size) + abort(); + + ret = _hx509_create_signature(context, + signer, + _hx509_crypto_default_sig_alg, + &data, + &r.signatureAlgorithm, + &os); + free(data.data); + if (ret) + goto out; + r.signature.data = os.data; + r.signature.length = os.length * 8; + + ASN1_MALLOC_ENCODE(CertificationRequest, data.data, data.length, + &r, &size, ret); + if (ret) + goto out; + if (data.length != size) + abort(); + + *request = data; + +out: + free_CertificationRequest(&r); + + return ret; +} + +int +_hx509_request_parse(hx509_context context, + const char *path, + hx509_request *req) +{ + CertificationRequest r; + CertificationRequestInfo *rinfo; + hx509_name subject; + size_t len, size; + void *p; + int ret; + + if (strncmp(path, "PKCS10:", 7) != 0) { + hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION, + "unsupport type in %s", path); + return HX509_UNSUPPORTED_OPERATION; + } + path += 7; + + /* XXX PEM request */ + + ret = rk_undumpdata(path, &p, &len); + if (ret) { + hx509_set_error_string(context, 0, ret, "Failed to map file %s", path); + return ret; + } + + ret = decode_CertificationRequest(p, len, &r, &size); + rk_xfree(p); + if (ret) { + hx509_set_error_string(context, 0, ret, "Failed to decode %s", path); + return ret; + } + + ret = _hx509_request_init(context, req); + if (ret) { + free_CertificationRequest(&r); + return ret; + } + + rinfo = &r.certificationRequestInfo; + + ret = _hx509_request_set_SubjectPublicKeyInfo(context, *req, + &rinfo->subjectPKInfo); + if (ret) { + free_CertificationRequest(&r); + _hx509_request_free(req); + return ret; + } + + ret = _hx509_name_from_Name(&rinfo->subject, &subject); + if (ret) { + free_CertificationRequest(&r); + _hx509_request_free(req); + return ret; + } + ret = _hx509_request_set_name(context, *req, subject); + hx509_name_free(&subject); + free_CertificationRequest(&r); + if (ret) { + _hx509_request_free(req); + return ret; + } + + return 0; +} + + +int +_hx509_request_print(hx509_context context, hx509_request req, FILE *f) +{ + int ret; + + if (req->name) { + char *subject; + ret = hx509_name_to_string(req->name, &subject); + if (ret) { + hx509_set_error_string(context, 0, ret, "Failed to print name"); + return ret; + } + fprintf(f, "name: %s\n", subject); + free(subject); + } + + return 0; +} + diff --git a/source4/heimdal/lib/hx509/revoke.c b/source4/heimdal/lib/hx509/revoke.c new file mode 100644 index 0000000000..a36ec964d2 --- /dev/null +++ b/source4/heimdal/lib/hx509/revoke.c @@ -0,0 +1,1539 @@ +/* + * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * 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$"); + +struct revoke_crl { + char *path; + time_t last_modfied; + CRLCertificateList crl; + int verified; + int failed_verify; +}; + +struct revoke_ocsp { + char *path; + time_t last_modfied; + OCSPBasicOCSPResponse ocsp; + hx509_certs certs; + hx509_cert signer; +}; + + +struct hx509_revoke_ctx_data { + unsigned int ref; + struct { + struct revoke_crl *val; + size_t len; + } crls; + struct { + struct revoke_ocsp *val; + size_t len; + } 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) +{ + *ctx = calloc(1, sizeof(**ctx)); + if (*ctx == NULL) + return ENOMEM; + + (*ctx)->ref = 1; + (*ctx)->crls.len = 0; + (*ctx)->crls.val = NULL; + (*ctx)->ocsps.len = 0; + (*ctx)->ocsps.val = NULL; + + 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 on ref"); + ctx->ref++; + if (ctx->ref == UINT_MAX) + _hx509_abort("revoke ctx refcount == UINT_MAX on ref"); + return ctx; +} + +static void +free_ocsp(struct revoke_ocsp *ocsp) +{ + free(ocsp->path); + free_OCSPBasicOCSPResponse(&ocsp->ocsp); + hx509_certs_free(&ocsp->certs); + 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) +{ + size_t i ; + + 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); + } + + for (i = 0; i < (*ctx)->ocsps.len; i++) + free_ocsp(&(*ctx)->ocsps.val[i]); + free((*ctx)->ocsps.val); + + free((*ctx)->crls.val); + + memset(*ctx, 0, sizeof(**ctx)); + free(*ctx); + *ctx = NULL; +} + +static int +verify_ocsp(hx509_context context, + struct revoke_ocsp *ocsp, + time_t time_now, + hx509_certs certs, + hx509_cert parent) +{ + hx509_cert signer = NULL; + hx509_query q; + int ret; + + _hx509_query_clear(&q); + + /* + * Need to match on issuer too in case there are two CA that have + * issued the same name to a certificate. One example of this is + * the www.openvalidation.org test's ocsp validator. + */ + + q.match = HX509_QUERY_MATCH_ISSUER_NAME; + q.issuer_name = &_hx509_get_cert(parent)->tbsCertificate.issuer; + + switch(ocsp->ocsp.tbsResponseData.responderID.element) { + case choice_OCSPResponderID_byName: + q.match |= HX509_QUERY_MATCH_SUBJECT_NAME; + q.subject_name = &ocsp->ocsp.tbsResponseData.responderID.u.byName; + break; + case choice_OCSPResponderID_byKey: + q.match |= HX509_QUERY_MATCH_KEY_HASH_SHA1; + q.keyhash_sha1 = &ocsp->ocsp.tbsResponseData.responderID.u.byKey; + break; + } + + ret = hx509_certs_find(context, certs, &q, &signer); + if (ret && ocsp->certs) + ret = hx509_certs_find(context, ocsp->certs, &q, &signer); + if (ret) + goto out; + + /* + * If signer certificate isn't the CA certificate, lets check the + * it is the CA that signed the signer certificate and the OCSP EKU + * is set. + */ + 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 OCSP 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); + if (ret) { + hx509_set_error_string(context, HX509_ERROR_APPEND, ret, + "OCSP signer signature invalid"); + goto out; + } + + ret = hx509_cert_check_eku(context, signer, + oid_id_pkix_kp_OCSPSigning(), 0); + if (ret) + goto out; + } + + ret = _hx509_verify_signature_bitstring(context, + _hx509_get_cert(signer), + &ocsp->ocsp.signatureAlgorithm, + &ocsp->ocsp.tbsResponseData._save, + &ocsp->ocsp.signature); + if (ret) { + hx509_set_error_string(context, HX509_ERROR_APPEND, ret, + "OCSP signature invalid"); + goto out; + } + + ocsp->signer = signer; + signer = NULL; +out: + if (signer) + hx509_cert_free(signer); + + return ret; +} + +/* + * + */ + +static int +parse_ocsp_basic(const void *data, size_t length, OCSPBasicOCSPResponse *basic) +{ + OCSPResponse resp; + size_t size; + int ret; + + memset(basic, 0, sizeof(*basic)); + + ret = decode_OCSPResponse(data, length, &resp, &size); + if (ret) + return ret; + if (length != size) { + free_OCSPResponse(&resp); + return ASN1_EXTRA_DATA; + } + + switch (resp.responseStatus) { + case successful: + break; + default: + free_OCSPResponse(&resp); + return HX509_REVOKE_WRONG_DATA; + } + + if (resp.responseBytes == NULL) { + free_OCSPResponse(&resp); + return EINVAL; + } + + ret = der_heim_oid_cmp(&resp.responseBytes->responseType, + oid_id_pkix_ocsp_basic()); + if (ret != 0) { + free_OCSPResponse(&resp); + return HX509_REVOKE_WRONG_DATA; + } + + ret = decode_OCSPBasicOCSPResponse(resp.responseBytes->response.data, + resp.responseBytes->response.length, + basic, + &size); + if (ret) { + free_OCSPResponse(&resp); + return ret; + } + if (size != resp.responseBytes->response.length) { + free_OCSPResponse(&resp); + free_OCSPBasicOCSPResponse(basic); + return ASN1_EXTRA_DATA; + } + free_OCSPResponse(&resp); + + return 0; +} + +/* + * + */ + +static int +load_ocsp(hx509_context context, struct revoke_ocsp *ocsp) +{ + OCSPBasicOCSPResponse basic; + hx509_certs certs = NULL; + size_t length; + struct stat sb; + void *data; + int ret; + + ret = rk_undumpdata(ocsp->path, &data, &length); + if (ret) + return ret; + + ret = stat(ocsp->path, &sb); + if (ret) + return errno; + + ret = parse_ocsp_basic(data, length, &basic); + rk_xfree(data); + if (ret) { + hx509_set_error_string(context, 0, ret, + "Failed to parse OCSP response"); + return ret; + } + + if (basic.certs) { + int i; + + ret = hx509_certs_init(context, "MEMORY:ocsp-certs", 0, + NULL, &certs); + if (ret) { + free_OCSPBasicOCSPResponse(&basic); + return ret; + } + + for (i = 0; i < basic.certs->len; i++) { + hx509_cert c; + + ret = hx509_cert_init(context, &basic.certs->val[i], &c); + if (ret) + continue; + + ret = hx509_certs_add(context, certs, c); + hx509_cert_free(c); + if (ret) + continue; + } + } + + ocsp->last_modfied = sb.st_mtime; + + free_OCSPBasicOCSPResponse(&ocsp->ocsp); + hx509_certs_free(&ocsp->certs); + hx509_cert_free(ocsp->signer); + + ocsp->ocsp = basic; + ocsp->certs = certs; + ocsp->signer = NULL; + + 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, + const char *path) +{ + void *data; + int ret; + size_t i; + + if (strncmp(path, "FILE:", 5) != 0) { + hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION, + "unsupport type in %s", path); + return HX509_UNSUPPORTED_OPERATION; + } + + path += 5; + + for (i = 0; i < ctx->ocsps.len; i++) { + if (strcmp(ctx->ocsps.val[0].path, path) == 0) + return 0; + } + + data = realloc(ctx->ocsps.val, + (ctx->ocsps.len + 1) * sizeof(ctx->ocsps.val[0])); + if (data == NULL) { + hx509_clear_error_string(context); + return ENOMEM; + } + + ctx->ocsps.val = data; + + memset(&ctx->ocsps.val[ctx->ocsps.len], 0, + sizeof(ctx->ocsps.val[0])); + + ctx->ocsps.val[ctx->ocsps.len].path = strdup(path); + if (ctx->ocsps.val[ctx->ocsps.len].path == NULL) { + hx509_clear_error_string(context); + return ENOMEM; + } + + ret = load_ocsp(context, &ctx->ocsps.val[ctx->ocsps.len]); + if (ret) { + free(ctx->ocsps.val[ctx->ocsps.len].path); + return ret; + } + ctx->ocsps.len++; + + return ret; +} + +/* + * + */ + +static int +verify_crl(hx509_context context, + hx509_revoke_ctx ctx, + CRLCertificateList *crl, + time_t time_now, + hx509_certs certs, + hx509_cert parent) +{ + hx509_cert signer; + hx509_query q; + time_t t; + int ret; + + t = _hx509_Time2time_t(&crl->tbsCertList.thisUpdate); + 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) { + 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) { + 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); + + /* + * 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) { + hx509_set_error_string(context, HX509_ERROR_APPEND, ret, + "Failed to find certificate for CRL"); + return ret; + } + } + + ret = _hx509_verify_signature_bitstring(context, + _hx509_get_cert(signer), + &crl->signatureAlgorithm, + &crl->tbsCertList._save, + &crl->signatureValue); + if (ret) { + hx509_set_error_string(context, HX509_ERROR_APPEND, ret, + "CRL signature invalid"); + 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); + + return ret; +} + +static int +load_crl(const char *path, time_t *t, CRLCertificateList *crl) +{ + size_t length, size; + struct stat sb; + void *data; + int ret; + + memset(crl, 0, sizeof(*crl)); + + ret = rk_undumpdata(path, &data, &length); + if (ret) + return ret; + + ret = stat(path, &sb); + if (ret) + return errno; + + *t = sb.st_mtime; + + ret = decode_CRLCertificateList(data, length, crl, &size); + rk_xfree(data); + if (ret) + return ret; + + /* check signature is aligned */ + if (crl->signatureValue.length & 7) { + free_CRLCertificateList(crl); + return HX509_CRYPTO_SIG_INVALID_FORMAT; + } + 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, + const char *path) +{ + void *data; + size_t i; + int ret; + + if (strncmp(path, "FILE:", 5) != 0) { + hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION, + "unsupport type in %s", path); + return HX509_UNSUPPORTED_OPERATION; + } + + + path += 5; + + for (i = 0; i < ctx->crls.len; i++) { + if (strcmp(ctx->crls.val[0].path, path) == 0) + return 0; + } + + data = realloc(ctx->crls.val, + (ctx->crls.len + 1) * sizeof(ctx->crls.val[0])); + if (data == NULL) { + hx509_clear_error_string(context); + return ENOMEM; + } + ctx->crls.val = data; + + memset(&ctx->crls.val[ctx->crls.len], 0, sizeof(ctx->crls.val[0])); + + ctx->crls.val[ctx->crls.len].path = strdup(path); + if (ctx->crls.val[ctx->crls.len].path == NULL) { + hx509_clear_error_string(context); + return ENOMEM; + } + + ret = load_crl(path, + &ctx->crls.val[ctx->crls.len].last_modfied, + &ctx->crls.val[ctx->crls.len].crl); + if (ret) { + free(ctx->crls.val[ctx->crls.len].path); + return ret; + } + + ctx->crls.len++; + + 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, + hx509_revoke_ctx ctx, + hx509_certs certs, + time_t now, + hx509_cert cert, + hx509_cert parent_cert) +{ + const Certificate *c = _hx509_get_cert(cert); + const Certificate *p = _hx509_get_cert(parent_cert); + 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; + + /* check this ocsp apply to this cert */ + + /* check if there is a newer version of the file */ + ret = stat(ocsp->path, &sb); + if (ret == 0 && ocsp->last_modfied != sb.st_mtime) { + ret = load_ocsp(context, ocsp); + if (ret) + continue; + } + + /* verify signature in ocsp if not already done */ + if (ocsp->signer == NULL) { + ret = verify_ocsp(context, ocsp, now, certs, parent_cert); + if (ret) + continue; + } + + for (j = 0; j < ocsp->ocsp.tbsResponseData.responses.len; j++) { + heim_octet_string os; + + ret = der_heim_integer_cmp(&ocsp->ocsp.tbsResponseData.responses.val[j].certID.serialNumber, + &c->tbsCertificate.serialNumber); + if (ret != 0) + continue; + + /* verify issuer hashes hash */ + ret = _hx509_verify_signature(context, + NULL, + &ocsp->ocsp.tbsResponseData.responses.val[i].certID.hashAlgorithm, + &c->tbsCertificate.issuer._save, + &ocsp->ocsp.tbsResponseData.responses.val[i].certID.issuerNameHash); + if (ret != 0) + continue; + + os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data; + os.length = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8; + + ret = _hx509_verify_signature(context, + NULL, + &ocsp->ocsp.tbsResponseData.responses.val[j].certID.hashAlgorithm, + &os, + &ocsp->ocsp.tbsResponseData.responses.val[j].certID.issuerKeyHash); + if (ret != 0) + continue; + + switch (ocsp->ocsp.tbsResponseData.responses.val[j].certStatus.element) { + 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; + } + + /* don't allow the update to be in the future */ + if (ocsp->ocsp.tbsResponseData.responses.val[j].thisUpdate > + now + context->ocsp_time_diff) + continue; + + /* 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; + } else + /* Should force a refetch, but can we ? */; + + return 0; + } + } + + 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, &diff); + if (ret || diff) + continue; + + ret = stat(crl->path, &sb); + if (ret == 0 && crl->last_modfied != sb.st_mtime) { + CRLCertificateList cl; + + ret = load_crl(crl->path, &crl->last_modfied, &cl); + if (ret == 0) { + 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, 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) { + 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; + + /* check if cert is in crl */ + for (j = 0; j < crl->crl.tbsCertList.revokedCertificates->len; j++) { + time_t t; + + ret = der_heim_integer_cmp(&crl->crl.tbsCertList.revokedCertificates->val[j].userCertificate, + &c->tbsCertificate.serialNumber); + if (ret != 0) + continue; + + t = _hx509_Time2time_t(&crl->crl.tbsCertList.revokedCertificates->val[j].revocationDate); + if (t > now) + continue; + + if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions) + for (k = 0; k < crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->len; k++) + if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->val[k].critical) + return HX509_CRL_UNKNOWN_EXTENSION; + + hx509_set_error_string(context, 0, + HX509_CERT_REVOKED, + "Certificate revoked by issuer in CRL"); + return HX509_CERT_REVOKED; + } + + return 0; + } + + + 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; +} + +struct ocsp_add_ctx { + OCSPTBSRequest *req; + hx509_certs certs; + const AlgorithmIdentifier *digest; + hx509_cert parent; +}; + +static int +add_to_req(hx509_context context, void *ptr, hx509_cert cert) +{ + struct ocsp_add_ctx *ctx = ptr; + OCSPInnerRequest *one; + hx509_cert parent = NULL; + Certificate *p, *c = _hx509_get_cert(cert); + heim_octet_string os; + int ret; + hx509_query q; + void *d; + + d = realloc(ctx->req->requestList.val, + sizeof(ctx->req->requestList.val[0]) * + (ctx->req->requestList.len + 1)); + if (d == NULL) + return ENOMEM; + ctx->req->requestList.val = d; + + one = &ctx->req->requestList.val[ctx->req->requestList.len]; + memset(one, 0, sizeof(*one)); + + _hx509_query_clear(&q); + + q.match |= HX509_QUERY_FIND_ISSUER_CERT; + q.subject = c; + + ret = hx509_certs_find(context, ctx->certs, &q, &parent); + if (ret) + goto out; + + if (ctx->parent) { + if (hx509_cert_cmp(ctx->parent, parent) != 0) { + ret = HX509_REVOKE_NOT_SAME_PARENT; + hx509_set_error_string(context, 0, ret, + "Not same parent certifate as " + "last certificate in request"); + goto out; + } + } else + ctx->parent = hx509_cert_ref(parent); + + p = _hx509_get_cert(parent); + + ret = copy_AlgorithmIdentifier(ctx->digest, &one->reqCert.hashAlgorithm); + if (ret) + goto out; + + ret = _hx509_create_signature(context, + NULL, + &one->reqCert.hashAlgorithm, + &c->tbsCertificate.issuer._save, + NULL, + &one->reqCert.issuerNameHash); + if (ret) + goto out; + + os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data; + os.length = + p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8; + + ret = _hx509_create_signature(context, + NULL, + &one->reqCert.hashAlgorithm, + &os, + NULL, + &one->reqCert.issuerKeyHash); + if (ret) + goto out; + + ret = copy_CertificateSerialNumber(&c->tbsCertificate.serialNumber, + &one->reqCert.serialNumber); + if (ret) + goto out; + + ctx->req->requestList.len++; +out: + hx509_cert_free(parent); + if (ret) { + free_OCSPInnerRequest(one); + memset(one, 0, sizeof(*one)); + } + + 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, + hx509_certs reqcerts, + hx509_certs pool, + hx509_cert signer, + const AlgorithmIdentifier *digest, + heim_octet_string *request, + heim_octet_string *nonce) +{ + OCSPRequest req; + size_t size; + int ret; + struct ocsp_add_ctx ctx; + Extensions *es; + + memset(&req, 0, sizeof(req)); + + if (digest == NULL) + digest = _hx509_crypto_default_digest_alg; + + ctx.req = &req.tbsRequest; + ctx.certs = pool; + ctx.digest = digest; + ctx.parent = NULL; + + ret = hx509_certs_iter(context, reqcerts, add_to_req, &ctx); + hx509_cert_free(ctx.parent); + if (ret) + goto out; + + if (nonce) { + req.tbsRequest.requestExtensions = + calloc(1, sizeof(*req.tbsRequest.requestExtensions)); + if (req.tbsRequest.requestExtensions == NULL) { + ret = ENOMEM; + goto out; + } + + es = req.tbsRequest.requestExtensions; + + 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) { + free_OCSPRequest(&req); + return ret; + } + + es->val[0].extnValue.data = malloc(10); + if (es->val[0].extnValue.data == NULL) { + 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) { + ret = HX509_CRYPTO_INTERNAL_ERROR; + goto out; + } + ret = der_copy_octet_string(nonce, &es->val[0].extnValue); + if (ret) { + ret = ENOMEM; + goto out; + } + } + + ASN1_MALLOC_ENCODE(OCSPRequest, request->data, request->length, + &req, &size, ret); + free_OCSPRequest(&req); + if (ret) + goto out; + if (size != request->length) + _hx509_abort("internal ASN.1 encoder error"); + + return 0; + +out: + free_OCSPRequest(&req); + return ret; +} + +static char * +printable_time(time_t t) +{ + static char s[128]; + strlcpy(s, ctime(&t)+ 4, sizeof(s)); + s[20] = 0; + 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) +{ + struct revoke_ocsp ocsp; + int ret, i; + + if (out == NULL) + out = stdout; + + memset(&ocsp, 0, sizeof(ocsp)); + + ocsp.path = strdup(path); + if (ocsp.path == NULL) + return ENOMEM; + + ret = load_ocsp(context, &ocsp); + if (ret) { + free_ocsp(&ocsp); + return ret; + } + + fprintf(out, "signer: "); + + switch(ocsp.ocsp.tbsResponseData.responderID.element) { + case choice_OCSPResponderID_byName: { + hx509_name n; + char *s; + _hx509_name_from_Name(&ocsp.ocsp.tbsResponseData.responderID.u.byName, &n); + hx509_name_to_string(n, &s); + hx509_name_free(&n); + fprintf(out, " byName: %s\n", s); + free(s); + break; + } + case choice_OCSPResponderID_byKey: { + char *s; + hex_encode(ocsp.ocsp.tbsResponseData.responderID.u.byKey.data, + ocsp.ocsp.tbsResponseData.responderID.u.byKey.length, + &s); + fprintf(out, " byKey: %s\n", s); + free(s); + break; + } + default: + _hx509_abort("choice_OCSPResponderID unknown"); + break; + } + + fprintf(out, "producedAt: %s\n", + printable_time(ocsp.ocsp.tbsResponseData.producedAt)); + + fprintf(out, "replies: %d\n", ocsp.ocsp.tbsResponseData.responses.len); + + for (i = 0; i < ocsp.ocsp.tbsResponseData.responses.len; i++) { + const char *status; + switch (ocsp.ocsp.tbsResponseData.responses.val[i].certStatus.element) { + case choice_OCSPCertStatus_good: + status = "good"; + break; + case choice_OCSPCertStatus_revoked: + status = "revoked"; + break; + case choice_OCSPCertStatus_unknown: + status = "unknown"; + break; + default: + status = "element unknown"; + } + + fprintf(out, "\t%d. status: %s\n", i, status); + + fprintf(out, "\tthisUpdate: %s\n", + printable_time(ocsp.ocsp.tbsResponseData.responses.val[i].thisUpdate)); + if (ocsp.ocsp.tbsResponseData.responses.val[i].nextUpdate) + fprintf(out, "\tproducedAt: %s\n", + printable_time(ocsp.ocsp.tbsResponseData.responses.val[i].thisUpdate)); + + } + + fprintf(out, "appended certs:\n"); + if (ocsp.certs) + ret = hx509_certs_iter(context, ocsp.certs, hx509_ci_print_names, out); + + free_ocsp(&ocsp); + return ret; +} + +/** + * 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 +hx509_ocsp_verify(hx509_context context, + time_t now, + hx509_cert cert, + int flags, + const void *data, size_t length, + time_t *expiration) +{ + const Certificate *c = _hx509_get_cert(cert); + OCSPBasicOCSPResponse basic; + int ret, i; + + if (now == 0) + now = time(NULL); + + *expiration = 0; + + ret = parse_ocsp_basic(data, length, &basic); + if (ret) { + hx509_set_error_string(context, 0, ret, + "Failed to parse OCSP response"); + return ret; + } + + for (i = 0; i < basic.tbsResponseData.responses.len; i++) { + + ret = der_heim_integer_cmp(&basic.tbsResponseData.responses.val[i].certID.serialNumber, + &c->tbsCertificate.serialNumber); + if (ret != 0) + continue; + + /* verify issuer hashes hash */ + ret = _hx509_verify_signature(context, + NULL, + &basic.tbsResponseData.responses.val[i].certID.hashAlgorithm, + &c->tbsCertificate.issuer._save, + &basic.tbsResponseData.responses.val[i].certID.issuerNameHash); + if (ret != 0) + continue; + + switch (basic.tbsResponseData.responses.val[i].certStatus.element) { + case choice_OCSPCertStatus_good: + break; + case choice_OCSPCertStatus_revoked: + case choice_OCSPCertStatus_unknown: + continue; + } + + /* don't allow the update to be in the future */ + if (basic.tbsResponseData.responses.val[i].thisUpdate > + now + context->ocsp_time_diff) + continue; + + /* don't allow the next update to be in the past */ + if (basic.tbsResponseData.responses.val[i].nextUpdate) { + if (*basic.tbsResponseData.responses.val[i].nextUpdate < now) + continue; + *expiration = *basic.tbsResponseData.responses.val[i].nextUpdate; + } else + *expiration = now; + + free_OCSPBasicOCSPResponse(&basic); + return 0; + } + + free_OCSPBasicOCSPResponse(&basic); + + { + hx509_name name; + char *subject; + + ret = hx509_cert_get_subject(cert, &name); + if (ret) { + hx509_clear_error_string(context); + goto out; + } + ret = hx509_name_to_string(name, &subject); + hx509_name_free(&name); + if (ret) { + hx509_clear_error_string(context); + goto out; + } + hx509_set_error_string(context, 0, HX509_CERT_NOT_IN_OCSP, + "Certificate %s not in OCSP response " + "or not good", + subject); + free(subject); + } +out: + return HX509_CERT_NOT_IN_OCSP; +} + +struct hx509_crl { + hx509_certs revoked; + 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) +{ + int ret; + + *crl = calloc(1, sizeof(**crl)); + if (*crl == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + + ret = hx509_certs_init(context, "MEMORY:crl", 0, NULL, &(*crl)->revoked); + if (ret) { + free(*crl); + *crl = NULL; + return ret; + } + (*crl)->expire = 0; + 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, + hx509_certs certs) +{ + 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) +{ + crl->expire = time(NULL) + 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) +{ + if (*crl == NULL) + return; + hx509_certs_free(&(*crl)->revoked); + memset(*crl, 0, sizeof(**crl)); + free(*crl); + *crl = NULL; +} + +static int +add_revoked(hx509_context context, void *ctx, hx509_cert cert) +{ + TBSCRLCertList *c = ctx; + unsigned int num; + void *ptr; + int ret; + + num = c->revokedCertificates->len; + ptr = realloc(c->revokedCertificates->val, + (num + 1) * sizeof(c->revokedCertificates->val[0])); + if (ptr == NULL) { + hx509_clear_error_string(context); + return ENOMEM; + } + c->revokedCertificates->val = ptr; + + ret = hx509_cert_get_serialnumber(cert, + &c->revokedCertificates->val[num].userCertificate); + if (ret) { + hx509_clear_error_string(context); + return ret; + } + c->revokedCertificates->val[num].revocationDate.element = + choice_Time_generalTime; + c->revokedCertificates->val[num].revocationDate.u.generalTime = + time(NULL) - 3600 * 24; + c->revokedCertificates->val[num].crlEntryExtensions = NULL; + + c->revokedCertificates->len++; + + 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, + hx509_cert signer, + hx509_crl crl, + heim_octet_string *os) +{ + const AlgorithmIdentifier *sigalg = _hx509_crypto_default_sig_alg; + CRLCertificateList c; + size_t size; + int ret; + hx509_private_key signerkey; + + memset(&c, 0, sizeof(c)); + + signerkey = _hx509_cert_private_key(signer); + if (signerkey == NULL) { + ret = HX509_PRIVATE_KEY_MISSING; + hx509_set_error_string(context, 0, ret, + "Private key missing for CRL signing"); + return ret; + } + + c.tbsCertList.version = malloc(sizeof(*c.tbsCertList.version)); + if (c.tbsCertList.version == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + + *c.tbsCertList.version = 1; + + ret = copy_AlgorithmIdentifier(sigalg, &c.tbsCertList.signature); + if (ret) { + hx509_clear_error_string(context); + goto out; + } + + ret = copy_Name(&_hx509_get_cert(signer)->tbsCertificate.issuer, + &c.tbsCertList.issuer); + if (ret) { + hx509_clear_error_string(context); + goto out; + } + + c.tbsCertList.thisUpdate.element = choice_Time_generalTime; + c.tbsCertList.thisUpdate.u.generalTime = time(NULL) - 24 * 3600; + + c.tbsCertList.nextUpdate = malloc(sizeof(*c.tbsCertList.nextUpdate)); + if (c.tbsCertList.nextUpdate == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + ret = ENOMEM; + goto out; + } + + { + time_t next = crl->expire; + if (next == 0) + next = time(NULL) + 24 * 3600 * 365; + + c.tbsCertList.nextUpdate->element = choice_Time_generalTime; + c.tbsCertList.nextUpdate->u.generalTime = next; + } + + c.tbsCertList.revokedCertificates = + calloc(1, sizeof(*c.tbsCertList.revokedCertificates)); + if (c.tbsCertList.revokedCertificates == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + ret = ENOMEM; + goto out; + } + c.tbsCertList.crlExtensions = NULL; + + ret = hx509_certs_iter(context, crl->revoked, add_revoked, &c.tbsCertList); + if (ret) + goto out; + + /* if not revoked certs, remove OPTIONAL entry */ + if (c.tbsCertList.revokedCertificates->len == 0) { + free(c.tbsCertList.revokedCertificates); + c.tbsCertList.revokedCertificates = NULL; + } + + ASN1_MALLOC_ENCODE(TBSCRLCertList, os->data, os->length, + &c.tbsCertList, &size, ret); + if (ret) { + hx509_set_error_string(context, 0, ret, "failed to encode tbsCRL"); + goto out; + } + if (size != os->length) + _hx509_abort("internal ASN.1 encoder error"); + + + ret = _hx509_create_signature_bitstring(context, + signerkey, + sigalg, + os, + &c.signatureAlgorithm, + &c.signatureValue); + free(os->data); + if (ret) { + hx509_set_error_string(context, 0, ret, "Failed to sign CRL"); + goto out; + } + + ASN1_MALLOC_ENCODE(CRLCertificateList, os->data, os->length, + &c, &size, ret); + if (ret) { + hx509_set_error_string(context, 0, ret, "failed to encode CRL"); + goto out; + } + if (size != os->length) + _hx509_abort("internal ASN.1 encoder error"); + + free_CRLCertificateList(&c); + + return 0; + +out: + free_CRLCertificateList(&c); + return ret; +} diff --git a/source4/heimdal/lib/hx509/sel-gram.c b/source4/heimdal/lib/hx509/sel-gram.c new file mode 100644 index 0000000000..905384da55 --- /dev/null +++ b/source4/heimdal/lib/hx509/sel-gram.c @@ -0,0 +1,1714 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton implementation for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.3" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + kw_TRUE = 258, + kw_FALSE = 259, + kw_AND = 260, + kw_OR = 261, + kw_IN = 262, + kw_TAILMATCH = 263, + NUMBER = 264, + STRING = 265, + IDENTIFIER = 266 + }; +#endif +/* Tokens. */ +#define kw_TRUE 258 +#define kw_FALSE 259 +#define kw_AND 260 +#define kw_OR 261 +#define kw_IN 262 +#define kw_TAILMATCH 263 +#define NUMBER 264 +#define STRING 265 +#define IDENTIFIER 266 + + + + +/* Copy the first part of user declarations. */ +#line 34 "heimdal/lib/hx509/sel-gram.y" + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include <hx_locl.h> + +RCSID("$Id$"); + + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +#line 46 "heimdal/lib/hx509/sel-gram.y" +{ + char *string; + struct hx_expr *expr; +} +/* Line 187 of yacc.c. */ +#line 135 "heimdal/lib/hx509/sel-gram.y" + YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +/* Copy the second part of user declarations. */ + + +/* Line 216 of yacc.c. */ +#line 148 "heimdal/lib/hx509/sel-gram.y" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if YYENABLE_NLS +# if ENABLE_NLS +# include <libintl.h> /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int i) +#else +static int +YYID (i) + int i; +#endif +{ + return i; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include <alloca.h> /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include <malloc.h> /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss; + YYSTYPE yyvs; + }; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 21 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 50 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 21 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 11 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 26 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 50 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 266 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 12, 2, 2, 2, 17, 2, 2, + 13, 14, 2, 2, 15, 2, 20, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 16, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 18, 2, 19, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint8 yyprhs[] = +{ + 0, 0, 3, 5, 7, 9, 12, 16, 20, 24, + 26, 28, 32, 37, 42, 46, 52, 56, 58, 60, + 62, 64, 66, 68, 73, 78, 82 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int8 yyrhs[] = +{ + 22, 0, -1, 23, -1, 3, -1, 4, -1, 12, + 23, -1, 23, 5, 23, -1, 23, 6, 23, -1, + 13, 23, 14, -1, 25, -1, 26, -1, 26, 15, + 24, -1, 26, 16, 16, 26, -1, 26, 12, 16, + 26, -1, 26, 8, 26, -1, 26, 7, 13, 24, + 14, -1, 26, 7, 30, -1, 27, -1, 28, -1, + 29, -1, 30, -1, 9, -1, 10, -1, 11, 13, + 24, 14, -1, 17, 18, 31, 19, -1, 11, 20, + 31, -1, 11, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint8 yyrline[] = +{ + 0, 74, 74, 76, 77, 78, 79, 80, 81, 82, + 85, 86, 89, 90, 91, 92, 93, 96, 97, 98, + 99, 102, 103, 105, 108, 111, 113 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "kw_TRUE", "kw_FALSE", "kw_AND", "kw_OR", + "kw_IN", "kw_TAILMATCH", "NUMBER", "STRING", "IDENTIFIER", "'!'", "'('", + "')'", "','", "'='", "'%'", "'{'", "'}'", "'.'", "$accept", "start", + "expr", "words", "comp", "word", "number", "string", "function", + "variable", "variables", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 33, 40, 41, 44, 61, 37, 123, 125, + 46 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 21, 22, 23, 23, 23, 23, 23, 23, 23, + 24, 24, 25, 25, 25, 25, 25, 26, 26, 26, + 26, 27, 28, 29, 30, 31, 31 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 1, 1, 1, 2, 3, 3, 3, 1, + 1, 3, 4, 4, 3, 5, 3, 1, 1, 1, + 1, 1, 1, 4, 4, 3, 1 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 0, 3, 4, 21, 22, 0, 0, 0, 0, 0, + 2, 9, 0, 17, 18, 19, 20, 0, 5, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 10, + 8, 26, 0, 6, 7, 0, 16, 14, 0, 0, + 23, 0, 0, 24, 0, 13, 12, 11, 25, 15 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 9, 10, 28, 11, 12, 13, 14, 15, 16, + 32 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -31 +static const yytype_int8 yypact[] = +{ + 22, -31, -31, -31, -31, -1, 22, 22, -11, 27, + 11, -31, -6, -31, -31, -31, -31, 19, 11, 9, + 26, -31, 22, 22, -4, 19, 24, 25, 28, 23, + -31, 29, 31, 11, 11, 19, -31, -31, 19, 19, + -31, 19, 26, -31, 30, -31, -31, -31, -31, -31 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -31, -31, -3, -30, -31, -17, -31, -31, -31, 21, + 1 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -1 +static const yytype_uint8 yytable[] = +{ + 29, 24, 25, 18, 19, 44, 26, 20, 37, 35, + 27, 47, 17, 8, 22, 23, 22, 23, 29, 33, + 34, 45, 46, 30, 29, 1, 2, 21, 3, 4, + 5, 3, 4, 5, 6, 7, 8, 31, 41, 8, + 38, 39, 40, 48, 49, 36, 0, 0, 0, 42, + 43 +}; + +static const yytype_int8 yycheck[] = +{ + 17, 7, 8, 6, 7, 35, 12, 18, 25, 13, + 16, 41, 13, 17, 5, 6, 5, 6, 35, 22, + 23, 38, 39, 14, 41, 3, 4, 0, 9, 10, + 11, 9, 10, 11, 12, 13, 17, 11, 15, 17, + 16, 16, 14, 42, 14, 24, -1, -1, -1, 20, + 19 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 3, 4, 9, 10, 11, 12, 13, 17, 22, + 23, 25, 26, 27, 28, 29, 30, 13, 23, 23, + 18, 0, 5, 6, 7, 8, 12, 16, 24, 26, + 14, 11, 31, 23, 23, 13, 30, 26, 16, 16, + 14, 15, 20, 19, 24, 26, 26, 24, 31, 14 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK (1); \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) +#endif + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if YYLTYPE_IS_TRIVIAL +# define YY_LOCATION_PRINT(File, Loc) \ + fprintf (File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include <stdio.h> /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) +#else +static void +yy_stack_print (bottom, top) + yytype_int16 *bottom; + yytype_int16 *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, int yyrule) +#else +static void +yy_reduce_print (yyvsp, yyrule) + YYSTYPE *yyvsp; + int yyrule; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + fprintf (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); + fprintf (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, Rule); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into YYRESULT an error message about the unexpected token + YYCHAR while in state YYSTATE. Return the number of bytes copied, + including the terminating null byte. If YYRESULT is null, do not + copy anything; just return the number of bytes that would be + copied. As a special case, return 0 if an ordinary "syntax error" + message will do. Return YYSIZE_MAXIMUM if overflow occurs during + size calculation. */ +static YYSIZE_T +yysyntax_error (char *yyresult, int yystate, int yychar) +{ + int yyn = yypact[yystate]; + + if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) + return 0; + else + { + int yytype = YYTRANSLATE (yychar); + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + int yysize_overflow = 0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + int yyx; + +# if 0 + /* This is so xgettext sees the translatable formats that are + constructed on the fly. */ + YY_("syntax error, unexpected %s"); + YY_("syntax error, unexpected %s, expecting %s"); + YY_("syntax error, unexpected %s, expecting %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); +# endif + char *yyfmt; + char const *yyf; + static char const yyunexpected[] = "syntax error, unexpected %s"; + static char const yyexpecting[] = ", expecting %s"; + static char const yyor[] = " or %s"; + char yyformat[sizeof yyunexpected + + sizeof yyexpecting - 1 + + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) + * (sizeof yyor - 1))]; + char const *yyprefix = yyexpecting; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 1; + + yyarg[0] = yytname[yytype]; + yyfmt = yystpcpy (yyformat, yyunexpected); + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + yyformat[sizeof yyunexpected - 1] = '\0'; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + yyfmt = yystpcpy (yyfmt, yyprefix); + yyprefix = yyor; + } + + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + + if (yysize_overflow) + return YYSIZE_MAXIMUM; + + if (yyresult) + { + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + char *yyp = yyresult; + int yyi = 0; + while ((*yyp = *yyf) != '\0') + { + if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyf += 2; + } + else + { + yyp++; + yyf++; + } + } + } + return yysize; + } +} +#endif /* YYERROR_VERBOSE */ + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yymsg, yytype, yyvaluep) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + YYUSE (yyvaluep); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + +/* The look-ahead symbol. */ +int yychar; + +/* The semantic value of the look-ahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + + int yystate; + int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Look-ahead token as an internal (translated) token number. */ + int yytoken = 0; +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss = yyssa; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp; + + + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + look-ahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to look-ahead token. */ + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a look-ahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the look-ahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +#line 74 "heimdal/lib/hx509/sel-gram.y" + { _hx509_expr_input.expr = (yyvsp[(1) - (1)].expr); } + break; + + case 3: +#line 76 "heimdal/lib/hx509/sel-gram.y" + { (yyval.expr) = _hx509_make_expr(op_TRUE, NULL, NULL); } + break; + + case 4: +#line 77 "heimdal/lib/hx509/sel-gram.y" + { (yyval.expr) = _hx509_make_expr(op_FALSE, NULL, NULL); } + break; + + case 5: +#line 78 "heimdal/lib/hx509/sel-gram.y" + { (yyval.expr) = _hx509_make_expr(op_NOT, (yyvsp[(2) - (2)].expr), NULL); } + break; + + case 6: +#line 79 "heimdal/lib/hx509/sel-gram.y" + { (yyval.expr) = _hx509_make_expr(op_AND, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); } + break; + + case 7: +#line 80 "heimdal/lib/hx509/sel-gram.y" + { (yyval.expr) = _hx509_make_expr(op_OR, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); } + break; + + case 8: +#line 81 "heimdal/lib/hx509/sel-gram.y" + { (yyval.expr) = (yyvsp[(2) - (3)].expr); } + break; + + case 9: +#line 82 "heimdal/lib/hx509/sel-gram.y" + { (yyval.expr) = _hx509_make_expr(op_COMP, (yyvsp[(1) - (1)].expr), NULL); } + break; + + case 10: +#line 85 "heimdal/lib/hx509/sel-gram.y" + { (yyval.expr) = _hx509_make_expr(expr_WORDS, (yyvsp[(1) - (1)].expr), NULL); } + break; + + case 11: +#line 86 "heimdal/lib/hx509/sel-gram.y" + { (yyval.expr) = _hx509_make_expr(expr_WORDS, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); } + break; + + case 12: +#line 89 "heimdal/lib/hx509/sel-gram.y" + { (yyval.expr) = _hx509_make_expr(comp_EQ, (yyvsp[(1) - (4)].expr), (yyvsp[(4) - (4)].expr)); } + break; + + case 13: +#line 90 "heimdal/lib/hx509/sel-gram.y" + { (yyval.expr) = _hx509_make_expr(comp_NE, (yyvsp[(1) - (4)].expr), (yyvsp[(4) - (4)].expr)); } + break; + + case 14: +#line 91 "heimdal/lib/hx509/sel-gram.y" + { (yyval.expr) = _hx509_make_expr(comp_TAILEQ, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); } + break; + + case 15: +#line 92 "heimdal/lib/hx509/sel-gram.y" + { (yyval.expr) = _hx509_make_expr(comp_IN, (yyvsp[(1) - (5)].expr), (yyvsp[(4) - (5)].expr)); } + break; + + case 16: +#line 93 "heimdal/lib/hx509/sel-gram.y" + { (yyval.expr) = _hx509_make_expr(comp_IN, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); } + break; + + case 17: +#line 96 "heimdal/lib/hx509/sel-gram.y" + { (yyval.expr) = (yyvsp[(1) - (1)].expr); } + break; + + case 18: +#line 97 "heimdal/lib/hx509/sel-gram.y" + { (yyval.expr) = (yyvsp[(1) - (1)].expr); } + break; + + case 19: +#line 98 "heimdal/lib/hx509/sel-gram.y" + { (yyval.expr) = (yyvsp[(1) - (1)].expr); } + break; + + case 20: +#line 99 "heimdal/lib/hx509/sel-gram.y" + { (yyval.expr) = (yyvsp[(1) - (1)].expr); } + break; + + case 21: +#line 102 "heimdal/lib/hx509/sel-gram.y" + { (yyval.expr) = _hx509_make_expr(expr_NUMBER, (yyvsp[(1) - (1)].string), NULL); } + break; + + case 22: +#line 103 "heimdal/lib/hx509/sel-gram.y" + { (yyval.expr) = _hx509_make_expr(expr_STRING, (yyvsp[(1) - (1)].string), NULL); } + break; + + case 23: +#line 105 "heimdal/lib/hx509/sel-gram.y" + { + (yyval.expr) = _hx509_make_expr(expr_FUNCTION, (yyvsp[(1) - (4)].string), (yyvsp[(3) - (4)].expr)); } + break; + + case 24: +#line 108 "heimdal/lib/hx509/sel-gram.y" + { (yyval.expr) = (yyvsp[(3) - (4)].expr); } + break; + + case 25: +#line 111 "heimdal/lib/hx509/sel-gram.y" + { + (yyval.expr) = _hx509_make_expr(expr_VAR, (yyvsp[(1) - (3)].string), (yyvsp[(3) - (3)].expr)); } + break; + + case 26: +#line 113 "heimdal/lib/hx509/sel-gram.y" + { + (yyval.expr) = _hx509_make_expr(expr_VAR, (yyvsp[(1) - (1)].string), NULL); } + break; + + +/* Line 1267 of yacc.c. */ +#line 1501 "heimdal/lib/hx509/sel-gram.y" + default: break; + } + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else + { + YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); + if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) + { + YYSIZE_T yyalloc = 2 * yysize; + if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) + yyalloc = YYSTACK_ALLOC_MAXIMUM; + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yyalloc); + if (yymsg) + yymsg_alloc = yyalloc; + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + } + } + + if (0 < yysize && yysize <= yymsg_alloc) + { + (void) yysyntax_error (yymsg, yystate, yychar); + yyerror (yymsg); + } + else + { + yyerror (YY_("syntax error")); + if (yysize != 0) + goto yyexhaustedlab; + } + } +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse look-ahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse look-ahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + *++yyvsp = yylval; + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEOF && yychar != YYEMPTY) + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + + diff --git a/source4/heimdal/lib/hx509/sel-gram.h b/source4/heimdal/lib/hx509/sel-gram.h new file mode 100644 index 0000000000..bb4a64d1c7 --- /dev/null +++ b/source4/heimdal/lib/hx509/sel-gram.h @@ -0,0 +1,83 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton interface for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + kw_TRUE = 258, + kw_FALSE = 259, + kw_AND = 260, + kw_OR = 261, + kw_IN = 262, + kw_TAILMATCH = 263, + NUMBER = 264, + STRING = 265, + IDENTIFIER = 266 + }; +#endif +/* Tokens. */ +#define kw_TRUE 258 +#define kw_FALSE 259 +#define kw_AND 260 +#define kw_OR 261 +#define kw_IN 262 +#define kw_TAILMATCH 263 +#define NUMBER 264 +#define STRING 265 +#define IDENTIFIER 266 + + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +#line 46 "heimdal/lib/hx509/sel-gram.y" +{ + char *string; + struct hx_expr *expr; +} +/* Line 1489 of yacc.c. */ +#line 76 "heimdal/lib/hx509/sel-gram.y" + YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + +extern YYSTYPE yylval; + diff --git a/source4/heimdal/lib/hx509/sel-gram.y b/source4/heimdal/lib/hx509/sel-gram.y new file mode 100644 index 0000000000..ca34a1975f --- /dev/null +++ b/source4/heimdal/lib/hx509/sel-gram.y @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2008 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +%{ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include <hx_locl.h> + +RCSID("$Id$"); + +%} + +%union { + char *string; + struct hx_expr *expr; +} + +%token kw_TRUE +%token kw_FALSE +%token kw_AND +%token kw_OR +%token kw_IN +%token kw_TAILMATCH + +%type <expr> expr +%type <expr> comp +%type <expr> word words +%type <expr> number +%type <expr> string +%type <expr> function +%type <expr> variable variables + +%token <string> NUMBER +%token <string> STRING +%token <string> IDENTIFIER + +%start start + +%% + +start: expr { _hx509_expr_input.expr = $1; } + +expr : kw_TRUE { $$ = _hx509_make_expr(op_TRUE, NULL, NULL); } + | kw_FALSE { $$ = _hx509_make_expr(op_FALSE, NULL, NULL); } + | '!' expr { $$ = _hx509_make_expr(op_NOT, $2, NULL); } + | expr kw_AND expr { $$ = _hx509_make_expr(op_AND, $1, $3); } + | expr kw_OR expr { $$ = _hx509_make_expr(op_OR, $1, $3); } + | '(' expr ')' { $$ = $2; } + | comp { $$ = _hx509_make_expr(op_COMP, $1, NULL); } + ; + +words : word { $$ = _hx509_make_expr(expr_WORDS, $1, NULL); } + | word ',' words { $$ = _hx509_make_expr(expr_WORDS, $1, $3); } + ; + +comp : word '=' '=' word { $$ = _hx509_make_expr(comp_EQ, $1, $4); } + | word '!' '=' word { $$ = _hx509_make_expr(comp_NE, $1, $4); } + | word kw_TAILMATCH word { $$ = _hx509_make_expr(comp_TAILEQ, $1, $3); } + | word kw_IN '(' words ')' { $$ = _hx509_make_expr(comp_IN, $1, $4); } + | word kw_IN variable { $$ = _hx509_make_expr(comp_IN, $1, $3); } + ; + +word : number { $$ = $1; } + | string { $$ = $1; } + | function { $$ = $1; } + | variable { $$ = $1; } + ; + +number : NUMBER { $$ = _hx509_make_expr(expr_NUMBER, $1, NULL); }; +string : STRING { $$ = _hx509_make_expr(expr_STRING, $1, NULL); }; + +function: IDENTIFIER '(' words ')' { + $$ = _hx509_make_expr(expr_FUNCTION, $1, $3); } + ; +variable: '%' '{' variables '}' { $$ = $3; } + ; + +variables: IDENTIFIER '.' variables { + $$ = _hx509_make_expr(expr_VAR, $1, $3); } + | IDENTIFIER { + $$ = _hx509_make_expr(expr_VAR, $1, NULL); } + ; diff --git a/source4/heimdal/lib/hx509/sel-lex.c b/source4/heimdal/lib/hx509/sel-lex.c new file mode 100644 index 0000000000..8dcb374c1f --- /dev/null +++ b/source4/heimdal/lib/hx509/sel-lex.c @@ -0,0 +1,1899 @@ +#include "config.h" + +#line 3 "heimdal/lib/hx509/sel-lex.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 34 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include <inttypes.h> +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; +#endif /* ! C99 */ + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +/* C99 requires __STDC__ to be defined as 1. */ +#if defined (__STDC__) + +#define YY_USE_CONST + +#endif /* defined (__STDC__) */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart(yyin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +extern int yyleng; + +extern FILE *yyin, *yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, (yytext_ptr) ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + * Given that the standard has decreed that size_t exists since 1989, + * I guess we can afford to depend on it. Manoj. + */ + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) + +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 0; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void yyrestart (FILE *input_file ); +void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ); +void yy_delete_buffer (YY_BUFFER_STATE b ); +void yy_flush_buffer (YY_BUFFER_STATE b ); +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ); +void yypop_buffer_state (void ); + +static void yyensure_buffer_stack (void ); +static void yy_load_buffer_state (void ); +static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file ); + +#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ); +YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ); +YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ); + +void *yyalloc (yy_size_t ); +void *yyrealloc (void *,yy_size_t ); +void yyfree (void * ); + +#define yy_new_buffer yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer(yyin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer(yyin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +typedef unsigned char YY_CHAR; + +FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; + +typedef int yy_state_type; + +extern int yylineno; + +int yylineno = 1; + +extern char *yytext; +#define yytext_ptr yytext + +static yy_state_type yy_get_previous_state (void ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); +static int yy_get_next_buffer (void ); +static void yy_fatal_error (yyconst char msg[] ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ + yyleng = (size_t) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; + +#define YY_NUM_RULES 12 +#define YY_END_OF_BUFFER 13 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_accept[36] = + { 0, + 0, 0, 13, 12, 11, 9, 10, 8, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 5, 4, 7, + 7, 3, 7, 7, 7, 7, 7, 1, 2, 7, + 7, 7, 7, 6, 0 + } ; + +static yyconst flex_int32_t yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 4, 5, 1, 1, 4, 1, 1, 4, + 4, 1, 1, 4, 6, 4, 1, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 1, 1, 1, + 4, 1, 1, 1, 7, 8, 9, 10, 11, 12, + 8, 13, 14, 8, 8, 15, 16, 17, 18, 8, + 8, 19, 20, 21, 22, 8, 8, 8, 8, 8, + 1, 1, 1, 1, 6, 1, 8, 8, 8, 8, + + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 4, 1, 4, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst flex_int32_t yy_meta[23] = + { 0, + 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2 + } ; + +static yyconst flex_int16_t yy_base[37] = + { 0, + 0, 0, 43, 44, 44, 44, 44, 44, 25, 0, + 34, 23, 20, 16, 0, 28, 22, 0, 0, 22, + 12, 0, 13, 17, 20, 19, 13, 0, 0, 21, + 6, 17, 12, 0, 44, 22 + } ; + +static yyconst flex_int16_t yy_def[37] = + { 0, + 35, 1, 35, 35, 35, 35, 35, 35, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 0, 35 + } ; + +static yyconst flex_int16_t yy_nxt[67] = + { 0, + 4, 5, 6, 7, 8, 4, 9, 10, 10, 10, + 10, 11, 10, 12, 10, 10, 10, 13, 10, 10, + 14, 10, 20, 15, 34, 33, 32, 31, 30, 29, + 28, 27, 26, 25, 21, 24, 23, 22, 19, 18, + 17, 16, 35, 3, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35 + } ; + +static yyconst flex_int16_t yy_chk[67] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 14, 36, 33, 32, 31, 30, 27, 26, + 25, 24, 23, 21, 14, 20, 17, 16, 13, 12, + 11, 9, 3, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35 + } ; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +extern int yy_flex_debug; +int yy_flex_debug = 0; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *yytext; +#line 1 "sel-lex.l" +#line 2 "sel-lex.l" +/* + * Copyright (c) 2004, 2008 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#undef ECHO + +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include "sel.h" +#include "sel-gram.h" +unsigned lineno = 1; + +static char * handle_string(void); +static int lex_input(char *, int); + +struct hx_expr_input _hx509_expr_input; + +#define YY_NO_UNPUT 1 + +#undef YY_INPUT +#define YY_INPUT(buf,res,maxsize) (res = lex_input(buf, maxsize)) + +#undef ECHO + +#line 541 "heimdal/lib/hx509/sel-lex.c" + +#define INITIAL 0 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include <unistd.h> +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +static int yy_init_globals (void ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap (void ); +#else +extern int yywrap (void ); +#endif +#endif + + static void yyunput (int c,char *buf_ptr ); + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (void ); +#else +static int input (void ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO fwrite( yytext, yyleng, 1, yyout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex (void); + +#define YY_DECL int yylex (void) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + +#line 64 "sel-lex.l" + + +#line 697 "heimdal/lib/hx509/sel-lex.c" + + if ( !(yy_init) ) + { + (yy_init) = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer(yyin,YY_BUF_SIZE ); + } + + yy_load_buffer_state( ); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* Support of yytext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = (yy_start); +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 36 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 44 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = (yy_hold_char); + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 66 "sel-lex.l" +{ return kw_TRUE; } + YY_BREAK +case 2: +YY_RULE_SETUP +#line 67 "sel-lex.l" +{ return kw_FALSE; } + YY_BREAK +case 3: +YY_RULE_SETUP +#line 68 "sel-lex.l" +{ return kw_AND; } + YY_BREAK +case 4: +YY_RULE_SETUP +#line 69 "sel-lex.l" +{ return kw_OR; } + YY_BREAK +case 5: +YY_RULE_SETUP +#line 70 "sel-lex.l" +{ return kw_IN; } + YY_BREAK +case 6: +YY_RULE_SETUP +#line 71 "sel-lex.l" +{ return kw_TAILMATCH; } + YY_BREAK +case 7: +YY_RULE_SETUP +#line 73 "sel-lex.l" +{ + yylval.string = strdup ((const char *)yytext); + return IDENTIFIER; + } + YY_BREAK +case 8: +YY_RULE_SETUP +#line 77 "sel-lex.l" +{ yylval.string = handle_string(); return STRING; } + YY_BREAK +case 9: +/* rule 9 can match eol */ +YY_RULE_SETUP +#line 78 "sel-lex.l" +{ ++lineno; } + YY_BREAK +case 10: +YY_RULE_SETUP +#line 79 "sel-lex.l" +{ return *yytext; } + YY_BREAK +case 11: +YY_RULE_SETUP +#line 80 "sel-lex.l" +; + YY_BREAK +case 12: +YY_RULE_SETUP +#line 81 "sel-lex.l" +ECHO; + YY_BREAK +#line 844 "heimdal/lib/hx509/sel-lex.c" +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_c_buf_p); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( yywrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (void) +{ + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = (yytext_ptr); + register int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), (size_t) num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart(yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + } + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (void) +{ + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = (yy_start); + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 36 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ + register int yy_is_jam; + register char *yy_cp = (yy_c_buf_p); + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 36 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 35); + + return yy_is_jam ? 0 : yy_current_state; +} + + static void yyunput (int c, register char * yy_bp ) +{ + register char *yy_cp; + + yy_cp = (yy_c_buf_p); + + /* undo effects of setting up yytext */ + *yy_cp = (yy_hold_char); + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = (yy_n_chars) + 2; + register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; + register char *source = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; + + while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + (yytext_ptr) = yy_bp; + (yy_hold_char) = *yy_cp; + (yy_c_buf_p) = yy_cp; +} + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (yy_c_buf_p) - (yytext_ptr); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart(yyin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( ) ) + return EOF; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve yytext */ + (yy_hold_char) = *++(yy_c_buf_p); + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer(yyin,YY_BUF_SIZE ); + } + + yy_init_buffer(YY_CURRENT_BUFFER,input_file ); + yy_load_buffer_state( ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +static void yy_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer(b,file ); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * + */ + void yy_delete_buffer (YY_BUFFER_STATE b ) +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree((void *) b->yy_ch_buf ); + + yyfree((void *) b ); +} + +#ifndef __cplusplus +extern int isatty (int ); +#endif /* __cplusplus */ + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) + +{ + int oerrno = errno; + + yy_flush_buffer(b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void yy_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void yypop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (void) +{ + int num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; + (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer(b ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (yyconst char * yystr ) +{ + + return yy_scan_bytes(yystr,strlen(yystr) ); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param bytes the byte buffer to scan + * @param len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = _yybytes_len + 2; + buf = (char *) yyalloc(n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer(buf,n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error (yyconst char* msg ) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = (yy_hold_char); \ + (yy_c_buf_p) = yytext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int yyget_lineno (void) +{ + + return yylineno; +} + +/** Get the input stream. + * + */ +FILE *yyget_in (void) +{ + return yyin; +} + +/** Get the output stream. + * + */ +FILE *yyget_out (void) +{ + return yyout; +} + +/** Get the length of the current token. + * + */ +int yyget_leng (void) +{ + return yyleng; +} + +/** Get the current token. + * + */ + +char *yyget_text (void) +{ + return yytext; +} + +/** Set the current line number. + * @param line_number + * + */ +void yyset_lineno (int line_number ) +{ + + yylineno = line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param in_str A readable stream. + * + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * in_str ) +{ + yyin = in_str ; +} + +void yyset_out (FILE * out_str ) +{ + yyout = out_str ; +} + +int yyget_debug (void) +{ + return yy_flex_debug; +} + +void yyset_debug (int bdebug ) +{ + yy_flex_debug = bdebug ; +} + +static int yy_init_globals (void) +{ + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + (yy_buffer_stack) = 0; + (yy_buffer_stack_top) = 0; + (yy_buffer_stack_max) = 0; + (yy_c_buf_p) = (char *) 0; + (yy_init) = 0; + (yy_start) = 0; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = (FILE *) 0; + yyout = (FILE *) 0; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(); + } + + /* Destroy the stack itself. */ + yyfree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( ); + + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) +{ + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * s ) +{ + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size ) +{ + return (void *) malloc( size ); +} + +void *yyrealloc (void * ptr, yy_size_t size ) +{ + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); +} + +void yyfree (void * ptr ) +{ + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 81 "sel-lex.l" + + + +static char * +handle_string(void) +{ + char x[1024]; + int i = 0; + int c; + int quote = 0; + while((c = input()) != EOF){ + if(quote) { + x[i++] = '\\'; + x[i++] = c; + quote = 0; + continue; + } + if(c == '\n'){ + _hx509_sel_yyerror("unterminated string"); + lineno++; + break; + } + if(c == '\\'){ + quote++; + continue; + } + if(c == '\"') + break; + x[i++] = c; + } + x[i] = '\0'; + return strdup(x); +} + +int +yywrap () +{ + return 1; +} + +static int +lex_input(char *buf, int max_size) +{ + int n; + + n = _hx509_expr_input.length - _hx509_expr_input.offset; + if (max_size < n) + n = max_size; + if (n <= 0) + return YY_NULL; + + memcpy(buf, _hx509_expr_input.buf + _hx509_expr_input.offset, n); + _hx509_expr_input.offset += n; + + return n; +} + diff --git a/source4/heimdal/lib/hx509/sel-lex.l b/source4/heimdal/lib/hx509/sel-lex.l new file mode 100644 index 0000000000..53944897f9 --- /dev/null +++ b/source4/heimdal/lib/hx509/sel-lex.l @@ -0,0 +1,135 @@ +%{ +/* + * Copyright (c) 2004, 2008 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#undef ECHO + +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include "sel.h" +#include "sel-gram.h" +unsigned lineno = 1; + +static char * handle_string(void); +static int lex_input(char *, int); + +struct hx_expr_input _hx509_expr_input; + +#define YY_NO_UNPUT 1 + +#undef YY_INPUT +#define YY_INPUT(buf,res,maxsize) (res = lex_input(buf, maxsize)) + +#undef ECHO + +%} +%% + +TRUE { return kw_TRUE; } +FALSE { return kw_FALSE; } +AND { return kw_AND; } +OR { return kw_OR; } +IN { return kw_IN; } +TAILMATCH { return kw_TAILMATCH; } + +[A-Za-z][-A-Za-z0-9_]* { + yylval.string = strdup ((const char *)yytext); + return IDENTIFIER; + } +"\"" { yylval.string = handle_string(); return STRING; } +\n { ++lineno; } +[,.!={}()%] { return *yytext; } +[ \t] ; +%% + +static char * +handle_string(void) +{ + char x[1024]; + int i = 0; + int c; + int quote = 0; + while((c = input()) != EOF){ + if(quote) { + x[i++] = '\\'; + x[i++] = c; + quote = 0; + continue; + } + if(c == '\n'){ + _hx509_sel_yyerror("unterminated string"); + lineno++; + break; + } + if(c == '\\'){ + quote++; + continue; + } + if(c == '\"') + break; + x[i++] = c; + } + x[i] = '\0'; + return strdup(x); +} + +int +yywrap () +{ + return 1; +} + +static int +lex_input(char *buf, int max_size) +{ + int n; + + n = _hx509_expr_input.length - _hx509_expr_input.offset; + if (max_size < n) + n = max_size; + if (n <= 0) + return YY_NULL; + + memcpy(buf, _hx509_expr_input.buf + _hx509_expr_input.offset, n); + _hx509_expr_input.offset += n; + + return n; +} diff --git a/source4/heimdal/lib/hx509/sel.c b/source4/heimdal/lib/hx509/sel.c new file mode 100644 index 0000000000..0e68f8ba5d --- /dev/null +++ b/source4/heimdal/lib/hx509/sel.c @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2008 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hx_locl.h" + +struct hx_expr * +_hx509_make_expr(enum hx_expr_op op, void *arg1, void *arg2) +{ + struct hx_expr *expr; + + expr = malloc(sizeof(*expr)); + if (expr == NULL) + return NULL; + expr->op = op; + expr->arg1 = arg1; + expr->arg2 = arg2; + + return expr; +} + +static const char * +eval_word(hx509_context context, hx509_env env, struct hx_expr *word) +{ + switch (word->op) { + case expr_STRING: + return word->arg1; + case expr_VAR: + if (word->arg2 == NULL) + return hx509_env_find(context, env, word->arg1); + + env = hx509_env_find_binding(context, env, word->arg1); + if (env == NULL) + return NULL; + + return eval_word(context, env, word->arg2); + default: + return NULL; + } +} + +static hx509_env +find_variable(hx509_context context, hx509_env env, struct hx_expr *word) +{ + assert(word->op == expr_VAR); + + if (word->arg2 == NULL) + return hx509_env_find_binding(context, env, word->arg1); + + env = hx509_env_find_binding(context, env, word->arg1); + if (env == NULL) + return NULL; + return find_variable(context, env, word->arg2); +} + +static int +eval_comp(hx509_context context, hx509_env env, struct hx_expr *expr) +{ + switch (expr->op) { + case comp_NE: + case comp_EQ: + case comp_TAILEQ: { + const char *s1, *s2; + int ret; + + s1 = eval_word(context, env, expr->arg1); + s2 = eval_word(context, env, expr->arg2); + + if (s1 == NULL || s2 == NULL) + return FALSE; + + if (expr->op == comp_TAILEQ) { + size_t len1 = strlen(s1); + size_t len2 = strlen(s2); + + if (len1 < len2) + return 0; + ret = strcmp(s1 + (len1 - len2), s2) == 0; + } else { + ret = strcmp(s1, s2) == 0; + if (expr->op == comp_NE) + ret = !ret; + } + return ret; + } + case comp_IN: { + struct hx_expr *subexpr; + const char *w, *s1; + + w = eval_word(context, env, expr->arg1); + + subexpr = expr->arg2; + + if (subexpr->op == expr_WORDS) { + while (subexpr) { + s1 = eval_word(context, env, subexpr->arg1); + if (strcmp(w, s1) == 0) + return TRUE; + subexpr = subexpr->arg2; + } + } else if (subexpr->op == expr_VAR) { + hx509_env subenv; + + subenv = find_variable(context, env, subexpr); + if (subenv == NULL) + return FALSE; + + while (subenv) { + if (subenv->type != env_string) + continue; + if (strcmp(w, subenv->name) == 0) + return TRUE; + if (strcmp(w, subenv->u.string) == 0) + return TRUE; + subenv = subenv->next; + } + + } else + _hx509_abort("hx509 eval IN unknown op: %d", (int)subexpr->op); + + return FALSE; + } + default: + _hx509_abort("hx509 eval expr with unknown op: %d", (int)expr->op); + } + return FALSE; +} + +int +_hx509_expr_eval(hx509_context context, hx509_env env, struct hx_expr *expr) +{ + switch (expr->op) { + case op_TRUE: + return 1; + case op_FALSE: + return 0; + case op_NOT: + return ! _hx509_expr_eval(context, env, expr->arg1); + case op_AND: + return _hx509_expr_eval(context, env, expr->arg1) && + _hx509_expr_eval(context, env, expr->arg2); + case op_OR: + return _hx509_expr_eval(context, env, expr->arg1) || + _hx509_expr_eval(context, env, expr->arg2); + case op_COMP: + return eval_comp(context, env, expr->arg1); + default: + _hx509_abort("hx509 eval expr with unknown op: %d", (int)expr->op); + } +} + +void +_hx509_expr_free(struct hx_expr *expr) +{ + switch (expr->op) { + case expr_STRING: + case expr_NUMBER: + free(expr->arg1); + break; + case expr_WORDS: + case expr_FUNCTION: + case expr_VAR: + free(expr->arg1); + if (expr->arg2) + _hx509_expr_free(expr->arg2); + break; + default: + if (expr->arg1) + _hx509_expr_free(expr->arg1); + if (expr->arg2) + _hx509_expr_free(expr->arg2); + break; + } + free(expr); +} + +struct hx_expr * +_hx509_expr_parse(const char *buf) +{ + _hx509_expr_input.buf = buf; + _hx509_expr_input.length = strlen(buf); + _hx509_expr_input.offset = 0; + _hx509_expr_input.expr = NULL; + + if (_hx509_expr_input.error) { + free(_hx509_expr_input.error); + _hx509_expr_input.error = NULL; + } + + yyparse(); + + return _hx509_expr_input.expr; +} + +void +_hx509_sel_yyerror (char *s) +{ + if (_hx509_expr_input.error) + free(_hx509_expr_input.error); + + _hx509_expr_input.error = strdup(s); +} + diff --git a/source4/heimdal/lib/hx509/sel.h b/source4/heimdal/lib/hx509/sel.h new file mode 100644 index 0000000000..ce6c3636bc --- /dev/null +++ b/source4/heimdal/lib/hx509/sel.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2008 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +enum hx_expr_op { + op_TRUE, + op_FALSE, + op_NOT, + op_AND, + op_OR, + op_COMP, + + comp_EQ, + comp_NE, + comp_IN, + comp_TAILEQ, + + expr_NUMBER, + expr_STRING, + expr_FUNCTION, + expr_VAR, + expr_WORDS +}; + +struct hx_expr { + enum hx_expr_op op; + void *arg1; + void *arg2; +}; + +struct hx_expr_input { + const char *buf; + size_t length; + size_t offset; + struct hx_expr *expr; + char *error; +}; + +extern struct hx_expr_input _hx509_expr_input; + +#define yyparse _hx509_sel_yyparse +#define yylex _hx509_sel_yylex +#define yyerror _hx509_sel_yyerror +#define yylval _hx509_sel_yylval +#define yychar _hx509_sel_yychar +#define yydebug _hx509_sel_yydebug +#define yynerrs _hx509_sel_yynerrs +#define yywrap _hx509_sel_yywrap + +int _hx509_sel_yyparse(void); +int _hx509_sel_yylex(void); +void _hx509_sel_yyerror(char *); + diff --git a/source4/heimdal/lib/hx509/test_name.c b/source4/heimdal/lib/hx509/test_name.c new file mode 100644 index 0000000000..7326fe632a --- /dev/null +++ b/source4/heimdal/lib/hx509/test_name.c @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hx_locl.h" +RCSID("$Id$"); + +static int +test_name(hx509_context context, const char *name) +{ + hx509_name n; + char *s; + int ret; + + ret = hx509_parse_name(context, name, &n); + if (ret) + return 1; + + ret = hx509_name_to_string(n, &s); + if (ret) + return 1; + + if (strcmp(s, name) != 0) + return 1; + + hx509_name_free(&n); + free(s); + + return 0; +} + +static int +test_name_fail(hx509_context context, const char *name) +{ + hx509_name n; + + if (hx509_parse_name(context, name, &n) == HX509_NAME_MALFORMED) + return 0; + hx509_name_free(&n); + return 1; +} + +static int +test_expand(hx509_context context, const char *name, const char *expected) +{ + hx509_env env = NULL; + hx509_name n; + char *s; + int ret; + + hx509_env_add(context, &env, "uid", "lha"); + + ret = hx509_parse_name(context, name, &n); + if (ret) + return 1; + + ret = hx509_name_expand(context, n, env); + hx509_env_free(&env); + if (ret) + return 1; + + ret = hx509_name_to_string(n, &s); + hx509_name_free(&n); + if (ret) + return 1; + + ret = strcmp(s, expected) != 0; + free(s); + if (ret) + return 1; + + return 0; +} + +int +main(int argc, char **argv) +{ + hx509_context context; + int ret = 0; + + ret = hx509_context_init(&context); + if (ret) + errx(1, "hx509_context_init failed with %d", ret); + + ret += test_name(context, "CN=foo,C=SE"); + ret += test_name(context, "CN=foo,CN=kaka,CN=FOO,DC=ad1,C=SE"); + ret += test_name(context, "1.2.3.4=foo,C=SE"); + ret += test_name_fail(context, "="); + ret += test_name_fail(context, "CN=foo,=foo"); + ret += test_name_fail(context, "CN=foo,really-unknown-type=foo"); + + ret += test_expand(context, "UID=${uid},C=SE", "UID=lha,C=SE"); + ret += test_expand(context, "UID=foo${uid},C=SE", "UID=foolha,C=SE"); + ret += test_expand(context, "UID=${uid}bar,C=SE", "UID=lhabar,C=SE"); + ret += test_expand(context, "UID=f${uid}b,C=SE", "UID=flhab,C=SE"); + ret += test_expand(context, "UID=${uid}${uid},C=SE", "UID=lhalha,C=SE"); + ret += test_expand(context, "UID=${uid}{uid},C=SE", "UID=lha{uid},C=SE"); + + hx509_context_free(&context); + + return ret; +} |