/** * * /brief functions for DNSSEC trust anchor management */ /* * Copyright (c) 2017, NLnet Labs, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 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. * * Neither the names of the copyright holders 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 COPYRIGHT HOLDERS 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 Verisign, Inc. 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 "config.h" #include "debug.h" #include "anchor.h" #include #include #include #include #include #define P7SIGNER "dnssec@iana.org" static const char* get_builtin_cert(void) { return /* The ICANN CA fetched at 24 Sep 2010. Valid to 2028 */ "-----BEGIN CERTIFICATE-----\n" "MIIDdzCCAl+gAwIBAgIBATANBgkqhkiG9w0BAQsFADBdMQ4wDAYDVQQKEwVJQ0FO\n" "TjEmMCQGA1UECxMdSUNBTk4gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNV\n" "BAMTDUlDQU5OIFJvb3QgQ0ExCzAJBgNVBAYTAlVTMB4XDTA5MTIyMzA0MTkxMloX\n" "DTI5MTIxODA0MTkxMlowXTEOMAwGA1UEChMFSUNBTk4xJjAkBgNVBAsTHUlDQU5O\n" "IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1JQ0FOTiBSb290IENB\n" "MQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKDb\n" "cLhPNNqc1NB+u+oVvOnJESofYS9qub0/PXagmgr37pNublVThIzyLPGCJ8gPms9S\n" "G1TaKNIsMI7d+5IgMy3WyPEOECGIcfqEIktdR1YWfJufXcMReZwU4v/AdKzdOdfg\n" "ONiwc6r70duEr1IiqPbVm5T05l1e6D+HkAvHGnf1LtOPGs4CHQdpIUcy2kauAEy2\n" "paKcOcHASvbTHK7TbbvHGPB+7faAztABLoneErruEcumetcNfPMIjXKdv1V1E3C7\n" "MSJKy+jAqqQJqjZoQGB0necZgUMiUv7JK1IPQRM2CXJllcyJrm9WFxY0c1KjBO29\n" "iIKK69fcglKcBuFShUECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B\n" "Af8EBAMCAf4wHQYDVR0OBBYEFLpS6UmDJIZSL8eZzfyNa2kITcBQMA0GCSqGSIb3\n" "DQEBCwUAA4IBAQAP8emCogqHny2UYFqywEuhLys7R9UKmYY4suzGO4nkbgfPFMfH\n" "6M+Zj6owwxlwueZt1j/IaCayoKU3QsrYYoDRolpILh+FPwx7wseUEV8ZKpWsoDoD\n" "2JFbLg2cfB8u/OlE4RYmcxxFSmXBg0yQ8/IoQt/bxOcEEhhiQ168H2yE5rxJMt9h\n" "15nu5JBSewrCkYqYYmaxyOC3WrVGfHZxVI7MpIFcGdvSb2a1uyuua8l0BKgk3ujF\n" "0/wsHNeP22qNyVO+XVBzrM8fk8BSUFuiT/6tZTYXRtEt5aKQZgXbKU5dUF3jT9qg\n" "j/Br5BZw3X/zd325TvnswzMC1+ljLzHnQGGk\n" "-----END CERTIFICATE-----\n"; } /* get key usage out of its extension, returns 0 if no key_usage extension */ static unsigned long get_usage_of_ex(X509* cert) { unsigned long val = 0; ASN1_BIT_STRING* s; if((s=X509_get_ext_d2i(cert, NID_key_usage, NULL, NULL))) { if(s->length > 0) { val = s->data[0]; if(s->length > 1) val |= s->data[1] << 8; } ASN1_BIT_STRING_free(s); } return val; } /** get valid signers from the list of signers in the signature */ static STACK_OF(X509)* get_valid_signers(PKCS7* p7, const char* p7signer) { const int verb = 5; int i; STACK_OF(X509)* validsigners = sk_X509_new_null(); STACK_OF(X509)* signers = PKCS7_get0_signers(p7, NULL, 0); unsigned long usage = 0; if(!validsigners) { if(verb) printf("out of memory\n"); sk_X509_free(signers); return NULL; } if(!signers) { if(verb) printf("no signers in pkcs7 signature\n"); sk_X509_free(validsigners); return NULL; } for(i=0; i= 3 && X509_NAME_get_text_by_NID(nm, NID_commonName, buf, (int)sizeof(buf))) printf("commonName: %s\n", buf); if(verb >= 3 && X509_NAME_get_text_by_NID(nm, NID_pkcs9_emailAddress, buf, (int)sizeof(buf))) printf("emailAddress: %s\n", buf); } if(verb) { int ku_loc = X509_get_ext_by_NID( sk_X509_value(signers, i), NID_key_usage, -1); if(verb >= 3 && ku_loc >= 0) { X509_EXTENSION *ex = X509_get_ext( sk_X509_value(signers, i), ku_loc); if(ex) { printf("keyUsage: "); X509V3_EXT_print_fp(stdout, ex, 0, 0); printf("\n"); } } } if(!p7signer || strcmp(p7signer, "")==0) { /* there is no name to check, return all records */ if(verb) printf("did not check commonName of signer\n"); } else { if(!X509_NAME_get_text_by_NID(nm, NID_pkcs9_emailAddress, buf, (int)sizeof(buf))) { if(verb) printf("removed cert with no name\n"); continue; /* no name, no use */ } if(strcmp(buf, p7signer) != 0) { if(verb) printf("removed cert with wrong name\n"); continue; /* wrong name, skip it */ } } /* check that the key usage allows digital signatures * (the p7s) */ usage = get_usage_of_ex(sk_X509_value(signers, i)); if(!(usage & KU_DIGITAL_SIGNATURE)) { if(verb) printf("removed cert with no key usage Digital Signature allowed\n"); continue; } /* we like this cert, add it to our list of valid * signers certificates */ sk_X509_push(validsigners, sk_X509_value(signers, i)); } sk_X509_free(signers); return validsigners; } static int verify_p7sig(BIO* data, BIO* p7s, X509_STORE *store, const char* p7signer) { const int verb = 5; PKCS7* p7; STACK_OF(X509)* validsigners; int secure = 0; #ifdef X509_V_FLAG_CHECK_SS_SIGNATURE X509_VERIFY_PARAM* param = X509_VERIFY_PARAM_new(); if(!param) { if(verb) printf("out of memory\n"); X509_STORE_free(store); return 0; } /* do the selfcheck on the root certificate; it checks that the * input is valid */ X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CHECK_SS_SIGNATURE); if(store) X509_STORE_set1_param(store, param); #endif if(!store) { if(verb) printf("out of memory\n"); #ifdef X509_V_FLAG_CHECK_SS_SIGNATURE X509_VERIFY_PARAM_free(param); #endif return 0; } #ifdef X509_V_FLAG_CHECK_SS_SIGNATURE X509_VERIFY_PARAM_free(param); #endif (void)BIO_reset(p7s); (void)BIO_reset(data); /* convert p7s to p7 (the signature) */ p7 = d2i_PKCS7_bio(p7s, NULL); if(!p7) { if(verb) printf("could not parse p7s signature file\n"); X509_STORE_free(store); return 0; } if(verb >= 2) printf("parsed the PKCS7 signature\n"); /* check what is in the Subject name of the certificates, * and build a stack that contains only the right certificates */ validsigners = get_valid_signers(p7, p7signer); if(!validsigners) { PKCS7_free(p7); return 0; } BIO *out = BIO_new_fd(fileno(stdout), BIO_NOCLOSE); BIO_printf(out, "Hello World\n"); if(PKCS7_verify(p7, validsigners, store, data, out, PKCS7_NOINTERN) == 1) { secure = 1; if(verb) printf("the PKCS7 signature verified\n"); } else { if(verb) printf("the PKCS7 signature did not verify\n"); if(verb) { ERR_print_errors_fp(stdout); } } BIO_free(out); sk_X509_free(validsigners); PKCS7_free(p7); return secure; } void _getdns_context_equip_with_anchor(getdns_context *context) { char fn[1024]; int xml_fd, p7s_fd; int n; BIO *xml, *p7s, *crt; X509 *x; X509_STORE *store; char *crt_str; DEBUG_ANCHOR("entering %s\n", __FUNC__); n = snprintf( fn, sizeof(fn) , "%s/.getdns/root-anchors.xml", getenv("HOME")); if (n < 0 || n >= (int)sizeof(fn)) return; if ((xml_fd = open(fn, O_RDONLY)) < 0) return; (void) snprintf( fn, sizeof(fn) , "%s/.getdns/root-anchors.p7s", getenv("HOME")); if ((p7s_fd = open(fn, O_RDONLY)) < 0) { close(xml_fd); return; } if (!(xml = BIO_new_fd(xml_fd, 1))) { close(xml_fd); close(p7s_fd); return; } if (!(p7s = BIO_new_fd(p7s_fd, 1))) { BIO_free(xml); close(p7s_fd); return; } if (!(crt_str = strdup(get_builtin_cert()))) goto error_free_xml; if (!(crt = BIO_new_mem_buf(crt_str, (int)strlen(crt_str)))) goto error_free_str; if (!(store = X509_STORE_new())) goto error_free_crt; if (!(x = PEM_read_bio_X509(crt, NULL, 0, NULL))) goto error_free_store; if (!X509_STORE_add_cert(store, x)) goto error_free_store; if (verify_p7sig(xml, p7s, store, "dnssec@iana.org")) { DEBUG_ANCHOR("Verifying trust-anchors SUCCEEDED, Yay!\n"); ; } else { DEBUG_ANCHOR("Verifying trust-anchors failed!\n"); ; } error_free_store: X509_STORE_free(store); error_free_crt: BIO_free(crt); error_free_str: free(crt_str); error_free_xml: BIO_free(xml); BIO_free(p7s); } /* anchor.c */