Diego Biurrun has proposed merging lp:~hipl-core/hipl/certificate-exchange into lp:hipl. Requested reviews: HIPL core team (hipl-core) For more details, see: https://code.launchpad.net/~hipl-core/hipl/certificate-exchange/+merge/96786 .. and another version, now with David's exponential backoff branch already merged in via trunk. -- https://code.launchpad.net/~hipl-core/hipl/certificate-exchange/+merge/96786 Your team HIPL core team is requested to review the proposed merge of lp:~hipl-core/hipl/certificate-exchange into lp:hipl.
=== modified file 'Makefile.am' --- Makefile.am 2012-03-09 14:42:31 +0000 +++ Makefile.am 2012-03-09 16:18:06 +0000 @@ -31,10 +31,10 @@ EXTRA_DIST += .vimrc process_modules.py version.h EXTRA_DIST += debian doc patches packaging tools/bazaar tools/maintainer EXTRA_DIST += $(wildcard modules/*/module_info.xml) +EXTRA_DIST += $(wildcard $(addprefix $(srcdir)/test/lib/core/,*.pem)) EXTRA_DIST += $(wildcard $(addprefix $(srcdir)/tools/,*.cfg *.pl *.sh *.xml)) EXTRA_DIST += $(wildcard $(addprefix $(srcdir)/hipfw/,*.cfg)) EXTRA_DIST += $(HIPL_HEADER_LIST) -EXTRA_DIST += hipd/pisa.c hipfw/pisa.c hipfw/pisa_cert.c ### user programs ### bin_PROGRAMS = test/auth_performance \ @@ -122,6 +122,7 @@ hipd/user_ipsec_sadb_api.c \ modules/heartbeat/hipd/heartbeat.c \ modules/heartbeat_update/hipd/hb_update.c \ + modules/cert/hipd/cert.c \ modules/update/hipd/update.c \ modules/update/hipd/update_builder.c \ modules/update/hipd/update_locator.c \ @@ -139,6 +140,7 @@ tools/nsupdate/nsupdate.conf hipfw_hipfw_sources = hipfw/cache.c \ + hipfw/cert.c \ hipfw/dlist.c \ hipfw/esp_prot_api.c \ hipfw/esp_prot_config.c \ @@ -170,6 +172,7 @@ lib_core_libhipcore_la_SOURCES = lib/core/builder.c \ lib/core/capability.c \ + lib/core/cert.c \ lib/core/certtools.c \ lib/core/conf.c \ lib/core/crypto.c \ @@ -220,6 +223,7 @@ $(hipfw_hipfw_sources) test_check_lib_core_SOURCES = test/check_lib_core.c \ + test/lib/core/cert.c \ test/lib/core/crypto.c \ test/lib/core/hit.c \ test/lib/core/hostid.c \ === modified file 'configure.ac' --- configure.ac 2012-03-09 14:42:31 +0000 +++ configure.ac 2012-03-09 16:18:06 +0000 @@ -101,6 +101,8 @@ AC_DEFINE_UNQUOTED(HIPL_LOCKDIR, "$(eval echo $lockdir)") AH_TEMPLATE(HIPL_LOCKDIR, [default lock file location]) +AC_DEFINE_UNQUOTED(HIPL_SOURCEDIR, "$(eval echo $srcdir)") +AH_TEMPLATE(HIPL_SOURCEDIR, [HIPL source directory location]) # Make sure that pythondir does not contain ${prefix} or similar so that it # can be substituted into our Python scripts. AC_SUBST(pythondir, $(eval echo $pythondir)) === modified file 'doc/HOWTO.xml.in' --- doc/HOWTO.xml.in 2012-01-25 10:44:48 +0000 +++ doc/HOWTO.xml.in 2012-03-09 16:18:06 +0000 @@ -2532,6 +2532,81 @@ </chapter> +<chapter id="ch_cert_exchange"> + <title>Certificate Exchange</title> + + <section id="ch_cert_functionality"> + <title>Provided functionality</title> + <para>At the protocol level, HIPL implements the HIP_CERT parameter as + defined in RFC 6253. Specifically, X509 certificates are supported, whereas + no functionality is provided for SPKI certificates. HIP does not define + the usage of certificates in the protocol exchange. In HIPL, we currently + add certificates to the R2 in the BEX and the second update message U2 + (e.g. during the mobility-triggered update exchange). The HIP firewall + verifies the certificate in the R2 and U2 if configured accordingly. If your + use case requires certificates to be included in different messages, changes + in the source code will be necessary.</para> + </section> + + <section id="ch_cert_usage"> + <title>Setting up a network scenario with certificates</title> + <para>For the following guide, we assume a setup consisting of three + machines (A, B, and C), where B is situated on the forwarding path between + A and C. A and C execute the hipd, while B executes the hipfw. The hipd + automatically adds a certificate to R2s and U2s if a file called + host-cert.der is located in @sysconfdir@. The hipfw checks for certificates + if certificate-based rules exist in @sysconfdir@/hipfw.conf.</para> + <para>To set up certificates, you have to perform the following steps:</para> + <itemizedlist> + <listitem><para>Generate a root certificate in PEM format (e.g. with + OpenSSL).</para></listitem> + <listitem><para>Copy the root certificate to @sysconfdir@/ca-root-cert.pem on + B.</para></listitem> + <listitem><para>Add a certificate-based rule to @sysconfdir@/hipfw.conf on B + (e.g. 'FORWARD -cert @sysconfdir@/ca-root-cert.pem ACCEPT').</para> + </listitem> + <listitem><para>Generate a certificate in DER format for the HI used by C + and place it at @sysconfdir@/host-cert.der on C.</para></listitem> + <listitem><para>No changes are required for A if its role is restricted to + an initiator. Otherwise, A requires the same setup as C.</para></listitem> + </itemizedlist> + <para>The setup of a certificate-based network scenario can be vastly + simplified by using a fourth HIPL-enabled host D as the certificate + authority. In this case, generate the root certificate on D by running: + </para> + <para><programlisting> + openssl req -new -x509 -key @sysconfdir@/hip_host_rsa_key_pub -out ca-root-cert.pem + </programlisting></para> + <para>NOTE: Apart from the common name, all fields in the certificate can be + empty. The common name must contain the HIT of D.</para> + <para>Now, run a BEX between C and D. After the BEX has succeeded, run the + following command on D as root in order to generate the certificate for C: + </para> + <para><programlisting> + hipconf acquire certificate #HIT_OF_C# > host-cert.der + </programlisting></para> + <para>As the final step, copy the certificates to the places mentioned in + the guide above.</para> + </section> + + <section id="ch_cert_troubleshooting"> + <title>Troubleshooting</title> + <para>There are a couple of things that may go wrong in a certificate-based + setup. The most common ones are listed below. Please, make sure to check + the listed items before asking questions on the hipl-users mailing list.</para> + <itemizedlist> + <listitem><para>Ensure that the certificates have not yet expired (e.g. + by checking the certificate lifetime with OpenSSL).</para></listitem> + <listitem><para>Validate that the time is set correctly on all hosts. + </para></listitem> + <listitem><para>Verify the certificate chain consisting of + root-ca-cert.pem and host-cert.der with an external tool (e.g. with + OpenSSL).</para></listitem> + </itemizedlist> + </section> + +</chapter> <!-- ch_cert_exchange --> + <chapter id="ch_exp_extensions"> <title>Other Experimental HIP Extensions</title> === modified file 'hipd/cert.c' --- hipd/cert.c 2012-03-01 14:06:24 +0000 +++ hipd/cert.c 2012-03-09 16:18:06 +0000 @@ -49,10 +49,13 @@ #include "lib/core/common.h" #include "lib/core/crypto.h" #include "lib/core/debug.h" +#include "lib/core/hit.h" #include "lib/core/ife.h" +#include "lib/core/prefix.h" #include "lib/core/protodefs.h" #include "lib/core/straddr.h" #include "lib/core/gpl/pk.h" +#include "hadb.h" #include "hidb.h" #include "cert.h" @@ -661,20 +664,19 @@ */ int hip_cert_x509v3_handle_request_to_sign(struct hip_common *msg) { - int err = 0, i = 0, nid = 0, ret = 0, secs = 0, algo = 0; - CONF *conf; + int err = 0, i = 0, ret = 0, secs = 0, algo = 0; + CONF *conf = NULL; CONF_VALUE *item; STACK_OF(CONF_VALUE) *sec_general = NULL; - STACK_OF(CONF_VALUE) *sec_name = NULL; STACK_OF(CONF_VALUE) *sec_ext = NULL; - X509_REQ *req = NULL; X509_NAME *issuer = NULL; X509_NAME *subj = NULL; X509_EXTENSION *ext = NULL; STACK_OF(X509_EXTENSION) *extlist = NULL; - X509_NAME_ENTRY *ent; - EVP_PKEY *pkey; + X509_NAME_ENTRY *ent = NULL; + EVP_PKEY *pkey = NULL; + EVP_PKEY *sig_key = NULL; /** XX TODO THIS should come from a configuration file * monotonically increasing counter */ long serial = 0; @@ -682,89 +684,108 @@ X509 *cert; X509V3_CTX ctx; const struct hip_cert_x509_req *subject; - char subject_hit[41]; - char issuer_hit[41]; - char ialtname[45]; - char saltname[45]; - struct in6_addr *issuer_hit_n; - struct hip_host_id *host_id; - RSA *rsa = NULL; - DSA *dsa = NULL; + char subject_hit[INET6_ADDRSTRLEN]; + char issuer_hit[INET6_ADDRSTRLEN] = { 0 }; + char ialtname[INET6_ADDRSTRLEN + 3]; + char saltname[INET6_ADDRSTRLEN + 3]; + hip_hit_t *issuer_hit_n = NULL; + struct hip_host_id *host_id = NULL; + void *key = NULL; unsigned char *der_cert = NULL; int der_cert_len = 0; char arg1[21]; char arg2[21]; - - HIP_IFEL(!(issuer_hit_n = malloc(sizeof(struct in6_addr))), -1, + const struct hip_tlv_common *validity_param = NULL; + time_t expiry_time = 0; + const struct hip_hadb_state *ha = NULL; + + + HIP_IFEL(!(issuer_hit_n = malloc(sizeof(hip_hit_t))), -1, "Malloc for subject failed\n"); - HIP_IFEL(!(pkey = malloc(sizeof(EVP_PKEY))), -1, - "Malloc for pkey failed\n"); + HIP_IFEL(!(pkey = EVP_PKEY_new()), -1, + "Allocating subject pub key failed\n"); + HIP_IFEL(!(sig_key = EVP_PKEY_new()), -1, + "Allocating issuer signature key failed\n"); - OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); HIP_DEBUG("Reading configuration file (%s)\n", HIP_CERT_CONF_PATH); conf = hip_open_conf(HIP_CERT_CONF_PATH); sec_general = hip_read_conf_section("hip_x509v3", conf); - sec_name = hip_read_conf_section("hip_x509v3_name", conf); sec_ext = hip_read_conf_section("hip_x509v3_extensions", conf); - NCONF_free(conf); - /* Get the general information */ + /* Fail if the hip_x509v3 or hip_x509v3_name sections are not found. */ HIP_IFEL(sec_general == NULL, -1, "Failed to load general certificate information\n"); - HIP_IFEL(!(req = X509_REQ_new()), -1, "Failed to create X509_REQ object"); - - HIP_IFEL(sec_name == NULL, -1, - "Failed to load issuer naming information for the certificate\n"); /* Issuer naming */ - if (sec_general != NULL) { - /* Loop through the conf stack for general information */ - extlist = sk_X509_EXTENSION_new_null(); - for (i = 0; i < sk_CONF_VALUE_num(sec_general); i++) { - item = sk_CONF_VALUE_value(sec_general, i); - if (!strcmp(item->name, "issuerhit")) { - strcpy(issuer_hit, item->value); - ret = inet_pton(AF_INET6, item->value, issuer_hit_n); - HIP_IFEL(ret < 0 && errno == EAFNOSUPPORT, -1, - "Failed to convert issuer HIT to hip_hit_t\n"); - HIP_DEBUG_HIT("Issuer HIT", issuer_hit_n); - HIP_IFEL(!inet_ntop(AF_INET6, issuer_hit_n, - issuer_hit, sizeof(issuer_hit)), - -1, "Failed to convert subject hit to " - "presentation format\n"); - } - if (!strcmp(item->name, "days")) { - secs = HIP_CERT_DAY * atoi(item->value); - } - } - } - HIP_IFEL(!(issuer = X509_NAME_new()), -1, "Failed to set create issuer name"); - nid = OBJ_txt2nid("commonName"); - HIP_IFEL(nid == NID_undef, -1, "NID text not defined\n"); - HIP_IFEL(!(ent = X509_NAME_ENTRY_create_by_NID(NULL, nid, MBSTRING_ASC, + /* Loop through the conf stack for general information */ + for (i = 0; i < sk_CONF_VALUE_num(sec_general); i++) { + item = sk_CONF_VALUE_value(sec_general, i); + if (!strcmp(item->name, "issuerhit")) { + ret = inet_pton(AF_INET6, item->value, issuer_hit_n); + HIP_IFEL(ret != 1, -1, + "Failed to convert issuer HIT to hip_hit_t\n"); + HIP_DEBUG_HIT("Issuer HIT", issuer_hit_n); + hip_convert_hit_to_str(issuer_hit_n, NULL, issuer_hit); + } + if (!strcmp(item->name, "days")) { + secs = HIP_CERT_DAY * atoi(item->value); + } + } + + /* In case no issuerhit was in the config, just use our default HIT. */ + if (issuer_hit[0] == 0) { + HIP_IFEL(hip_get_default_hit(issuer_hit_n), -1, + "Unable to determine default HIT\n"); + hip_convert_hit_to_str(issuer_hit_n, NULL, issuer_hit); + } + + HIP_IFEL(!(issuer = X509_NAME_new()), -1, "Failed to create issuer name"); + + HIP_IFEL(!(ent = X509_NAME_ENTRY_create_by_NID(NULL, NID_commonName, MBSTRING_ASC, (unsigned char *) issuer_hit, -1)), -1, "Failed to create name entry for issuer\n"); HIP_IFEL(X509_NAME_add_entry(issuer, ent, -1, 0) != 1, -1, "Failed to add entry to issuer name\n"); + X509_NAME_ENTRY_free(ent); /* "ent" var will be re-used */ + ent = NULL; + /* Subject naming */ /* Get the subject hit from msg */ HIP_IFEL(!(subject = hip_get_param(msg, HIP_PARAM_CERT_X509_REQ)), - -1, "No cert_info struct found\n"); - HIP_IFEL(!inet_ntop(AF_INET6, &subject->addr, subject_hit, sizeof(subject_hit)), - -1, "Failed to convert subject hit to presentation format\n"); - HIP_IFEL(!(subj = X509_NAME_new()), -1, "Failed to set create subject name"); - nid = OBJ_txt2nid("commonName"); - HIP_IFEL(nid == NID_undef, -1, "NID text not defined\n"); - HIP_IFEL(!(ent = X509_NAME_ENTRY_create_by_NID(NULL, nid, MBSTRING_ASC, + -1, "No cert_x509_req struct found\n"); + HIP_IFEL(!ipv6_addr_is_hit(&subject->addr), + -1, "Address in certificate request is no HIT.\n"); + HIP_DEBUG_HIT("Subject HIT", &subject->addr); + hip_convert_hit_to_str(&subject->addr, NULL, subject_hit); + + HIP_IFEL(!(subj = X509_NAME_new()), -1, "Failed to create subject name"); + + HIP_IFEL(!(ent = X509_NAME_ENTRY_create_by_NID(NULL, NID_commonName, MBSTRING_ASC, (unsigned char *) subject_hit, -1)), -1, "Failed to create name entry for subject\n"); HIP_IFEL(X509_NAME_add_entry(subj, ent, -1, 0) != 1, -1, "Failed to add entry to subject name\n"); - HIP_IFEL(X509_REQ_set_subject_name(req, subj) != 1, -1, - "Failed to add subject name to certificate request\n"); + + /* Were we sent a timestamp which indicates a requested cert validity? */ + validity_param = hip_get_param(msg, HIP_PARAM_UINT); + + if (validity_param) { + const uint32_t *valid_until_n = hip_get_param_contents_direct(validity_param); + const uint32_t valid_until_h = ntohl(*valid_until_n); + + /* If time_t is only 32 bits wide and signed, we cannot copy a value of + * valid_until_h which has its MSB set since it would be misunderstood + * as being negative; so only set the value if this is not the case. */ + if (!(sizeof(time_t) == 4 && ((time_t) -1 < 0) && + (0x80000000 & valid_until_h))) { + expiry_time = valid_until_h; + } else { + HIP_OUT_ERR(-1, "Received invalid timestamp parameter.\n"); + } + } /* XX TODO add a check to skip subjectAltName and issuerAltName because they are * already in use by with IP:<hit> stuff */ @@ -778,8 +799,6 @@ "Failed to create extension\n"); sk_X509_EXTENSION_push(extlist, ext); } - HIP_IFEL(!X509_REQ_add_extensions(req, extlist), -1, - "Failed to add extensions to the request\n"); } /** NOW WE ARE READY TO CREATE A CERTIFICATE FROM THE REQUEST */ @@ -797,33 +816,63 @@ "Failed to set subject name of certificate\n"); HIP_IFEL(X509_set_issuer_name(cert, issuer) != 1, -1, "Failed to set issuer name of certificate\n"); - HIP_IFEL(!X509_gmtime_adj(X509_get_notBefore(cert), 0), -1, + + X509_get_notBefore(cert)->type = V_ASN1_GENERALIZEDTIME; + X509_get_notAfter(cert)->type = V_ASN1_GENERALIZEDTIME; + + const time_t now = time(NULL); + time_t starttime = 0, endtime = 0; + time_t *starttime_p = NULL, *endtime_p = NULL; + + if (expiry_time) { + /* A specific expiry time is demanded by the caller. */ + if (now < expiry_time) { + /* Just set it up as wanted. */ + starttime = now; + endtime = expiry_time; + } else { + /* Just set the start time to one second before the expiry time. + * This yields a - syntactically - valid certificate. It is not + * our task to second-guess the motives for requesting an expiry + * time from the past. */ + if (expiry_time == 1) { + expiry_time++; /* another pathological case */ + } + starttime = expiry_time - 1; + endtime = expiry_time; + } + + starttime_p = &starttime; + endtime_p = &endtime; + secs = 0; + } else { + starttime_p = NULL; + endtime_p = NULL; + if (secs <= 0) { + secs = 10; /* and yet another one */ + } + } + + HIP_IFEL(!X509_time_adj(X509_get_notBefore(cert), 0, starttime_p), -1, "Error setting beginning time of the certificate"); - HIP_IFEL(!X509_gmtime_adj(X509_get_notAfter(cert), secs), -1, + HIP_IFEL(!X509_time_adj(X509_get_notAfter(cert), secs, endtime_p), -1, "Error setting ending time of the certificate"); - HIP_DEBUG("Getting the key\n"); - - HIP_IFEL(hip_get_host_id_and_priv_key(issuer_hit_n, - HIP_ANY_ALGO, - &host_id, - (void *) &rsa), - -1, "Private key not found\n"); - - algo = host_id->rdata.algorithm; - if (algo == HIP_HI_DSA) { - dsa = (DSA *) rsa; - } + /* Get the subject public key from HADB */ + HIP_IFEL(!(ha = hip_hadb_find_byhits(issuer_hit_n, &subject->addr)), + -1, "Could not retrieve host association for subject HIT\n"); + + algo = ha->peer_pub->rdata.algorithm; switch (algo) { case HIP_HI_RSA: - HIP_IFEL(!EVP_PKEY_assign_RSA(pkey, rsa), -1, + HIP_IFEL(!EVP_PKEY_set1_RSA(pkey, ha->peer_pub_key), -1, "Failed to convert RSA to EVP_PKEY\n"); HIP_IFEL(X509_set_pubkey(cert, pkey) != 1, -1, "Failed to set public key of the certificate\n"); break; case HIP_HI_DSA: - HIP_IFEL(!EVP_PKEY_assign_DSA(pkey, dsa), -1, + HIP_IFEL(!EVP_PKEY_set1_DSA(pkey, ha->peer_pub_key), -1, "Failed to convert DSA to EVP_PKEY\n"); HIP_IFEL(X509_set_pubkey(cert, pkey) != 1, -1, "Failed to set public key of the certificate\n"); @@ -926,7 +975,25 @@ HIP_OUT_ERR(-1, "Unknown algorithm\n"); } - HIP_IFEL(!X509_sign(cert, pkey, digest), -1, + /* Get the issuer key for signing */ + HIP_IFEL(hip_get_host_id_and_priv_key(issuer_hit_n, HIP_ANY_ALGO, + &host_id, &key), + -1, "Private key not found\n"); + + switch (host_id->rdata.algorithm) { + case HIP_HI_RSA: + HIP_IFEL(!EVP_PKEY_set1_RSA(sig_key, key), -1, + "Failed to convert RSA to EVP_PKEY\n"); + break; + case HIP_HI_DSA: + HIP_IFEL(!EVP_PKEY_set1_DSA(sig_key, key), -1, + "Failed to convert DSA to EVP_PKEY\n"); + break; + default: + HIP_OUT_ERR(-1, "Unknown algorithm\n"); + } + + HIP_IFEL(!X509_sign(cert, sig_key, digest), -1, "Failed to sign x509v3 certificate\n"); /** DER */ @@ -943,8 +1010,15 @@ out_err: free(host_id); - X509_REQ_free(req); sk_X509_EXTENSION_pop_free(extlist, X509_EXTENSION_free); + X509_NAME_ENTRY_free(ent); + X509_NAME_free(subj); + X509_NAME_free(issuer); + NCONF_free(conf); + ERR_free_strings(); + EVP_PKEY_free(pkey); + EVP_PKEY_free(sig_key); + free(issuer_hit_n); return err; } @@ -985,7 +1059,7 @@ der_cert = (unsigned char *) &p->der; vessel = p->der; - HIP_IFEL((cert = d2i_X509(NULL, &vessel, verify.der_len)) == NULL, -1, + HIP_IFEL((cert = d2i_X509(NULL, &vessel, ntohl(verify.der_len))) == NULL, -1, "Failed to convert cert from DER to internal format\n"); /* * HIP_IFEL(!X509_print_fp(stdout, cert), -1, === modified file 'hipd/input.c' --- hipd/input.c 2012-03-09 13:38:34 +0000 +++ hipd/input.c 2012-03-09 16:18:06 +0000 @@ -73,7 +73,6 @@ #include "netdev.h" #include "opp_mode.h" #include "output.h" -#include "pisa.h" #include "pkt_handling.h" #include "registration.h" #include "input.h" === modified file 'hipd/output.c' --- hipd/output.c 2012-03-09 13:38:34 +0000 +++ hipd/output.c 2012-03-09 16:18:06 +0000 @@ -65,7 +65,6 @@ #include "hiprelay.h" #include "nat.h" #include "netdev.h" -#include "pisa.h" #include "registration.h" #include "output.h" === renamed file 'hipfw/pisa.c' => 'hipfw/cert.c' --- hipfw/pisa.c 2011-11-25 13:52:20 +0000 +++ hipfw/cert.c 2012-03-09 16:18:06 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Aalto University and RWTH Aachen University. + * Copyright (c) 2010-2011 Aalto University and RWTH Aachen University. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -25,551 +25,190 @@ /** * @file - * This file contains PISA specific functions for the firewall. The basic idea - * is to modify the HIP messages and manage state for allowed connections to - * allow or reject associated ESP traffic. + * certifcate functionality for the firewall * - * @brief PISA functions for the firewall + * @brief certificate functions for the firewall */ #define _BSD_SOURCE +#include <limits.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <time.h> -#include <arpa/inet.h> #include <netinet/in.h> #include <linux/netfilter.h> -#include <openssl/hmac.h> +#include <linux/netfilter_ipv6.h> +#include <openssl/x509.h> -#include "config.h" +#include "lib/core/common.h" +#include "lib/core/ife.h" #include "lib/core/builder.h" -#include "lib/core/certtools.h" -#include "lib/core/crypto.h" +#include "lib/core/cert.h" #include "lib/core/debug.h" -#include "lib/core/ife.h" -#include "lib/core/performance.h" -#include "lib/core/prefix.h" -#include "modules/midauth/hipd/midauth.h" #include "conntrack.h" #include "hipfw_defines.h" -#include "midauth.h" -#include "pisa_cert.h" -#include "pisa.h" - - -#define PISA_RANDOM_LEN 16 -#define PISA_PUZZLE_SEED 0xDEADC0DE -#define PISA_PUZZLE_OPAQUE_LEN (4 + HIP_AH_SHA_LEN) - -/* pisa_check_for_random_update is called at least every PISA_RANDOM_TTL - * seconds. Worst case timer resolution depends on the timeout in the select - * call */ -#define PISA_RANDOM_TTL 2.0 - -static char pisa_random_data[2][PISA_RANDOM_LEN]; -static struct in6_addr community_operator_hit; - -/* @todo make this configurable, issuer HIT */ -#define CO_HIT "2001:001a:b1b0:0aad:0f92:15ca:280c:9430" -#define CO_HIT_FILE HIPL_SYSCONFDIR "/co_hit" - -/** - * Generate a new random number and shift the old one down. - */ -static void pisa_generate_random(void) -{ - void *p0, *p1; - - p0 = &pisa_random_data[0][0]; - p1 = &pisa_random_data[1][0]; - - memcpy(p0, p1, PISA_RANDOM_LEN); - get_random_bytes(p1, PISA_RANDOM_LEN); -} - -/** - * Reads out the HIT of the Community-Operator - * from file CO_HIT_FILE - * @param hit A pointer to the char where the HIT should be stored - * @return 1-> success - * @return 0-> error - */ -static int pisa_read_communit_operator_hit(char *hit) -{ - FILE *f; - char *eofline; - - f = fopen(CO_HIT_FILE, "r"); - - if (f == NULL) { +#include "rule_management.h" +#include "cert.h" + +// runtime configuration +static int use_cert = false; + +static STACK_OF(X509) *root_chain = NULL; + +/** + * Init function for certificate functionality. + * + * Iterate the firewall rules and look for the cert option. If found, + * certificates are used and the root certificate is preloaded to verify + * certificates. If no such rule is found, certificates are deactivated. + * + * @return 0 on success, negative if an error occurred + */ +int cert_init(void) +{ + X509 *cert = NULL; + struct rule *rule = NULL; + struct dlist *list = NULL; + + if (!(list = get_rule_list(NF_IP6_FORWARD))) { + use_cert = false; + HIP_DEBUG("certificates deactivated\n"); return 0; } - if (fgets(hit, INET6_ADDRSTRLEN, f) != NULL) { - eofline = strchr(hit, '\n'); - if (eofline) { - *eofline = '\0'; - } - } else { - HIP_ERROR("Fgets failed"); - } - fclose(f); - - return 1; -} - -void pisa_check_for_random_update(void) -{ - static time_t lastupdate = 0; - time_t now; - - time(&now); - if (difftime(now, lastupdate) > PISA_RANDOM_TTL) { - pisa_generate_random(); - lastupdate = now; - } -} - -/** - * Appends HMAC/SHA1 to a block of data. - * - * @param hit1 first HIT - * @param hit2 second HIT - * @param rnd which random number to use, either 0 or 1 - * @param data pointer to buffer for data and the HMAC - * @param data_len length of data - * @return 0 on success - */ -static int pisa_append_hmac(struct in6_addr *hit1, struct in6_addr *hit2, - int rnd, void *data, int data_len) -{ - uint8_t key[32 + PISA_RANDOM_LEN]; - int err = 0; - unsigned int len = HIP_AH_SHA_LEN; - - /* sanity checks for arguments */ - HIP_IFEL(data == NULL, -1, "No data given.\n"); - HIP_IFEL(hit1 == NULL, -1, "No first HIT given.\n"); - HIP_IFEL(hit2 == NULL, -1, "No second HIT given.\n"); - HIP_IFEL(data_len < 1, -1, "Data has invalid length.\n"); - HIP_IFEL(rnd != 0 && rnd != 1, -1, "Random ID is neither 0 nor 1.\n"); - - /* The key for HMAC/SHA1 consists of: - * 16 bytes HIT1 - * 16 bytes HIT2 - * PISA_RANDOM_LEN bytes pisa_random_data - */ - - ipv6_addr_copy((struct in6_addr *) (key + 0), hit1); - ipv6_addr_copy((struct in6_addr *) (key + 16), hit2); - memcpy(key + 32, &pisa_random_data[rnd][0], PISA_RANDOM_LEN); - - HMAC(EVP_sha1(), key, 32 + PISA_RANDOM_LEN, data, data_len, - (uint8_t *) data + data_len, &len); - -out_err: - return err; -} - -/** - * Insert a PISA puzzle into a packet. - * - * @param ctx context of the packet where the puzzle will be inserted - * @return success (0) or failure - */ -static int pisa_insert_puzzle(struct hip_fw_context *ctx) -{ - uint8_t opaque[PISA_PUZZLE_OPAQUE_LEN]; - - struct hip_common *hip = ctx->transport_hdr.hip; - int seed = PISA_PUZZLE_SEED; - - memcpy(&opaque, &seed, 4); - - /* here we switch order of initiator and receiver to obtain a - * different SHA1 hash */ - pisa_append_hmac(&hip->hitr, &hip->hits, 1, &opaque, 4); - - return midauth_add_challenge_request(ctx, 3, 4, opaque, PISA_PUZZLE_OPAQUE_LEN); -} - -/** - * Check the validity of a PISA challenge_response. - * - * @param ctx context of the packet with the puzzle to check - * @return pointer to the puzzle we accepted or NULL at failure - */ -static struct hip_challenge_response *pisa_check_challenge_response(struct hip_fw_context *ctx) -{ - struct hip_challenge_response *response; - struct hip_common *hip = ctx->transport_hdr.hip; - uint8_t hash[2][PISA_PUZZLE_OPAQUE_LEN]; - int seed = PISA_PUZZLE_SEED; - - memcpy(&hash[0][0], &seed, 4); - memcpy(&hash[1][0], &seed, 4); - - pisa_append_hmac(&hip->hits, &hip->hitr, 0, &hash[0], 4); - pisa_append_hmac(&hip->hits, &hip->hitr, 1, &hash[1], 4); - - response = hip_get_param_readwrite(hip, HIP_PARAM_CHALLENGE_RESPONSE); - - while (response) { - /* loop over all HIP_PARAM_CHALLENGE_RESPONSE */ - if (hip_get_param_type(response) != HIP_PARAM_CHALLENGE_RESPONSE) { - break; - } - if ((!memcmp(response->opaque, &hash[1][0], PISA_PUZZLE_OPAQUE_LEN)) || - (!memcmp(response->opaque, &hash[0][0], PISA_PUZZLE_OPAQUE_LEN))) { - if (!midauth_verify_challenge_response(response, - hip->hits, - hip->hitr)) { - return response; + if (!(root_chain = sk_X509_new_null())) { + HIP_ERROR("Memory allocation failure.\n"); + return -ENOMEM; + } + + /* Search for rules with cert option */ + while (list) { + rule = list->data; + if (rule->cert) { + HIP_DEBUG("allowed cert: %s\n", rule->cert->value); + use_cert = true; + if (!(cert = cert_load_x509_certificate(rule->cert->value, + ENCODING_FORMAT_PEM))) { + HIP_ERROR("Could not load certificate of community operator from file: %s \n", + rule->cert->value); + return -1; } - } - - response = (struct hip_challenge_response *) - hip_get_next_param_readwrite(hip, - (struct hip_tlv_common *) response); - } - - return NULL; -} - -/** - * Check the certificate of the packet. - * - * @param ctx context of the packet with the certificate to check - * @return success (0) or failure - */ -static int pisa_check_certificate(struct hip_fw_context *ctx) -{ - struct hip_common *hip = ctx->transport_hdr.hip; - const struct hip_cert *cert; - struct hip_cert_spki_info ci; - struct pisa_cert pc; - char *buf = NULL; - int err = 0, len; - time_t now = time(NULL); - - cert = hip_get_param(hip, HIP_PARAM_CERT); - HIP_IFEL(cert == NULL, -1, "No certificate found.\n"); - - len = ntohs(cert->length); - buf = calloc(1, len); - memcpy(buf, cert + 1, len); - - HIP_IFEL(hip_cert_spki_char2certinfo(buf, &ci), -1, - "Certificate could not be parsed.\n"); - HIP_IFEL(hip_cert_spki_lib_verify(&ci), -1, - "Certificate could not be verified.\n"); - - pisa_split_cert(ci.cert, &pc); - - /* Three conditions must be fulfilled for a certificate to be valid: - * - * - The current time on the middlebox must be in the before/after - * interval - * - The certificate must be issued by the community operator (i.e. - * the CO HIT must be used by the issuer) - * - The host sending the certificate must be the one mentioned in - * the certificate - */ - HIP_IFEL(now < pc.not_before, -1, - "Certificate is not valid yet.\n"); - HIP_IFEL(now > pc.not_after, -1, - "Certificate has expired.\n"); - - - HIP_IFEL(ipv6_addr_cmp(&pc.hit_issuer, &community_operator_hit) != 0, - -1, "Certificate not issued by the community operator.\n"); - HIP_IFEL(ipv6_addr_cmp(&pc.hit_subject, &hip->hits) != 0, -1, - "Certificate does not belong to subject.\n"); - - HIP_INFO("Certificate successfully verified.\n"); - -out_err: - free(buf); - return err; -} - -/** - * Accept a connection via PISA. Update firewall to allow data packets to - * pass through. - * - * @param ctx context of the packet that belongs to that connection - */ -static void pisa_accept_connection(const struct hip_fw_context *ctx) -{ - struct hip_common *hip = ctx->transport_hdr.hip; - struct tuple *t = get_tuple_by_hits(&hip->hits, &hip->hitr); - - if (t) { - t->connection->pisa_state = PISA_STATE_ALLOW; - HIP_INFO("PISA accepted the connection.\n"); - hip_fw_manage_all_esp_tuples(t, true); - } else { - HIP_ERROR("Connection not found.\n"); - } -} - -/** - * Remove a connection from the list of accepted connections based on the hits - * of a packet. - * - * @param ctx context of the packet that contains HITs of the connection - */ -static void pisa_remove_connection(const struct hip_fw_context *ctx) -{ - struct hip_common *hip = ctx->transport_hdr.hip; - struct tuple *t = get_tuple_by_hits(&hip->hits, &hip->hitr); - - if (t) { - t->connection->pisa_state = PISA_STATE_DISALLOW; - HIP_INFO("PISA removed the connection.\n"); - hip_fw_manage_all_esp_tuples(t, false); - } else { - HIP_ERROR("Connection not found.\n"); - } -} - -/** - * Reject a connection via PISA. Update firewall to allow no data packets - * to pass through. - * - * @param ctx context of the packet that belongs to that connection - */ -static void pisa_reject_connection(const struct hip_fw_context *ctx) -{ - HIP_INFO("PISA rejected the connection.\n"); - pisa_remove_connection(ctx); -} - -/** - * Dummy function, necessary only for performance measurements. - * - * @param ctx context of the packet containing the I1 - * @return NF_ACCEPT verdict - */ -static int pisa_handler_i1(UNUSED struct hip_fw_context *ctx) -{ -#ifdef CONFIG_HIP_PERFORMANCE - HIP_DEBUG("Start PERF_BASE, PERF_I1\n"); - hip_perf_start_benchmark(perf_set, PERF_BASE); - hip_perf_start_benchmark(perf_set, PERF_I1); -#endif - -#ifdef CONFIG_HIP_PERFORMANCE - HIP_DEBUG("Stop and write PERF_I1\n"); - hip_perf_stop_benchmark(perf_set, PERF_I1); - hip_perf_write_benchmark(perf_set, PERF_I1); -#endif - - return NF_ACCEPT; -} - -/** - * Dummy function, necessary only for performance measurements. - * - * @param ctx context of the packet containing the R1 - * @return NF_ACCEPT verdict - */ -static int pisa_handler_r1(UNUSED struct hip_fw_context *ctx) -{ -#ifdef CONFIG_HIP_PERFORMANCE - HIP_DEBUG("Start PERF_R1\n"); - hip_perf_start_benchmark(perf_set, PERF_R1); -#endif - -#ifdef CONFIG_HIP_PERFORMANCE - HIP_DEBUG("Stop and write PERF_R1\n"); - hip_perf_stop_benchmark(perf_set, PERF_R1); - hip_perf_write_benchmark(perf_set, PERF_R1); -#endif - - return NF_ACCEPT; -} - -/** - * Insert a PISA puzzle into the I2 packet. - * - * @param ctx context of the packet to modify - * @return NF_ACCEPT verdict - */ -static int pisa_handler_i2(struct hip_fw_context *ctx) -{ -#ifdef CONFIG_HIP_PERFORMANCE - HIP_DEBUG("Start PERF_I2\n"); - hip_perf_start_benchmark(perf_set, PERF_I2); -#endif - pisa_insert_puzzle(ctx); - -#ifdef CONFIG_HIP_PERFORMANCE - HIP_DEBUG("Stop and write PERF_I2\n"); - hip_perf_stop_benchmark(perf_set, PERF_I2); - hip_perf_write_benchmark(perf_set, PERF_I2); -#endif - - return NF_ACCEPT; -} - -/** - * Check for a PISA puzzle, a valid signature and a valid - * certificate in the R2 packet. - * - * @param ctx context of the packet to check - * @return verdict, either NF_ACCEPT or NF_DROP - */ -static int pisa_handler_r2(struct hip_fw_context *ctx) -{ - int verdict = NF_DROP, sig = 0, cert = 0; - struct hip_challenge_response *solution = NULL; - -#ifdef CONFIG_HIP_PERFORMANCE - HIP_DEBUG("Start PERF_R2\n"); - hip_perf_start_benchmark(perf_set, PERF_R2); -#endif - - solution = pisa_check_challenge_response(ctx); - cert = pisa_check_certificate(ctx); - - if (solution == NULL || sig != 0 || cert != 0) { - /* disallow further communication if either nonce, solution, - * signature or certificate were not correct */ - pisa_reject_connection(ctx); - verdict = NF_DROP; - } else { - /* allow futher communication otherwise */ - pisa_accept_connection(ctx); - verdict = NF_ACCEPT; - } - -#ifdef CONFIG_HIP_PERFORMANCE - HIP_DEBUG("Stop and write PERF_R2, PERF_BASE\n"); - hip_perf_stop_benchmark(perf_set, PERF_R2); - hip_perf_stop_benchmark(perf_set, PERF_BASE); - hip_perf_write_benchmark(perf_set, PERF_R2); - hip_perf_write_benchmark(perf_set, PERF_BASE); -#endif - - return verdict; -} - -/** - * Insert a PISA nonce and a PISA puzzle into the U1 packet. - * - * @param ctx context of the packet to modify - * @return NF_ACCEPT verdict - */ -static int pisa_handler_u1(struct hip_fw_context *ctx) -{ - pisa_insert_puzzle(ctx); - - return NF_ACCEPT; -} - -/** - * Check for a PISA nonce, a PISA puzzle, a valid signature and a valid - * certificate in the U2 packet. - * - * @param ctx context of the packet to check - * @return verdict, either NF_ACCEPT or NF_DROP - */ -static int pisa_handler_u2(struct hip_fw_context *ctx) -{ - int verdict = NF_DROP; - int sig = 0; - int cert = 0; - struct hip_challenge_response *solution = NULL; - - solution = pisa_check_challenge_response(ctx); - cert = pisa_check_certificate(ctx); - - if (solution == NULL || sig != 0 || cert != 0) { - HIP_DEBUG("U2 packet did not match criteria: " - "solution %p, signature %i, cert %i\n", - solution, sig, cert); - verdict = NF_DROP; - } else { - /* packet was ok, insert another puzzle */ - pisa_insert_puzzle(ctx); - verdict = NF_ACCEPT; - } - - return verdict; -} - -/** - * Check for a PISA nonce and a valid signature in the U3 packet. - * - * @param ctx context of the packet to check - * @return verdict, either NF_ACCEPT or NF_DROP - */ -static int pisa_handler_u3(struct hip_fw_context *ctx) -{ - int verdict = NF_DROP; - int sig = 0; - struct hip_challenge_response *solution = NULL; - - solution = pisa_check_challenge_response(ctx); - - if (solution == NULL || sig != 0) { - HIP_DEBUG("U2 packet did not match criteria: " - "solution %p\n", - solution); - pisa_reject_connection(ctx); - verdict = NF_DROP; - } else { - pisa_accept_connection(ctx); - verdict = NF_ACCEPT; - } - return verdict; -} - -/** - * Handle CLOSE_ACK packet. Remove the connection from the list of accepted - * connections - * - * @param ctx context of the packet - * @return verdict, either NF_ACCEPT or NF_DROP - */ -static int pisa_handler_close_ack(struct hip_fw_context *ctx) -{ - pisa_remove_connection(ctx); - return NF_ACCEPT; -} - -/** - * Initialize basic PISA functionality - * - * @param h function pointers for packet handlers - */ -void pisa_init(struct midauth_handlers *h) -{ - char hit[INET6_ADDRSTRLEN]; - h->i1 = pisa_handler_i1; - h->r1 = pisa_handler_r1; - h->i2 = pisa_handler_i2; - h->r2 = pisa_handler_r2; - h->u1 = pisa_handler_u1; - h->u2 = pisa_handler_u2; - h->u3 = pisa_handler_u3; - h->close = midauth_handler_accept; - h->close_ack = pisa_handler_close_ack; - - pisa_generate_random(); - pisa_generate_random(); - - if (!pisa_read_communit_operator_hit(hit)) { - hit[0] = '\0'; - HIP_ERROR("Could not load Communit-Operator HIT from file %s\n", - CO_HIT_FILE); - } - - if (inet_pton(AF_INET6, hit, &community_operator_hit) <= 0) { - HIP_ERROR("Coult not parse Community-Operator HIT\n"); - } + sk_X509_push(root_chain, cert); + } + list = list->next; + } + + if (use_cert) { + HIP_DEBUG("certificates activated\n"); + } + + return 0; +} + +/** + * Uninit function for certificate functionality. + * + * @return 0 + */ +int cert_uninit(void) +{ + sk_X509_free(root_chain); + return 0; +} + +/** + * Helper function that converts the special RSA and DSA key structures + * to the generic EVP_PKEY structure. + * + * @param key the RSA or DSA key structure + * @param algo either HIP_HI_RSA or HIP_HI_DSA + * + * @return the EVP_PKEY structure that wraps the original key, + * or NULL on error + */ +static EVP_PKEY *any_key_to_evp_key(void *key, int algo) +{ + int err = 0; + EVP_PKEY *ret = NULL; + + if (!(ret = EVP_PKEY_new())) { + HIP_ERROR("Could not init EVP_PKEY wrapper\n"); + return NULL; + } + + switch (algo) { + case HIP_HI_RSA: + err = EVP_PKEY_assign_RSA(ret, key); + break; + case HIP_HI_DSA: + err = EVP_PKEY_assign_DSA(ret, key); + break; + default: + HIP_DEBUG("Unknown algorithm \n"); + } + if (err == 0) { + HIP_ERROR("Could not assign key to EVP_PKEY.\n"); + EVP_PKEY_free(ret); + return NULL; + } + + return ret; +} + +/** + * Extract the certificate from the R2 packet and match the contained public + * key against the HI provided in the R1 and try to build and verify + * a certificate chain. + * + * For an update exchange, a certificate must be contained either in the U2 + * (if the exchange was started by the Initiator) or in the U3 + * (if the exchange was started by the Responder). If a certificate cannot be + * found in these situations an error is returned. + * + * @param common the R2 or U2 packet + * @param tuple the connection tracking tuple + * @param ctx the firewall context + * @return 0 on success, negative error code otherwise + */ +int cert_handle_certificate(const struct hip_common *const common, + UNUSED struct tuple *const tuple, + UNUSED const struct hip_fw_context *const ctx) +{ + X509 *cert = NULL; + EVP_PKEY *pkey = NULL; + + if (use_cert) { + /* Should there be a certificate? + * Not if this update is not sent by the Responder. */ + if (!(tuple->direction == REPLY_DIR)) { + return 0; + } + + /* Extract certificate of trust point from the packet. */ + if (!(cert = cert_get_X509_from_msg(common))) { + HIP_DEBUG("Could not find trust-point certificate in R2/U2.\n"); + return -1; + } + + /* Match HI against public key in given certificate. */ + pkey = any_key_to_evp_key(tuple->hip_tuple->data->src_pub_key, + hip_get_host_id_algo(tuple->hip_tuple->data->src_hi)); + if (!cert_match_public_key(cert, pkey)) { + HIP_ERROR("HI does not match public key in given certificate.\n"); + return -1; + } + HIP_DEBUG("HI matches given certificate.\n"); + + /* Check certificate of trust point. */ + if (cert_verify_chain(cert, NULL, root_chain, NULL)) { + HIP_ERROR("Could not verify trust point certificate.\n"); + return -1; + } + + HIP_DEBUG("Verified trust-point certificate.\n"); + } + + return 0; } === renamed file 'hipfw/pisa.h' => 'hipfw/cert.h' --- hipfw/pisa.h 2011-11-25 13:52:20 +0000 +++ hipfw/cert.h 2012-03-09 16:18:06 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Aalto University and RWTH Aachen University. + * Copyright (c) 2010-2011 Aalto University and RWTH Aachen University. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -23,26 +23,16 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef HIPL_HIPFW_PISA_H -#define HIPL_HIPFW_PISA_H - -#include "midauth.h" - -#define PISA_STATE_DISALLOW 0 -#define PISA_STATE_ALLOW 1 - -#ifdef CONFIG_HIP_PISA -/** - * Register PISA handlers with midauth and initialize data structures. - * - * @param h pointer to the handlers - */ -void pisa_init(struct midauth_handlers *h); -#endif - -/** - * Check if a new random number is necessary. - */ -void pisa_check_for_random_update(void); - -#endif /* HIPL_HIPFW_PISA_H */ +#ifndef HIPL_HIPFW_CERT_H +#define HIPL_HIPFW_CERT_H + +#include "hipfw_defines.h" + +int cert_init(void); +int cert_uninit(void); + +int cert_handle_certificate(const struct hip_common *const common, + struct tuple *const tuple, + const struct hip_fw_context *const ctx); + +#endif /* HIPL_HIPFW_CERT_H */ === modified file 'hipfw/conntrack.c' --- hipfw/conntrack.c 2012-03-08 14:33:33 +0000 +++ hipfw/conntrack.c 2012-03-09 16:18:06 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2011 Aalto University and RWTH Aachen University. + * Copyright (c) 2010-2012 Aalto University and RWTH Aachen University. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -62,8 +62,9 @@ #include "lib/core/state.h" #include "lib/core/gpl/pk.h" #include "modules/update/hipd/update.h" +#include "config.h" +#include "cert.h" #include "common_types.h" -#include "config.h" #include "conntrack.h" #include "dlist.h" #include "hslist.h" @@ -72,7 +73,7 @@ #include "hipfw.h" #include "helpers.h" #include "hslist.h" -#include "pisa.h" +#include "midauth.h" #include "reinject.h" @@ -1296,6 +1297,11 @@ return 0; } + if (cert_handle_certificate(common, tuple, ctx)) { + HIP_ERROR("certificate error on R2\n"); + return 0; + } + /* check if the R2 contains ESP protection anchor and store state */ if (esp_prot_conntrack_R2_anchor(common, tuple)) { HIP_ERROR("failed to track esp protection extension state\n"); @@ -1330,7 +1336,7 @@ uint8_t locator_type; ptr = (const char *) (locator + 1); - while (ptr < ptr + hip_get_param_contents_len(locator)) { + while (ptr < ((const char *) (locator + 1)) + hip_get_param_contents_len(locator)) { locator_type = ((const struct hip_locator_type_0 *) ptr)->header.locator_type; if (locator_type == HIP_LOCATOR_LOCATOR_TYPE_IPV6) { esp_dest_addr = &((const struct hip_locator_type_0 *) ptr)->address; @@ -1368,11 +1374,6 @@ { int err = 1; - if (hip_get_param_total_len(locator) - sizeof(struct hip_locator) < sizeof(struct hip_locator_type_0)) { - HIP_ERROR("no locator param found\n"); - return 0; - } - if (esp_info && locator && seq) { HIP_DEBUG("esp_info, locator and seq\n"); @@ -1386,6 +1387,11 @@ esp_tuple->spi = ntohl(esp_info->new_spi); esp_tuple->spi_update_id = seq->update_id; + if (hip_get_param_total_len(locator) - sizeof(struct hip_locator) < sizeof(struct hip_locator_type_0)) { + HIP_ERROR("no locator param found\n"); + return 0; + } + HIP_IFEL(!update_esp_dest_addr(locator, seq, esp_tuple), 0, "error updateing ESP destination addresses\n"); } else if (esp_info && seq) { @@ -1597,6 +1603,11 @@ return 0; } + if (cert_handle_certificate(common, tuple, ctx)) { + HIP_ERROR("certificate error on U2\n"); + return 0; + } + if (handle_second_update(tuple, ctx, esp_info, seq)) { HIP_ERROR("unable to process second UPDATE message\n"); return 0; === modified file 'hipfw/hipfw.c' --- hipfw/hipfw.c 2012-03-08 12:28:00 +0000 +++ hipfw/hipfw.c 2012-03-09 16:18:06 +0000 @@ -78,6 +78,7 @@ #include "hipd/hipd.h" #include "config.h" #include "cache.h" +#include "cert.h" #include "common_types.h" #include "conntrack.h" #include "esp_prot_api.h" @@ -87,7 +88,6 @@ #include "helpers.h" #include "lsi.h" #include "midauth.h" -#include "pisa.h" #include "port_bindings.h" #include "reinject.h" #include "rewrite.h" @@ -481,6 +481,8 @@ midauth_init(); + HIP_IFEL(cert_init(), -1, "failed to load extension (cert)\n"); + // Initializing local port cache database hip_port_bindings_init(true); /* Initialize raw sockets for packet reinjection */ @@ -628,6 +630,8 @@ fw_uninit_lsi_support(); hip_fw_uninit_conntrack(); + cert_uninit(); + #ifdef CONFIG_HIP_PERFORMANCE /* Deallocate memory of perf_set after finishing all of tests */ hip_perf_destroy(perf_set); @@ -1876,12 +1880,6 @@ continue; } -#ifdef CONFIG_HIP_PISA - if (use_midauth) { - pisa_check_for_random_update(); - } -#endif - if (FD_ISSET(h4->fd, &read_fdset)) { HIP_DEBUG("received IPv4 packet from iptables queue\n"); err = fw_handle_packet(buf, h4, 4, &ctx); === modified file 'hipfw/hipfw.conf' --- hipfw/hipfw.conf 2011-12-15 13:25:00 +0000 +++ hipfw/hipfw.conf 2012-03-09 16:18:06 +0000 @@ -7,5 +7,6 @@ # -i [!] <incoming interface> # -o [!] <outgoing interface> # -state [!] <state> --verify_responder --accept_mobile --decrypt_contents +# -cert <root certificate>" # === modified file 'hipfw/hipfw_defines.h' --- hipfw/hipfw_defines.h 2012-02-17 18:01:18 +0000 +++ hipfw/hipfw_defines.h 2012-03-09 16:18:06 +0000 @@ -158,7 +158,6 @@ /* members needed for ESP protection extension */ int num_esp_prot_tfms; uint8_t esp_prot_tfms[MAX_NUM_TRANSFORMS]; - int pisa_state; }; #endif /* HIPL_HIPFW_FIREWALL_DEFINES_H */ === modified file 'hipfw/main.c' --- hipfw/main.c 2011-11-25 13:52:20 +0000 +++ hipfw/main.c 2012-03-09 16:18:06 +0000 @@ -37,14 +37,15 @@ #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <unistd.h> #include <sys/types.h> #include "lib/core/filemanip.h" #include "lib/core/debug.h" #include "lib/core/util.h" +#include "conntrack.h" #include "hipfw.h" -#include "conntrack.h" #include "midauth.h" === removed file 'hipfw/pisa_cert.c' --- hipfw/pisa_cert.c 2011-08-15 14:11:56 +0000 +++ hipfw/pisa_cert.c 1970-01-01 00:00:00 +0000 @@ -1,205 +0,0 @@ -/* - * Copyright (c) 2010 Aalto University and RWTH Aachen University. - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/** - * @file - * This file deals with the PISA specific handling of SPKI certificates. The - * certificate is parsed and split into small chunks. - * - * @brief PISA handling for SPKI certificates - */ - -#define _BSD_SOURCE - -#include <string.h> -#include <time.h> -#include <arpa/inet.h> -#include <netinet/in.h> -#include <sys/types.h> - -#include "pisa_cert.h" - - -/** - * Extract parts of a SPKI certificate. - * - * @param cert pointer to the certificate text or part of a certificate text - * @param name pointer to the pattern we are looking for - * @param r pointer to a buffer that the search result will be copied to - * @return 0 on success - */ -static char *pisa_cert_get_part(char *cert, const char *name, char *r) -{ - int level = 0, len = 0; - char *p = cert, *start = NULL; - - if (!r) { - return NULL; - } - - if (!cert) { - goto out_err; - } - - if (!name) { - goto out_err; - } - - len = strlen(name); - if (len == 0) { - goto out_err; - } - - while (*p) { - if (*p == '(') { - level++; - if (level == 2 && !strncmp(p + 1, name, len)) { - if (*(p + len + 1) == ' ') { - start = p++; - break; - } - } - } - if (*p == ')') { - level--; - } - if (level == 0) { - break; - } - p++; - } - - if (!start) { - goto out_err; - } - - len = 0; - - while (*p) { - if (*p == '(') { - level++; - } - if (*p == ')') { - level--; - if (level == 1) { - len = p - start + 1; - break; - } - } - if (level == 0) { - break; - } - p++; - } - - strncpy(r, start, len); - r[len] = '\0'; - - return r; - -out_err: - r[0] = '\0'; - return NULL; -} - -/** - * Get the content from a SPKI certificate part. - * - * @param cert pointer to the certificate text or part of a certificate text - * @param name pointer to the pattern we are looking for - * @param r pointer to a buffer that the search result will be copied to - * @return 0 on success - */ -static void pisa_cert_get_content(char *cert, const char *name, char *r) -{ - char *start = cert; - int len = 0; - - if (!r) { - return; - } - - if (!cert || !name || !*name == '(') { - goto out_err; - } - - if (strlen(name) + 3 > strlen(cert)) { - goto out_err; - } - - if (strncmp(name, cert + 1, strlen(name))) { - goto out_err; - } - start = cert + strlen(name) + 2; - - if (*start == '\0') { - goto out_err; - } - - len = strlen(start) - 1; - if (*(start + len) != ')') { - goto out_err; - } - strncpy(r, start, len); - -out_err: - r[len] = '\0'; -} - -/** - * Splits a certificate into semantic chunks and converts these to - * sensible binary representations. - * - * @param cert the original certificate - * @param pc internal representation of the certificate - */ -void pisa_split_cert(char *cert, struct pisa_cert *pc) -{ - struct tm t; - char buffer1[224], buffer2[224]; - struct in6_addr addr; - - pisa_cert_get_part(cert, "not-before", buffer1); - pisa_cert_get_content(buffer1, "not-before", buffer2); - strptime(buffer2, "\"%Y-%m-%d_%H:%M:%S\"", &t); - pc->not_before = mktime(&t); - - pisa_cert_get_part(cert, "not-after", buffer1); - pisa_cert_get_content(buffer1, "not-after", buffer2); - strptime(buffer2, "\"%Y-%m-%d_%H:%M:%S\"", &t); - pc->not_after = mktime(&t); - - pisa_cert_get_part(cert, "issuer", buffer1); - pisa_cert_get_part(buffer1, "hash hit", buffer2); - pisa_cert_get_content(buffer2, "hash hit", buffer1); - inet_pton(AF_INET6, buffer1, &addr); - memcpy(&pc->hit_issuer, &addr, sizeof(struct in6_addr)); - - pisa_cert_get_part(cert, "subject", buffer1); - pisa_cert_get_part(buffer1, "hash hit", buffer2); - pisa_cert_get_content(buffer2, "hash hit", buffer1); - inet_pton(AF_INET6, buffer1, &addr); - memcpy(&pc->hit_subject, &addr, sizeof(struct in6_addr)); -} === removed file 'hipfw/pisa_cert.h' --- hipfw/pisa_cert.h 2011-11-25 13:52:20 +0000 +++ hipfw/pisa_cert.h 1970-01-01 00:00:00 +0000 @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2010 Aalto University and RWTH Aachen University. - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef HIPL_HIPFW_PISA_CERT_H -#define HIPL_HIPFW_PISA_CERT_H - -#include <time.h> -#include <netinet/in.h> - -struct pisa_cert { - struct in6_addr hit_issuer; - struct in6_addr hit_subject; - time_t not_before; - time_t not_after; -}; - -/** - * Split the hip_cert_spki_info.cert part into small chunks - * - * @param cert the hip_cert_spki_info.cert part of the certificate - * @param pc datastructure that will contain the chunks - */ -void pisa_split_cert(char *cert, struct pisa_cert *pc); - -#endif /* HIPL_HIPFW_PISA_CERT_H */ === modified file 'hipfw/rule_management.c' --- hipfw/rule_management.c 2011-12-15 13:53:03 +0000 +++ hipfw/rule_management.c 2012-03-09 16:18:06 +0000 @@ -68,6 +68,7 @@ #define IN_IF_STR "-i" #define OUT_IF_STR "-o" #define STATE_STR "-state" +#define CERT_STR "-cert" #define SRC_HI_STR "--hi" #define VERIFY_RESPONDER_STR "--verify_responder" #define ACCEPT_MOBILE_STR "--accept_mobile" @@ -97,6 +98,7 @@ STATE_OPTION, IN_IF_OPTION, OUT_IF_OPTION, + CERT_OPTION, HOOK }; @@ -248,6 +250,7 @@ rule->state = NULL; rule->in_if = NULL; rule->out_if = NULL; + rule->cert = NULL; rule->hook = -1; rule->accept = -1; return rule; @@ -283,6 +286,7 @@ free(rule->state); free_string_option(rule->in_if); free_string_option(rule->out_if); + free_string_option(rule->cert); free(rule); } } @@ -682,6 +686,15 @@ return NULL; } option_found = STATE_OPTION; + } else if (!strcmp(token, CERT_STR)) { + /* option already defined */ + /* rule only applicable to forward hook */ + if (rule->cert != NULL || rule->hook != NF_IP6_FORWARD) { + HIP_DEBUG("error parsing rule: cert option \n"); + free_rule(rule); + return NULL; + } + option_found = CERT_OPTION; } else if (!strcmp(token, VERIFY_RESPONDER_STR)) { /* related state option must be defined */ if (rule->state == NULL) { @@ -794,6 +807,16 @@ return NULL; } option_found = NO_OPTION; + } else if (option_found == CERT_OPTION) { + rule->cert = malloc(sizeof(struct string_option)); + rule->cert->value = strdup(token); + rule->cert->boolean = 0; + if (rule->cert == NULL || rule->cert->value == NULL) { + HIP_DEBUG("error parsing rule: cert value \n"); + free_rule(rule); + return NULL; + } + option_found = NO_OPTION; } else if (option_found == IN_IF_OPTION) { rule->in_if = parse_if(token); if (rule->in_if == NULL) { === modified file 'hipfw/rule_management.h' --- hipfw/rule_management.h 2011-11-25 13:52:20 +0000 +++ hipfw/rule_management.h 2012-03-09 16:18:06 +0000 @@ -76,6 +76,7 @@ struct hip_host_id *src_hi; struct int_option *type; struct state_option *state; + struct string_option *cert; struct string_option *in_if; struct string_option *out_if; unsigned int hook; === modified file 'lib/core/builder.c' --- lib/core/builder.c 2012-03-08 13:39:18 +0000 +++ lib/core/builder.c 2012-03-09 16:18:06 +0000 @@ -1118,6 +1118,10 @@ case HIP_MSG_REINIT_FULLRELAY: return "HIP_MSG_REINIT_FULLRELAY"; case HIP_MSG_FIREWALL_START: return "HIP_MSG_FIREWALL_START"; case HIP_MSG_MANUAL_UPDATE_PACKET: return "HIP_MSG_MANUAL_UPDATE_PACKET"; + case HIP_MSG_CERT_SPKI_SIGN: return "HIP_MSG_CERT_SPKI_SIGN"; + case HIP_MSG_CERT_SPKI_VERIFY: return "HIP_MSG_CERT_SPKI_VERIFY"; + case HIP_MSG_CERT_X509V3_SIGN: return "HIP_MSG_CERT_X509V3_SIGN"; + case HIP_MSG_CERT_X509V3_VERIFY: return "HIP_MSG_CERT_X509V3_VERIFY"; default: return lmod_get_packet_identifier(msg_type); } @@ -1137,6 +1141,8 @@ switch (param_type) { case HIP_PARAM_ACK: return "HIP_PARAM_ACK"; case HIP_PARAM_CERT: return "HIP_PARAM_CERT"; + case HIP_PARAM_CERT_X509_REQ: return "HIP_PARAM_CERT_X509_REQ"; + case HIP_PARAM_CERT_X509_RESP: return "HIP_PARAM_CERT_X509_RESP"; case HIP_PARAM_DH_SHARED_KEY: return "HIP_PARAM_DH_SHARED_KEY"; case HIP_PARAM_DIFFIE_HELLMAN: return "HIP_PARAM_DIFFIE_HELLMAN"; case HIP_PARAM_DSA_SIGN_DATA: return "HIP_PARAM_DSA_SIGN_DATA"; @@ -3265,7 +3271,8 @@ * @return zero on success or negative on failure */ int hip_build_param_cert(struct hip_common *msg, uint8_t group, uint8_t count, - uint8_t id, uint8_t type, void *data, size_t size) + uint8_t id, enum hip_cert_type type, void *data, + size_t size) { struct hip_cert cert; @@ -3352,7 +3359,7 @@ } /** - * Build and append a X509 certiticate request parameter into a HIP control + * Build and append a X509 certificate request parameter into a HIP control * message (on-the-wire) * * @param msg a pointer to the message where the parameter will be @@ -3361,7 +3368,7 @@ * @return zero on success, or negative on failure * @see <a href="http://tools.ietf.org/html/draft-ietf-hip-cert";>draft-ietf-hip-cert</a> */ -int hip_build_param_cert_x509_req(struct hip_common *msg, struct in6_addr *addr) +int hip_build_param_cert_x509_req(struct hip_common *msg, hip_hit_t *addr) { struct hip_cert_x509_req subj; @@ -3399,7 +3406,7 @@ * @return zero on success, or negative on failure * @see <a href="http://tools.ietf.org/html/draft-ietf-hip-cert";>draft-ietf-hip-cert</a> */ -int hip_build_param_cert_x509_resp(struct hip_common *msg, char *der, int len) +int hip_build_param_cert_x509_resp(struct hip_common *msg, char *der, unsigned len) { return build_param_cert_x509(msg, der, len, HIP_PARAM_CERT_X509_RESP); } @@ -3415,7 +3422,7 @@ * @return zero on success, or negative on failure * @see <a href="http://tools.ietf.org/html/draft-ietf-hip-cert";>draft-ietf-hip-cert</a> */ -int hip_build_param_cert_x509_ver(struct hip_common *msg, char *der, int len) +int hip_build_param_cert_x509_ver(struct hip_common *msg, char *der, unsigned len) { return build_param_cert_x509(msg, der, len, HIP_PARAM_CERT_X509_REQ); } === modified file 'lib/core/builder.h' --- lib/core/builder.h 2012-02-23 10:43:16 +0000 +++ lib/core/builder.h 2012-03-09 16:18:06 +0000 @@ -115,13 +115,9 @@ uint32_t, uint16_t, struct hip_crypto_key *); -int hip_build_param_cert(struct hip_common *, - uint8_t, - uint8_t, - uint8_t, - uint8_t, - void *, - size_t); +int hip_build_param_cert(struct hip_common *msg, uint8_t group, uint8_t count, + uint8_t id, enum hip_cert_type type, void *data, + size_t size); int hip_build_param_puzzle(struct hip_common *const msg, const uint8_t val_K, const uint8_t lifetime, @@ -159,8 +155,10 @@ int hip_build_param_cert_spki_info(struct hip_common *msg, struct hip_cert_spki_info *cert_info); int hip_build_param_cert_x509_req(struct hip_common *, struct in6_addr *); -int hip_build_param_cert_x509_resp(struct hip_common *, char *, int); -int hip_build_param_cert_x509_ver(struct hip_common *, char *, int); +int hip_build_param_cert_x509_resp(struct hip_common *msg, char *der, + unsigned len); +int hip_build_param_cert_x509_ver(struct hip_common *msg, char *der, + unsigned len); int hip_build_param_hit_to_ip_set(struct hip_common *, const char *); int hip_build_user_hdr(struct hip_common *, hip_hdr, hip_hdr_err); === added file 'lib/core/cert.c' --- lib/core/cert.c 1970-01-01 00:00:00 +0000 +++ lib/core/cert.c 2012-03-09 16:18:06 +0000 @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2010-2012 Aalto University and RWTH Aachen University. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file + * @brief functionality for handling X509 certificates + */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <openssl/x509.h> +#include <openssl/pem.h> + +#include "builder.h" +#include "debug.h" +#include "ife.h" +#include "common.h" +#include "cert.h" + +/** + * This function encodes the given certificate to its DER encoding for + * transmission over the wire. The encoded certificate is written to @p buf. + * + * @param cert the certificate to encode + * @param buf the output is written here + * + * @return the length of the encoded data + * + * @note: The encoded data is in binary form and may contain embedded zeroes. + * Functions such as strlen() will not return the correct length of the + * encoded structure. Therefore the length value returned by this + * function should always be used. + */ +int cert_X509_to_DER(X509 *const cert, unsigned char **buf) +{ + int len; + + if (!cert) { + HIP_ERROR("Cannot encode NULL-certificate\n"); + return -1; + } + if (!buf) { + HIP_ERROR("Cannot create output buffer at NULL-pointer.\n"); + return -1; + } + + *buf = NULL; + len = i2d_X509(cert, buf); + + if (len < 0) { + HIP_ERROR("Could not DER-encode the given certificate.\n"); + return -1; + } + return len; +} + +/** + * Function to decode a DER-encoded certificate to the internal OpenSSL X509 + * structure. + * + * @param buf the buffer from which the DER-encoded certificate is read + * @param len the length of the DER-encoded certificate + * (NOTE: strlen() and similar functions fail, since DER is + * basically binary data that can contain 0-bytes). + * + * @return the OpenSSL X509 structure corresponding the DER-encoded + * certificate, NULL if errors occured + */ +X509 *cert_DER_to_X509(const unsigned char *buf, const int len) +{ + if (!buf) { + HIP_ERROR("Cannot decode from NULL-buffer\n"); + return NULL; + } + if (len <= 0) { + HIP_ERROR("Cannot decode certificate of length <= 0\n"); + return NULL; + } + return d2i_X509(NULL, &buf, len); +} + +/** + * Load a X509 certificate from @p file. If @p file contains more + * than one certificate, the certificate at the top of the file is + * returned. + * + * @param file the file to load the certficate from + * @param fmt the input format of the certificate + * + * @return a pointer to an X.509 certificate on success, NULL on error + */ +X509 *cert_load_x509_certificate(const char *const file, + enum encoding_format fmt) +{ + FILE *fp = NULL; + X509 *cert = NULL; + + if (!file) { + HIP_ERROR("Cannot read certificate from NULL-filename.\n"); + return NULL; + } + + fp = fopen(file, "rb"); + if (!fp) { + HIP_ERROR("Could not open file for reading: %s\n", file); + return NULL; + } + + if (fmt == ENCODING_FORMAT_PEM) { + cert = PEM_read_X509(fp, NULL, NULL, NULL); + } else if (fmt == ENCODING_FORMAT_DER) { + cert = d2i_X509_fp(fp, NULL); + } else { + HIP_ERROR("Invalid encoding format %i \n", fmt); + return NULL; + } + + if (fclose(fp)) { + HIP_ERROR("Error closing file: %s\n", file); + X509_free(cert); + return NULL; + } else if (!cert) { + HIP_ERROR("Could not decode certificate from file.\n"); + return NULL; + } + + return cert; +} + +/** + * Search for hip_cert parameter in @p msg and try to decode the data in + * the first certificate parameter of a X509 certificate. + * + * @param msg the message to extract the certificate from + * + * @return the first X509 certificate found in the message on success, + * NULL on error and if no certificates were found + */ +X509 *cert_get_X509_from_msg(const struct hip_common *const msg) +{ + const struct hip_cert *param_cert = NULL; + + if (!(param_cert = hip_get_param(msg, HIP_PARAM_CERT))) { + HIP_ERROR("Message contains no certificate.\n"); + return NULL; + } + + /* The contents of the certificate begin after the header of the + * hip_cert parameter. */ + return cert_DER_to_X509((const unsigned char *) (param_cert + 1), + ntohs(param_cert->length) - + sizeof(struct hip_cert) + + sizeof(struct hip_tlv_common)); +} + +/** + * Compare a given public key @p pkey with the public key + * contained in @p cert. + * + * @param cert the X509 certificate + * @param pkey the public key to match + * + * @return 1 if match, 0 otherwise + */ +int cert_match_public_key(X509 *cert, const EVP_PKEY *pkey) +{ + EVP_PKEY *pkey2 = NULL; + + if (!cert || !pkey) { + return 0; + } + pkey2 = X509_get_pubkey(cert); + return EVP_PKEY_cmp(pkey2, pkey); +} + +/** + * Build and verify a certificate chain. + * + * @param leaf_cert the certificate to verify + * @param trusted_lookup_dir certificates in this directory are used as + * root certificates + * @param trusted_chain a certificate stack that can contain additional + * trusted certificates + * @param untrusted_chain a chain of untrusted certificates that can be + * used to build a complete certificate chain + * + * @return 0 if a certificate chain could be built and + * verified, a non-zero error code otherwise + */ +int cert_verify_chain(X509 *leaf_cert, + const char *trusted_lookup_dir, + STACK_OF(X509) *trusted_chain, + STACK_OF(X509) *untrusted_chain) +{ + int err = 0; + X509_LOOKUP *lookup = NULL; + X509_STORE *verify_ctx_store = NULL; + X509_STORE_CTX *verify_ctx = NULL; + + if (!leaf_cert) { + HIP_ERROR("Cannot verify NULL-certificate.\n"); + return -1; + } + + if (!trusted_lookup_dir && !trusted_chain) { + HIP_ERROR("Need trusted dir and trusted chain.\n"); + return -1; + } + + /* Build the verify context */ + if (!(verify_ctx_store = X509_STORE_new())) { + HIP_ERROR("Failed to init certificate store.\n"); + return -1; + } + if (!(lookup = X509_STORE_add_lookup(verify_ctx_store, X509_LOOKUP_hash_dir()))) { + HIP_ERROR("Failed to init lookup directory.\n"); + return -1; + } + if (trusted_lookup_dir) { + if (!X509_LOOKUP_add_dir(lookup, trusted_lookup_dir, + X509_FILETYPE_PEM)) { + HIP_ERROR("Failed to add directory %s to trusted lookup resources.\n", + trusted_lookup_dir); + return -1; + } + } else { + X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT); + } + + if (!(verify_ctx = X509_STORE_CTX_new())) { + HIP_ERROR("Failed to allocate new verify context.\n"); + return -ENOMEM; + } + if (!X509_STORE_CTX_init(verify_ctx, verify_ctx_store, leaf_cert, + untrusted_chain)) { + HIP_ERROR("Failed to setup verify context.\n"); + X509_STORE_CTX_free(verify_ctx); + return -1; + } + if (trusted_chain) { + X509_STORE_CTX_trusted_stack(verify_ctx, trusted_chain); + } + + /* Finally do the verification and output some info on error */ + OpenSSL_add_all_algorithms(); + err = X509_verify_cert(verify_ctx); + + if (err != 1) { + err = X509_STORE_CTX_get_error(verify_ctx); + HIP_DEBUG("X509 verify cert error: %d \n", err); + HIP_DEBUG("at depth: %d \n", X509_STORE_CTX_get_error_depth(verify_ctx)); + HIP_DEBUG("reason: %s\n", X509_verify_cert_error_string(err)); + HIP_DEBUG("certificate:\n"); + X509_print_fp(stderr, X509_STORE_CTX_get_current_cert(verify_ctx)); + } else { + err = 0; + } + + X509_STORE_CTX_free(verify_ctx); + return err; +} === added file 'lib/core/cert.h' --- lib/core/cert.h 1970-01-01 00:00:00 +0000 +++ lib/core/cert.h 2012-03-09 16:18:06 +0000 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2010-2011 Aalto University and RWTH Aachen University. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef HIPL_LIB_CORE_CERT_H +#define HIPL_LIB_CORE_CERT_H + +#include <openssl/x509.h> + +#include "lib/core/protodefs.h" + +enum encoding_format { + ENCODING_FORMAT_PEM, + ENCODING_FORMAT_DER +}; + +int cert_X509_to_DER(X509 *const cert, unsigned char **buf); +X509 *cert_DER_to_X509(const unsigned char *const buf, const int len); + +X509 *cert_load_x509_certificate(const char *const file, + enum encoding_format fmt); +X509 *cert_get_X509_from_msg(const struct hip_common *const msg); + +int cert_match_public_key(X509 *cert, const EVP_PKEY *pkey); +int cert_verify_chain(X509 *leaf_cert, + const char *trusted_lookup_dir, + STACK_OF(X509) *trusted_chain, + STACK_OF(X509) *untrusted_chain); + +#endif /* HIPL_LIB_CORE_CERT_H */ === modified file 'lib/core/certtools.c' --- lib/core/certtools.c 2012-01-14 15:29:47 +0000 +++ lib/core/certtools.c 2012-03-09 16:18:06 +0000 @@ -710,8 +710,8 @@ /* get the struct from the message sent back by the daemon */ HIP_IFEL(!(p = hip_get_param(msg, HIP_PARAM_CERT_X509_RESP)), -1, "No name x509 struct found\n"); - memcpy(certificate, p->der, p->der_len); - err = p->der_len; + memcpy(certificate, p->der, ntohl(p->der_len)); + err = ntohl(p->der_len); out_err: free(msg); @@ -797,18 +797,18 @@ } /** - * Load a configuration file from HIP_CERT_CONF_PATH. + * Load the indicated configuration file. * * @return CONF pointer if OK, NULL on error */ CONF *hip_open_conf(const char *filename) { - long err; CONF *conf; conf = NCONF_new(NCONF_default()); - if (!NCONF_load(conf, filename, &err)) { + if (!NCONF_load(conf, filename, NULL)) { HIP_ERROR("Error opening the configuration file"); + NCONF_free(conf); return NULL; } return conf; === modified file 'lib/core/conf.c' --- lib/core/conf.c 2012-01-13 16:25:15 +0000 +++ lib/core/conf.c 2012-03-09 16:18:06 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2011 Aalto University and RWTH Aachen University. + * Copyright (c) 2010-2012 Aalto University and RWTH Aachen University. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -59,10 +59,12 @@ #include <sys/ioctl.h> #include <sys/socket.h> #include <sys/types.h> +#include <openssl/pem.h> -#include "common.h" #include "config.h" #include "builder.h" +#include "cert.h" +#include "common.h" #include "crypto.h" #include "debug.h" #include "hostid.h" @@ -126,7 +128,8 @@ /* unused, was ACTION_HANDOVER 39 */ #define ACTION_MANUAL_UPDATE 40 #define ACTION_BROADCAST 41 -#define ACTION_MAX 42 /* exclusive */ +#define ACTION_ACQUIRE 42 +#define ACTION_MAX 43 /* exclusive */ /** * TYPE_ constant list, as an index for each action_handler function. @@ -176,7 +179,8 @@ /* unused, was TYPE_HANDOVER 42 */ #define TYPE_MANUAL_UPDATE 43 #define TYPE_BROADCAST 44 -#define TYPE_MAX 45 /* exclusive */ +#define TYPE_CERTIFICATE 45 +#define TYPE_MAX 46 /* exclusive */ /* #define TYPE_RELAY 22 */ @@ -230,6 +234,7 @@ "shotgun on|off\n" "id-to-addr hit|lsi\n" "broadcast on|off\n" + "acquire certificate <hit> [valid-till_timestamp]\n" ; /** @@ -648,6 +653,8 @@ } } else if (!strcmp("broadcast", argv[2])) { ret = ACTION_BROADCAST; + } else if (!strcmp("acquire", argv[2])) { + ret = ACTION_ACQUIRE; } return ret; @@ -698,6 +705,7 @@ case ACTION_TRANSORDER: case ACTION_NAT_LOCAL_PORT: case ACTION_NAT_PEER_PORT: + case ACTION_ACQUIRE: count = 2; break; default: @@ -750,6 +758,8 @@ } else { ret = TYPE_NAT; } + } else if (!strcmp("certificate", text)) { + ret = TYPE_CERTIFICATE; } else if (strcmp("locator", argv[2]) == 0) { ret = TYPE_LOCATOR; } else if (!strcmp("debug", text)) { @@ -819,6 +829,7 @@ case ACTION_HIT_TO_IP: case ACTION_HIT_TO_IP_SET: case ACTION_BROADCAST: + case ACTION_ACQUIRE: type_arg = 3; break; case ACTION_MANUAL_UPDATE: @@ -2424,6 +2435,122 @@ } /** + * Utility function which compactly checks whether a string represents + * a positive natural number, since scanf() is too lenient. + * + * @param string NULL-terminated string to be checked + * + * @return 1 if the only characters in the string are digits, + * optionally with one leading '+' sign; 0 otherwise + */ +static int is_pos_natural_number(const char *string) +{ + if (string) { + size_t len; + + // allow one leading '+' + if (string[0] == '+') { + string++; + } + + if ((len = strlen(string)) > 0) { + for (size_t i = 0; i < len; i++) { + if (string[i] < '0' || string[i] > '9') { + return 0; + } + } + return 1; + } + } + return 0; +} + +static int conf_handle_certificate(struct hip_common *msg, + int action, + const char *opt[], + int optc, + UNUSED int send_only) +{ + hip_hit_t subject_hit; + uint32_t valid_until_h = 0, valid_until_n = 0; + int scanf_res = 0, err = 0; + X509 *cert = NULL; + const struct hip_cert_x509_resp *p = NULL; + + /* Convert the parameter strings input by the user into the + * appropriate binary formats for further processing. */ + const int pton_res = inet_pton(AF_INET6, opt[0], &subject_hit); + + if (optc == 2 && is_pos_natural_number(opt[1])) { + /* Need to use sscanf() here: strtol() won't do since we want the result + * of the conversion to fit into an uint32_t, whereas strtol() and + * relatives would write to a variable of platform-dependent size. */ + scanf_res = sscanf(opt[1], "%" SCNu32, &valid_until_h); + } + + /* The second parameter after "acquire certificate" is optional, so we + * accept optc == 1 as well as optc == 2 (the latter is implied if + * scanf_res is 1). */ + if (pton_res == 1 && action == ACTION_ACQUIRE && + (optc == 1 || scanf_res == 1)) { + HIP_DEBUG_HIT("Requesting certificate for subject", &subject_hit); + + if (hip_build_user_hdr(msg, HIP_MSG_CERT_X509V3_SIGN, 0)) { + HIP_ERROR("Failed to build user message header.\n"); + return -1; + } + + /* If the user provides a second parameter (following the HIT), it is + * meant to indicate the point in time when validity of the issued + * certificate ends; expressed in "number of seconds since the epoch"; + * we use a uint32_t for this, so we're creating a year-2106-problem + * here (be scared). */ + if (scanf_res == 1) { + valid_until_n = htonl(valid_until_h); + if (hip_build_param_contents(msg, &valid_until_n, + HIP_PARAM_UINT, sizeof(uint32_t))) { + HIP_ERROR("Failed to build validity parameter.\n"); + return -1; + } + } + + if (hip_build_param_cert_x509_req(msg, &subject_hit)) { + HIP_ERROR("Failed to build cert_x509_req parameter.\n"); + return -1; + } + } else { + err = -1; + HIP_ERROR("Invalid arguments.\n"); + } + + /* Send the message to the daemon. The daemon fills the message. */ + if (hip_send_recv_daemon_info(msg, send_only, 0)) { + return -ECOMM; + } + + /* Handle the answer */ + if (!(p = hip_get_param(msg, HIP_PARAM_CERT_X509_RESP))) { + HIP_ERROR("No name x509 struct found\n"); + return -1; + } + const uint32_t der_len_h = ntohl(p->der_len); + if (!(cert = cert_DER_to_X509(p->der, der_len_h))) { + HIP_ERROR("Could not get X509 certificate from DER encoding\n."); + return -1; + } + if (fwrite(p->der, der_len_h, 1, stdout) != 1) { + HIP_ERROR("Couldn't output certificate.\n"); + return -1; + } + + /* Clear the msg (and in particular its message type field) + * to prevent hip_do_hipconf() from sending it again. */ + hip_msg_init(msg); + + return err; +} + +/** * Function pointer array containing pointers to handler functions. * Add a handler function for your new action in the action_handler[] array. * If you added a handler function here, do not forget to define that function @@ -2492,6 +2619,7 @@ NULL, /* 42: unused, was TYPE_HANDOVER */ conf_handle_manual_update, /* 43: TYPE_MANUAL_UPDATE */ conf_handle_broadcast, /* 44: TYPE_BROADCAST */ + conf_handle_certificate, /* 45: TYPE_CERTIFICATE */ NULL /* TYPE_MAX, the end. */ }; === modified file 'lib/core/protodefs.h' --- lib/core/protodefs.h 2012-03-08 18:50:52 +0000 +++ lib/core/protodefs.h 2012-03-09 16:18:06 +0000 @@ -375,6 +375,20 @@ #define HIP_VER_RES 0x01 /* Version 1, reserved 0 */ #define HIP_USER_VER_RES 0xF0 /* Internal messages */ + +/* HIP certificate types + * (according to RFC 6253) */ +enum hip_cert_type { + HIP_CERT_X509V3, + HIP_CERT_SPKI, + HIP_CERT_X509V3_HASH_URL, + HIP_CERT_SPKI_HASH_URL, + HIP_CERT_X509V3_LDAP_URL, + HIP_CERT_SPKI_LDAP_URL, + HIP_CERT_X509V3_DN, + HIP_CERT_SPKI_DN, +}; + /** * @defgroup hip_ha_controls HIP host association controls * === added directory 'modules/cert' === added directory 'modules/cert/hipd' === renamed file 'hipd/pisa.c' => 'modules/cert/hipd/cert.c' --- hipd/pisa.c 2011-10-25 21:14:16 +0000 +++ modules/cert/hipd/cert.c 2012-03-09 16:18:06 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Aalto University and RWTH Aachen University. + * Copyright (c) 2010-2012 Aalto University and RWTH Aachen University. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -25,59 +25,179 @@ /** * @file - * This file contains functions that are specific to PISA. They deal with the - * certificate loading. - * - * @brief Functions for certificate loading + * @brief functions to add certificates to R2 and U2 messages */ +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include "config.h" +#include "hipd/hipd.h" +#include "hipd/pkt_handling.h" +#include "lib/core/builder.h" +#include "lib/core/cert.h" #include "lib/core/debug.h" -#include "hipd.h" -#include "pisa.h" - -#define CERT_MAX_SIZE 1024 - -static char *midauth_cert = NULL; - -/** - * Load a certificate from the file HIPL_SYSCONFDIR/cert and store it in memory - * - * @return 0 on success - */ -static int pisa_load_certificate(void) -{ - FILE *f = NULL; - - free(midauth_cert); - midauth_cert = calloc(1, CERT_MAX_SIZE); - - if (!(f = fopen(HIPL_SYSCONFDIR "/cert", "r"))) { - HIP_ERROR("Could not open certificate file.\n"); - return -1; - } - - if (fread(midauth_cert, CERT_MAX_SIZE - 1, 1, f) == 0) { - perror("fread returned 0"); - } - fclose(f); - return 0; -} - -/** - * Load a certificate from disk and return a pointer to the global - * variable containing it. - * - * @see pisa_load_certificate* - * - * @return pointer to midauth_cert - */ -char *hip_pisa_get_certificate(void) -{ - pisa_load_certificate(); - return midauth_cert; +#include "lib/core/ife.h" +#include "lib/core/protodefs.h" +#include "modules/midauth/hipd/midauth.h" +#include "modules/update/hipd/update.h" +#include "cert.h" + +static X509 *host_cert = NULL; + +/** + * Handler that adds the certificate to an R2 message. + * + * @param msg the message where to add the certificate + * @param certificate the certificate to add to the message + * + * @return 0 on success, negative on error + */ +static int hip_add_certificate(struct hip_common *msg, X509 *certificate) +{ + unsigned char *buf; + int len = 0; + + /* Sanity checks */ + if (!msg) { + HIP_ERROR("Message is NULL\n"); + return -1; + } + if (!certificate) { + HIP_ERROR("Certificate is NULL\n"); + return -1; + } + + /* Encode the certificate to DER and build the certificate parameter. */ + if ((len = cert_X509_to_DER(certificate, &buf)) < 0) { + HIP_ERROR("Encoding error\n"); + return -1; + } + if (hip_build_param_cert(msg, 0, 1, 1, HIP_CERT_X509V3, buf, len)) { + HIP_ERROR("Building of certificate parameter failed.\n"); + return -1; + } + + return 0; +} + +/** + * Handler that adds the certificate to an R2 message. + * + * @param packet_type unused + * @param ha_state unused + * @param ctx the packet context + * + * @return 0 on success, negative on failure + * + * @note: The certificate is regarded non-critical thus the function does + * not fail even if no certificate is available. + */ +static int hip_add_certificate_r2(UNUSED const uint8_t packet_type, + UNUSED const uint32_t ha_state, + struct hip_packet_context *ctx) +{ + if (hip_add_certificate(ctx->output_msg, host_cert)) { + HIP_DEBUG("Sending R2 without certificate.\n"); + } + return 0; +} + +/** + * Handler that adds the certificate to the second or third update message. + * A certificate should only be included if the previous + * update packet contained a middlebox challenge. + * + * @param packet_type unused + * @param ha_state unused + * @param ctx the packet context + * + * @return 0 on success, negative on failure + * + * @note: The certificate is regarded non-critical thus the function does + * not fail even if no certificate is available. + */ +static int hip_add_certificate_update(UNUSED const uint8_t packet_type, + UNUSED const uint32_t ha_state, + struct hip_packet_context *ctx) +{ + if (!host_cert) { + HIP_DEBUG("No certificate available.\n"); + return 0; + } + + /* Include a certificate in the U2 or U3, if available. */ + if (hip_classify_update_type(ctx->input_msg) == FIRST_UPDATE_PACKET || + hip_classify_update_type(ctx->input_msg) == SECOND_UPDATE_PACKET) { + if (hip_get_param_contents(ctx->input_msg, HIP_PARAM_CHALLENGE_REQUEST)) { + if (hip_add_certificate(ctx->output_msg, host_cert)) { + HIP_ERROR("Failed to add certificate to update message.\n"); + return -1; + } + } else { + HIP_DEBUG("No middlebox found in previous update, omitting certificate.\n"); + } + } + + return 0; +} + +/** + * Initialize certificate functionality in the hipd. + * Registers handlers to add the trust point certificate in R2 and U2 messages. + * + * @return 0 on success, negative on error + */ +int hip_cert_init(void) +{ + if (hip_register_handle_function(HIP_I2, HIP_STATE_NONE, + &hip_add_certificate_r2, 40500)) { + HIP_ERROR("Error on registering certificate handle function.\n"); + return -1; + } + if (hip_register_handle_function(HIP_I2, HIP_STATE_UNASSOCIATED, + &hip_add_certificate_r2, 40500)) { + HIP_ERROR("Error on registering certificate handle function.\n"); + return -1; + } + if (hip_register_handle_function(HIP_I2, HIP_STATE_I1_SENT, + &hip_add_certificate_r2, 40500)) { + HIP_ERROR("Error on registering certificate handle function.\n"); + return -1; + } + if (hip_register_handle_function(HIP_I2, HIP_STATE_I2_SENT, + &hip_add_certificate_r2, 40500)) { + HIP_ERROR("Error on registering certificate handle function.\n"); + return -1; + } + if (hip_register_handle_function(HIP_I2, HIP_STATE_R2_SENT, + &hip_add_certificate_r2, 40500)) { + HIP_ERROR("Error on registering certificate handle function.\n"); + return -1; + } + if (hip_register_handle_function(HIP_I2, HIP_STATE_ESTABLISHED, + &hip_add_certificate_r2, 40500)) { + HIP_ERROR("Error on registering certificate handle function.\n"); + return -1; + } + if (hip_register_handle_function(HIP_UPDATE, HIP_STATE_ESTABLISHED, + &hip_add_certificate_update, 20752)) { + HIP_ERROR("Error on registering certificate handle function.\n"); + return -1; + } + if (hip_register_handle_function(HIP_UPDATE, HIP_STATE_R2_SENT, + &hip_add_certificate_update, 20752)) { + HIP_ERROR("Error on registering certificate handle function.\n"); + return -1; + } + + if (!(host_cert = cert_load_x509_certificate(HIPL_SYSCONFDIR "/host-cert.der", + ENCODING_FORMAT_DER))) { + HIP_DEBUG("Could not load certificate.\n"); + } + + HIP_DEBUG("certificates initialized\n"); + + return 0; } === renamed file 'hipd/pisa.h' => 'modules/cert/hipd/cert.h' --- hipd/pisa.h 2011-11-25 17:56:24 +0000 +++ modules/cert/hipd/cert.h 2012-03-09 16:18:06 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Aalto University and RWTH Aachen University. + * Copyright (c) 2010-2011 Aalto University and RWTH Aachen University. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -23,22 +23,9 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -/** - * @file - * This file contains function declarations specific to PISA. They deal with the - * certificate loading. - * - * @brief Functions declarations for certificate loading - */ - -#ifndef HIPL_HIPD_PISA_H -#define HIPL_HIPD_PISA_H - -/** - * Get the certificate text that will be appended to R2 and U2 packets - * - * @return pointer to the certificate text - */ -char *hip_pisa_get_certificate(void); - -#endif /* HIPL_HIPD_PISA_H */ +#ifndef HIPL_MODULES_CERT_HIPD_CERT_H +#define HIPL_MODULES_CERT_HIPD_CERT_H + +int hip_cert_init(void); + +#endif /* HIPL_MODULES_CERT_HIPD_CERT_H */ === added file 'modules/cert/module_info.xml' --- modules/cert/module_info.xml 1970-01-01 00:00:00 +0000 +++ modules/cert/module_info.xml 2012-03-09 16:18:06 +0000 @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- Mandatory: name, version --> +<module + name="cert" + version="0.0.1" + description="certificate exchange for the HIP daemon." + developer="" + bugaddress="hipl-users@xxxxxxxxxxxxx" + webpage="http://infrahip.hiit.fi/";> + + <requires> + <module name="update" minversion="0.0.1" /> + <module name="midauth" minversion="0.0.1" /> + </requires> + + <!-- Mandatory: name, header_file, init_function --> + <application + name="hipd" + header_file="modules/cert/hipd/cert.h" + init_function="hip_cert_init" /> +</module> + === modified file 'modules/update/hipd/update.c' (properties changed: -x to +x) === modified file 'test/check_lib_core.c' --- test/check_lib_core.c 2012-03-01 14:06:24 +0000 +++ test/check_lib_core.c 2012-03-09 16:18:06 +0000 @@ -37,6 +37,7 @@ { int number_failed; SRunner *sr = srunner_create(lib_core_hit()); + srunner_add_suite(sr, lib_core_cert()); srunner_add_suite(sr, lib_core_hostid()); srunner_add_suite(sr, lib_core_solve()); srunner_add_suite(sr, lib_core_straddr()); === added file 'test/lib/core/cert.c' --- test/lib/core/cert.c 1970-01-01 00:00:00 +0000 +++ test/lib/core/cert.c 2012-03-09 16:18:06 +0000 @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2011 Aalto University and RWTH Aachen University. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <check.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include "lib/core/cert.h" +#include "lib/core/crypto.h" +#include "lib/core/ife.h" +#include "lib/core/protodefs.h" +#include "config.h" +#include "test_suites.h" + +#define TEST_CERT HIPL_SOURCEDIR "/test/lib/core/test_cert.pem" +#define TEST_KEY HIPL_SOURCEDIR "/test/lib/core/test_key.pem" + +START_TEST(test_cert_load_x509_certificate) +{ + X509 *cert = NULL; + + HIP_DEBUG("Test loading of X509 certificates.\n"); + + fail_unless((cert = cert_load_x509_certificate(TEST_CERT, + ENCODING_FORMAT_PEM)) != NULL, + NULL); + fail_unless((cert = cert_load_x509_certificate("non_existing_cert.pem", + ENCODING_FORMAT_PEM)) == NULL, + NULL); + fail_unless((cert = cert_load_x509_certificate(NULL, + ENCODING_FORMAT_PEM)) == NULL, + NULL); + + X509_free(cert); + HIP_DEBUG("Successfully passed tests for loading X509 certificates.\n"); +} +END_TEST + +START_TEST(test_cert_DER_encoding) +{ + int len = 0; + X509 *cert = NULL; + X509 *cert_decoded = NULL; + unsigned char *buf = NULL; + + HIP_DEBUG("Test DER en/decoding of X509 certificates.\n"); + + fail_unless((cert = cert_load_x509_certificate(TEST_CERT, + ENCODING_FORMAT_PEM)) != NULL, + NULL); + fail_unless((len = cert_X509_to_DER(cert, &buf)) > 0, NULL); + fail_unless((cert_decoded = cert_DER_to_X509(buf, len)) != NULL, NULL); + fail_unless(X509_cmp(cert, cert_decoded) == 0, NULL); + + fail_unless((len = cert_X509_to_DER(NULL, &buf)) < 0, NULL); + fail_unless((len = cert_X509_to_DER(cert, NULL)) < 0, NULL); + fail_unless((len = cert_X509_to_DER(NULL, NULL)) < 0, NULL); + + fail_unless((cert_decoded = cert_DER_to_X509(NULL, len)) == NULL, NULL); + fail_unless((cert_decoded = cert_DER_to_X509(buf, len - 1)) == NULL, NULL); + fail_unless((cert_decoded = cert_DER_to_X509(buf, len + 1)) == NULL, NULL); + fail_unless((cert_decoded = cert_DER_to_X509(buf, 0)) == NULL, NULL); + + X509_free(cert); + X509_free(cert_decoded); + + HIP_DEBUG("Successfully passed tests for DER en/decoding of X509 certificates.\n"); +} + +END_TEST + +START_TEST(test_cert_match_public_key) +{ + int err = 0; + RSA *rsa = NULL; + X509 *cert = NULL; + EVP_PKEY *pkey = NULL; + + HIP_DEBUG("Test matching of public keys.\n"); + + fail_unless((err = load_rsa_private_key(TEST_KEY, &rsa)) == 0, NULL); + fail_unless((cert = cert_load_x509_certificate(TEST_CERT, + ENCODING_FORMAT_PEM)) != NULL, + NULL); + pkey = EVP_PKEY_new(); + EVP_PKEY_assign_RSA(pkey, rsa); + fail_unless((err = cert_match_public_key(cert, pkey)) == 1, NULL); + + fail_unless((err = cert_match_public_key(NULL, pkey)) == 0, NULL); + fail_unless((err = cert_match_public_key(cert, NULL)) == 0, NULL); + fail_unless((err = cert_match_public_key(NULL, NULL)) == 0, NULL); + + EVP_PKEY_free(pkey); + X509_free(cert); + + HIP_DEBUG("Successfully passed test for matching of public keys.\n"); +} + +END_TEST + +START_TEST(test_cert_verify_chain) +{ + int err = 0; + X509 *cert = NULL; + STACK_OF(X509) *chain = NULL; + + HIP_DEBUG("Test verification of certificate chains.\n"); + + fail_unless((cert = cert_load_x509_certificate(TEST_CERT, + ENCODING_FORMAT_PEM)) != NULL, + NULL); + fail_unless((chain = sk_X509_new_null()) != NULL, NULL); + sk_X509_push(chain, cert); + fail_unless((err = cert_verify_chain(cert, NULL, chain, NULL)) == 0, NULL); + + fail_unless((err = cert_verify_chain(NULL, NULL, chain, NULL)) != 0, NULL); + fail_unless((err = cert_verify_chain(cert, NULL, NULL, NULL)) != 0, NULL); + + HIP_DEBUG("Successfully passed test for verification of certificate chains.\n"); +} + +END_TEST + +START_TEST(test_cert_get_X509_from_msg) +{ + int len = 0; + X509 *cert = NULL, *cert2 = NULL; + struct hip_common *msg = NULL; + unsigned char *buf = NULL; + + HIP_DEBUG("Test certificate extraction functionality.\n"); + + fail_unless((cert = cert_load_x509_certificate(TEST_CERT, + ENCODING_FORMAT_PEM)) != NULL, + NULL); + msg = hip_msg_alloc(); + hip_build_network_hdr(msg, HIP_UPDATE, 0, &in6addr_any, &in6addr_any); + fail_unless((len = cert_X509_to_DER(cert, &buf)) > 0, NULL); + fail_unless(hip_build_param_cert(msg, 0, 1, 1, HIP_CERT_X509V3, buf, len) == 0, NULL); + fail_unless((cert2 = cert_get_X509_from_msg(msg)) != NULL, NULL); + fail_unless(X509_cmp(cert, cert2) == 0, NULL); + + X509_free(cert); + X509_free(cert2); + + HIP_DEBUG("Successfully passed test for certificate extraction functionality.\n"); +} + +END_TEST + + +Suite *lib_core_cert(void) +{ + Suite *s = suite_create("lib/core/cert"); + + TCase *tc_core = tcase_create("Core"); + tcase_add_test(tc_core, test_cert_load_x509_certificate); + tcase_add_test(tc_core, test_cert_DER_encoding); + tcase_add_test(tc_core, test_cert_match_public_key); + tcase_add_test(tc_core, test_cert_verify_chain); + tcase_add_test(tc_core, test_cert_get_X509_from_msg); + + suite_add_tcase(s, tc_core); + + return s; +} === added file 'test/lib/core/test_cert.pem' --- test/lib/core/test_cert.pem 1970-01-01 00:00:00 +0000 +++ test/lib/core/test_cert.pem 2012-03-09 16:18:06 +0000 @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICWTCCAcKgAwIBAgIJAPRDK58Gc992MA0GCSqGSIb3DQEBBQUAMCgxCzAJBgNV +BAYTAkRFMRkwFwYDVQQDExBUZXN0IENlcnRpZmljYXRlMB4XDTExMTExNTA4NDYx +MloXDTE0MDgxMTA4NDYxMlowKDELMAkGA1UEBhMCREUxGTAXBgNVBAMTEFRlc3Qg +Q2VydGlmaWNhdGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMn5azBSwPlu +DIzEhd6fGdo1+ze9uS+i+AbifvjHKYaDwDZktANnUmTuLNAXoeVtFi5GH/5aczUx +rFaW+XT33oO1OjotZOHvoQd5cVAvXTtzuJj9PnBtA0lkrQ36f765p65Id0+Xme3o +sXfqLsJy12C23RjisPgAo0hsLUFXt+FZAgMBAAGjgYowgYcwHQYDVR0OBBYEFGrj +JefuwL7xAFj9NZoDVksjz1JBMFgGA1UdIwRRME+AFGrjJefuwL7xAFj9NZoDVksj +z1JBoSykKjAoMQswCQYDVQQGEwJERTEZMBcGA1UEAxMQVGVzdCBDZXJ0aWZpY2F0 +ZYIJAPRDK58Gc992MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAKX5Q +0di8aNk1Js5Sk6QstSWwoVT9DUNZ8wKKRZVHy4kSL7dGMNGRPyNydzJmKeacbZJ0 +wM64rMMC1E8Wnyo+7eS1k9NKgaR5S1K6SVTfgP0HYIeJquD5Im/KW3aMz/q5CWdV +IZtpTC2/1YsK+dVlvvcYRxnSTpIEd/pmu574lis= +-----END CERTIFICATE----- === added file 'test/lib/core/test_key.pem' --- test/lib/core/test_key.pem 1970-01-01 00:00:00 +0000 +++ test/lib/core/test_key.pem 2012-03-09 16:18:06 +0000 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQDJ+WswUsD5bgyMxIXenxnaNfs3vbkvovgG4n74xymGg8A2ZLQD +Z1Jk7izQF6HlbRYuRh/+WnM1MaxWlvl0996DtTo6LWTh76EHeXFQL107c7iY/T5w +bQNJZK0N+n++uaeuSHdPl5nt6LF36i7Cctdgtt0Y4rD4AKNIbC1BV7fhWQIDAQAB +AoGAT1OS8evOtyit7SvSmFlMwhOpk38EmN0dJTcYP4WZnadpevOacCvIhLO3DhP6 +Fi3+JDaOokvMK/xSf7/UQkiIL+dwY5FtWtG6K+T1WI4+DR7ULShr82jh4KemhYv3 +Xz6PvEBIUrf64VfsNsAzoWvbwum/Ev1XzFn5YS4/EW3ApnECQQD2YW6tTjzyq3y7 +sgil9/VtUrXpPi5Ekm8l/bFW2Ijwin03g6J+YvkDNIq5Wwyka7lBOLwpdSpzT2qB +70Ne1nlfAkEA0dwlgou4xOVVmbtTb40FxIFgd2gDQO8bUFEa+TdPWLLxz9yxHQ7D +YROYh5Gdi0u4lz0KNex/6zyaP7E+utbIRwJAexNc2FH2/DpSCuj6jP36qevhV2xq +bHLB9zZtujZc4dwshOjK6VvDjKhYjBNBk3kEh+IxjHwtAoEvcUz2WI/G/QJAcMr5 +/ihKrsj0MSRVu+b36p3+0y68UPIypABzlu77XpkPDsF3ED8XE94MZREGtA+GrwLH +siPivPRdk04YgSNfkQJBAMhHvxL5xsb31bu8h7kxoYHj/XuqJxyToTcqksr7M1RO +z33Bid+znwnB7n8CLFpV3iqF14xbRKiPlc//wVNWtZ8= +-----END RSA PRIVATE KEY----- === modified file 'test/lib/core/test_suites.h' --- test/lib/core/test_suites.h 2012-03-01 14:06:24 +0000 +++ test/lib/core/test_suites.h 2012-03-09 16:18:06 +0000 @@ -28,6 +28,7 @@ #include <check.h> +Suite *lib_core_cert(void); Suite *lib_core_crypto(void); Suite *lib_core_hit(void); Suite *lib_core_hostid(void); === modified file 'tools/hipl_autobuild.sh' --- tools/hipl_autobuild.sh 2012-02-28 16:54:46 +0000 +++ tools/hipl_autobuild.sh 2012-03-09 16:18:06 +0000 @@ -169,7 +169,7 @@ run_program "make $MAKEOPTS check" # minimal configuration -compile --enable-firewall --disable-rvs --disable-profiling --disable-debug --disable-performance --with-nomodules=heartbeat,update,heartbeat_update,midauth +compile --enable-firewall --disable-rvs --disable-profiling --disable-debug --disable-performance --with-nomodules=heartbeat,update,heartbeat_update,midauth,cert # Max compile coverage configuration FEATURES_ALL="--enable-firewall --enable-rvs --enable-profiling --disable-debug --enable-performance" @@ -185,9 +185,6 @@ # FIXME: Disabled until the tree compiles with this optimization level. #compile $FEATURES_ALL CFLAGS="-O3" -# Without modules -compile --with-nomodules=heartbeat,update,heartbeat_update,midauth - # test binary distribution packages # This is run as the last test because it can have sideeffects on the # other standard configurations.