]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[sedhcpv6] add cryptolink validate_certificate hook
authorFrancis Dupont <fdupont@isc.org>
Sat, 6 Jun 2015 00:48:31 +0000 (02:48 +0200)
committerFrancis Dupont <fdupont@isc.org>
Sat, 6 Jun 2015 00:48:31 +0000 (02:48 +0200)
12 files changed:
src/bin/dhcp6/dhcp6_srv.cc
src/bin/perfdhcp/Makefile.am
src/bin/perfdhcp/tests/Makefile.am
src/lib/asiodns/tests/Makefile.am
src/lib/cryptolink/botan_asym.cc
src/lib/cryptolink/openssl_asym.cc
src/lib/cryptolink/tests/Makefile.am
src/lib/cryptolink/tests/asym_unittests.cc
src/lib/cryptolink/tests/run_unittests.cc
src/lib/dhcp/tests/Makefile.am
src/lib/dhcp_ddns/Makefile.am
src/lib/dns/Makefile.am

index c7440c7b7a6f7ba23c34b012997f590d712d7c64..0d1e2dfbca966bba9b575bb83ae16a85c3af71bf 100644 (file)
@@ -2876,268 +2876,275 @@ bool Dhcpv6Srv::validateSeDhcpOptions(const Pkt6Ptr& query, Pkt6Ptr& answer,
     // Get the secure DHCPv6 global configuration state
     ConstCfgSeDhcp6Ptr state =
         CfgMgr::instance().getCurrentCfg()->getCfgSeDhcp6();
-    // If doesn't exit give up
-    if (!state) {
+    // If doesn't exit or check-signatures disabled give up
+    if (!state || !state->getCheckSignatures()) {
         return (true);
     }
 
-    // Is check-signatures enabled
-    if (state->getCheckSignatures()) {
-        // Get the public key option
-        bool has_pubkey = false;
-        if (query->getOption(D6O_PUBLIC_KEY)) {
-            has_pubkey = true;
-            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, SEDHCP6_OPTION_RECEIVED)
-                .arg("public key");
-            if (query->getOptions(D6O_PUBLIC_KEY).size() > 1) {
-                answer->addOption(createStatusCode(STATUS_UnspecFail,
-                            "More than one public key option"));
-                return (false);
-            }
-        }
-        // Get the certificate option
-        bool has_cert = false;
-        if (query->getOption(D6O_CERTIFICATE)) {
-            has_cert = true;
-            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, SEDHCP6_OPTION_RECEIVED)
-                .arg("certificate");
-            if (query->getOptions(D6O_CERTIFICATE).size() > 1) {
-                answer->addOption(createStatusCode(STATUS_UnspecFail,
-                            "More than one certificate option"));
-                return (false);
-            }
-        }
-        // Both must not be presented at the same time
-        if (has_pubkey && has_cert) {
-            answer->addOption(createStatusCode(STATUS_UnspecFail,
-                        "Both public key and certificate options"));
-            return (false);
-        }
-        // Is signature required?
-        bool signopt_required = state->getCheckAuthorizations();
-        string host_credential = "";
-        if (ctx.host_) {
-            host_credential = ctx.host_->getCredential();
-        }
-        if (signopt_required && host_credential.empty()) {
-            answer->addOption(createStatusCode(STATUS_AuthenticationFail,
-                        "No configured credentials"));
-            return (false);
-        }
-        // Get the signature option
-        OptionPtr signopt = query->getOption(D6O_SIGNATURE);
-        if (signopt_required && !signopt) {
+    // Get the public key option
+    bool has_pubkey = false;
+    if (query->getOption(D6O_PUBLIC_KEY)) {
+        has_pubkey = true;
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, SEDHCP6_OPTION_RECEIVED)
+            .arg("public key");
+        if (query->getOptions(D6O_PUBLIC_KEY).size() > 1) {
             answer->addOption(createStatusCode(STATUS_UnspecFail,
-                        "No signature option"));
+                        "More than one public key option"));
             return (false);
         }
-        // Unsecure
-        if (!signopt_required && !signopt) {
-            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, SEDHCP6_UNSECURE);
-            return (true);
-        }
-        // signopt is true
+    }
+    // Get the certificate option
+    bool has_cert = false;
+    if (query->getOption(D6O_CERTIFICATE)) {
+        has_cert = true;
         LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, SEDHCP6_OPTION_RECEIVED)
-            .arg("signature");
-        // Either public key or certificate must available
-        if (!has_pubkey && !has_cert) {
+            .arg("certificate");
+        if (query->getOptions(D6O_CERTIFICATE).size() > 1) {
             answer->addOption(createStatusCode(STATUS_UnspecFail,
-                        "No public key or certificate options"));
+                        "More than one certificate option"));
             return (false);
         }
-        if (query->getOptions(D6O_SIGNATURE).size() > 1) {
+    }
+    // Both must not be presented at the same time
+    if (has_pubkey && has_cert) {
+        answer->addOption(createStatusCode(STATUS_UnspecFail,
+                    "Both public key and certificate options"));
+        return (false);
+    }
+
+    // Is signature required?
+    bool signopt_required = state->getCheckAuthorizations();
+    string host_credential = "";
+    if (ctx.host_) {
+        host_credential = ctx.host_->getCredential();
+    }
+    if (signopt_required && host_credential.empty()) {
+        answer->addOption(createStatusCode(STATUS_AuthenticationFail,
+                    "No configured credentials"));
+        return (false);
+    }
+    // Get the signature option
+    OptionPtr signopt = query->getOption(D6O_SIGNATURE);
+    if (signopt_required && !signopt) {
+        answer->addOption(createStatusCode(STATUS_UnspecFail,
+                    "No signature option"));
+        return (false);
+    }
+    // Unsecure
+    if (!signopt_required && !signopt) {
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, SEDHCP6_UNSECURE);
+        return (true);
+    }
+    // signopt is true
+    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, SEDHCP6_OPTION_RECEIVED)
+        .arg("signature");
+    // Either public key or certificate must available
+    if (!has_pubkey && !has_cert) {
+        answer->addOption(createStatusCode(STATUS_UnspecFail,
+                    "No public key or certificate options"));
+        return (false);
+    }
+    if (query->getOptions(D6O_SIGNATURE).size() > 1) {
+        answer->addOption(createStatusCode(STATUS_UnspecFail,
+                    "More than one signature options"));
+        return (false);
+    }
+    OptionCustomPtr signature =
+        boost::dynamic_pointer_cast<OptionCustom>(signopt);
+    if (!signature) {
+        answer->addOption(createStatusCode(STATUS_UnspecFail,
+                    "Invalid signature option"));
+        return (false);
+    }
+
+    // Check algorithms
+    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, SEDHCP6_INCOMING_TRACE)
+        .arg("checking algorithms");
+    uint8_t ha_id = signature->readInteger<uint8_t>(0);
+    if ((ha_id != SHA_256) && (ha_id != SHA_512)) {
+        answer->addOption(createStatusCode(STATUS_AlgorithmNotSupported,
+                    "Unsupported hash algorithm"));
+        return (false);
+    }
+    HashAlgorithm hash_algo = SHA256;
+    if (ha_id == SHA_512) {
+        hash_algo = SHA512;
+    }
+    uint8_t sa_id = signature->readInteger<uint8_t>(1);
+    if (sa_id != RSASSA_PKCS1v1_5) {
+        answer->addOption(createStatusCode(STATUS_AlgorithmNotSupported,
+                    "Unsupported signature algorithm"));
+        return (false);
+    }
+    AsymAlgorithm sign_algo = RSA_;
+    AsymKeyKind key_kind = PUBLIC;
+    if (has_cert) {
+        key_kind = CERT;
+    }
+
+    // Create the asym crypto key object
+    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, SEDHCP6_INCOMING_TRACE)
+        .arg("creating RSA objects");
+    vector<uint8_t> keybin;
+    if (has_pubkey) {
+        keybin = query->getOption(D6O_PUBLIC_KEY)->getData();
+    } else {
+        keybin = query->getOption(D6O_CERTIFICATE)->getData();
+    }
+    CryptoLink& crypto = CryptoLink::getCryptoLink();
+    CfgSeDhcp6::AsymPtr key(crypto.createAsym(keybin,
+                                              sign_algo,
+                                              hash_algo,
+                                              key_kind,
+                                              ASN1),
+                            deleteAsym);
+    if (!key) {
+        if (has_pubkey) {
             answer->addOption(createStatusCode(STATUS_UnspecFail,
-                        "More than one signature options"));
-            return (false);
-        }
-        OptionCustomPtr signature =
-            boost::dynamic_pointer_cast<OptionCustom>(signopt);
-        if (!signature) {
+                        "Malformed public key option"));
+        } else {
             answer->addOption(createStatusCode(STATUS_UnspecFail,
-                        "Invalid signature option"));
-            return (false);
+                        "Malformed certificate option"));
         }
-        // Check algorithms
-        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, SEDHCP6_INCOMING_TRACE)
-            .arg("checking algorithms");
-        uint8_t ha_id = signature->readInteger<uint8_t>(0);
-        if ((ha_id != SHA_256) && (ha_id != SHA_512)) {
-            answer->addOption(createStatusCode(STATUS_AlgorithmNotSupported,
-                        "Unsupported hash algorithm"));
+        return (false);
+    }
+
+    // Compare with the credential for authorization
+    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, SEDHCP6_INCOMING_TRACE)
+        .arg(signopt_required ?
+             "comparing with config" : "not comparing with config");
+    if (signopt_required) {
+        CfgSeDhcp6::AsymPtr cred(crypto.createAsym(host_credential,
+                                                   "",
+                                                   sign_algo,
+                                                   hash_algo,
+                                                   key_kind,
+                                                   ASN1),
+                                 deleteAsym);
+        if (!cred) {
+            answer->addOption(createStatusCode(STATUS_AuthenticationFail,
+                        "Bad configured credentials"));
             return (false);
         }
-        HashAlgorithm hash_algo = SHA256;
-        if (ha_id == SHA_512) {
-            hash_algo = SHA512;
-        }
-        uint8_t sa_id = signature->readInteger<uint8_t>(1);
-        if (sa_id != RSASSA_PKCS1v1_5) {
-            answer->addOption(createStatusCode(STATUS_AlgorithmNotSupported,
-                        "Unsupported signature algorithm"));
+        if (!cred->compare(key.get(), key_kind)) {
+            answer->addOption(createStatusCode(STATUS_AuthenticationFail,
+                        "Credential mismatch"));
             return (false);
         }
-        AsymAlgorithm sign_algo = RSA_;
-        AsymKeyKind key_kind = PUBLIC;
-        if (has_cert) {
-            key_kind = CERT;
-        }
-        // Create the asym crypto object
-        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, SEDHCP6_INCOMING_TRACE)
-            .arg("creating RSA objects");
-        vector<uint8_t> keybin;
-        if (has_pubkey) {
-            keybin = query->getOption(D6O_PUBLIC_KEY)->getData();
-        } else {
-            keybin = query->getOption(D6O_CERTIFICATE)->getData();
-        }
-        CryptoLink& crypto = CryptoLink::getCryptoLink();
-        CfgSeDhcp6::AsymPtr key(crypto.createAsym(keybin,
-                                                  sign_algo,
-                                                  hash_algo,
-                                                  key_kind,
-                                                  ASN1),
-                                deleteAsym);
-        if (!key && has_pubkey) {
-            answer->addOption(createStatusCode(STATUS_UnspecFail,
-                        "Malformed public key option"));
+        // Add a hook in cryptolink for this!
+        if (state->getOnlineValidation() && !cred->validate()) {
+            answer->addOption(createStatusCode(STATUS_AuthenticationFail,
+                        "Credential does not validate"));
             return (false);
         }
-        if (!key) {
+    }
+
+    // Handle the timestamp option
+    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, SEDHCP6_INCOMING_TRACE)
+        .arg("handling timestamp");
+    OptionPtr tmstmp_opt;
+    Ntp rd_new;
+    Ntp ts_new;
+    Ntp rd_last;
+    Ntp ts_last;
+    bool update_tmstmp = false;
+    if (state->getCheckTimestamps()) {
+        tmstmp_opt = query->getOption(D6O_TIMESTAMP);
+    }
+    if (tmstmp_opt) {
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, SEDHCP6_OPTION_RECEIVED)
+            .arg("timestamp");
+        // Get timestamps in NTP format
+        vector<uint8_t> tmstmp_bin = tmstmp_opt->getData();
+        if (!ts_new.from_binary(tmstmp_bin)) {
             answer->addOption(createStatusCode(STATUS_UnspecFail,
-                        "Malformed certificate option"));
+                        "Malformed timestamp option"));
             return (false);
         }
-        // Compare with the credential for authorization
-        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, SEDHCP6_INCOMING_TRACE)
-            .arg(signopt_required ?
-                 "comparing with config" :
-                 "not comparing with config");
-        if (signopt_required) {
-            CfgSeDhcp6::AsymPtr cred(crypto.createAsym(host_credential,
-                                                       "",
-                                                       sign_algo,
-                                                       hash_algo,
-                                                       key_kind,
-                                                       ASN1),
-                                     deleteAsym);
-            if (!cred) {
-                answer->addOption(createStatusCode(STATUS_AuthenticationFail,
-                            "Bad configured credentials"));
-                return (false);
-            }
-            if (!cred->compare(key.get(), key_kind)) {
-                answer->addOption(createStatusCode(STATUS_AuthenticationFail,
-                            "Credential mismatch"));
-                return (false);
-            }
+        rd_new = Ntp(query->getTimestamp());
+        if (ctx.host_) {
+            rd_last = Ntp(ctx.host_->getRDlast());
+            ts_last = Ntp(ctx.host_->getTSlast());
         }
-        // Handle the timestamp option
-        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, SEDHCP6_INCOMING_TRACE)
-            .arg("handling timestamp");
-        OptionPtr tmstmp_opt;
-        Ntp rd_new;
-        Ntp ts_new;
-        Ntp rd_last;
-        Ntp ts_last;
-        bool update_tmstmp = false;
-        if (state->getCheckTimestamps()) {
-            tmstmp_opt = query->getOption(D6O_TIMESTAMP);
-        }
-        if (tmstmp_opt) {
-            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, SEDHCP6_OPTION_RECEIVED)
-                .arg("timestamp");
-            // Get timestamps in NTP format
-            vector<uint8_t> tmstmp_bin = tmstmp_opt->getData();
-            if (!ts_new.from_binary(tmstmp_bin)) {
-                answer->addOption(createStatusCode(STATUS_UnspecFail,
-                            "Malformed timestamp option"));
+        // Verify the given timestamp
+        bool valid = false;
+        if (rd_last.is_zero() || ts_last.is_zero()) {
+            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
+                      SEDHCP6_INCOMING_TRACE)
+                .arg("new client");
+            valid = Ntp::verify_new(rd_new, ts_new);
+            if (!valid) {
+                answer->addOption(createStatusCode(STATUS_TimestampFail,
+                            "New timestamp too far"));
                 return (false);
             }
-            rd_new = Ntp(query->getTimestamp());
-            if (ctx.host_) {
-                rd_last = Ntp(ctx.host_->getRDlast());
-                ts_last = Ntp(ctx.host_->getTSlast());
+            if (valid && ctx.host_) {
+                update_tmstmp = true;
             }
-            // Verify the given timestamp
-            bool valid = false;
-            if (rd_last.is_zero() || ts_last.is_zero()) {
-                LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
-                          SEDHCP6_INCOMING_TRACE)
-                    .arg("new client");
-                valid = Ntp::verify_new(rd_new, ts_new);
-                if (!valid) {
-                    answer->addOption(createStatusCode(STATUS_TimestampFail,
-                                "New timestamp too far"));
-                    return (false);
-                }
-                if (valid && ctx.host_) {
-                    update_tmstmp = true;
-                }
-            } else {
-                LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
-                          SEDHCP6_INCOMING_TRACE)
-                    .arg("known client");
-                valid = Ntp::verify(rd_new, ts_new, rd_last, ts_last,
-                                    &update_tmstmp);
-                if (!valid) {
-                    answer->addOption(createStatusCode(STATUS_TimestampFail,
-                                "Timestamp out of acceptable range"));
-                    return (false);
-                }
+        } else {
+            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
+                      SEDHCP6_INCOMING_TRACE)
+                .arg("known client");
+            valid = Ntp::verify(rd_new, ts_new, rd_last, ts_last,
+                                &update_tmstmp);
+            if (!valid) {
+                answer->addOption(createStatusCode(STATUS_TimestampFail,
+                            "Timestamp out of acceptable range"));
+                return (false);
             }
         }
+    }
 
-        // Check the signature
-        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, SEDHCP6_INCOMING_TRACE)
-            .arg("checking signature");
-        if (!query->getSignatureOffset()) {
-            LOG_ERROR(dhcp6_logger, SEDHCP6_SIGNATURE_CHECK_FAIL)
-                .arg("null signature offset");
-            return (false);
-        }
-        if (query->getSignatureOffset() + signature->len() >
-            query->rawEnd() - query->rawBegin()) {
-            LOG_ERROR(dhcp6_logger, SEDHCP6_SIGNATURE_CHECK_FAIL)
-                .arg("signature offset overflow");
-            return (false);
-        }
-        OptionBuffer tbs(query->rawBegin(), query->rawEnd());
-        size_t sig_off = query->getSignatureOffset();
-        sig_off += signature->getHeaderLen() + 2;
-        size_t sig_len = signature->len();
-        sig_len -= signature->getHeaderLen() + 2;
-        memset(&tbs[sig_off], 0, sig_len);
-        OptionBuffer sig(query->rawBegin() + sig_off,
-                         query->rawBegin() + sig_off + sig_len);
-        bool valid = false;
-        ostringstream vermsg("signature verify failed");
-        try {
-            key->update(&tbs[0], tbs.size());
-            valid = key->verify(&sig[0], sig_len, BASIC);
-        } catch (const Exception& ex) {
-            vermsg.str("signature verify failed: ");
-            vermsg << ex.what();
-        } catch (...) {
-            vermsg.str("signature verify failed?!");
-        }
-        if (!valid) {
-            answer->addOption(createStatusCode(STATUS_SignatureFail,
-                                               vermsg.str()));
-            return (false);
-        }
+    // Check the signature
+    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, SEDHCP6_INCOMING_TRACE)
+        .arg("checking signature");
+    if (!query->getSignatureOffset()) {
+        LOG_ERROR(dhcp6_logger, SEDHCP6_SIGNATURE_CHECK_FAIL)
+            .arg("null signature offset");
+        return (false);
+    }
+    if (query->getSignatureOffset() + signature->len() >
+        query->rawEnd() - query->rawBegin()) {
+        LOG_ERROR(dhcp6_logger, SEDHCP6_SIGNATURE_CHECK_FAIL)
+            .arg("signature offset overflow");
+        return (false);
+    }
+    OptionBuffer tbs(query->rawBegin(), query->rawEnd());
+    size_t sig_off = query->getSignatureOffset();
+    sig_off += signature->getHeaderLen() + 2;
+    size_t sig_len = signature->len();
+    sig_len -= signature->getHeaderLen() + 2;
+    memset(&tbs[sig_off], 0, sig_len);
+    OptionBuffer sig(query->rawBegin() + sig_off,
+                     query->rawBegin() + sig_off + sig_len);
+    bool valid = false;
+    ostringstream vermsg("signature verify failed");
+    try {
+        key->update(&tbs[0], tbs.size());
+        valid = key->verify(&sig[0], sig_len, BASIC);
+    } catch (const Exception& ex) {
+        vermsg.str("signature verify failed: ");
+        vermsg << ex.what();
+    } catch (...) {
+        vermsg.str("signature verify failed?!");
+    }
+    if (!valid) {
+        answer->addOption(createStatusCode(STATUS_SignatureFail,
+                                           vermsg.str()));
+        return (false);
+    }
+    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, SEDHCP6_INCOMING_TRACE)
+        .arg("signature is valid");
+
+    // Update timestamps
+    if (update_tmstmp) {
         LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, SEDHCP6_INCOMING_TRACE)
-            .arg("signature is valid");
-
-        // Update timestamps
-        if (update_tmstmp) {
-            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, SEDHCP6_INCOMING_TRACE)
-                .arg("updating timestamps");
-            Host* hp = const_cast<Host*>(ctx.host_.get());
-            hp->setRDlast(rd_new);
-            hp->setTSlast(ts_new);
-            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
-                      SEDHCP6_TIMESTAMP_UPDATED);
-        }
+            .arg("updating timestamps");
+        Host* hp = const_cast<Host*>(ctx.host_.get());
+        hp->setRDlast(rd_new);
+        hp->setTSlast(ts_new);
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
+                  SEDHCP6_TIMESTAMP_UPDATED);
     }
 
     // Done
index 5aaf80154fa46c43ccda970e188ec53732327f2a..47b1964eae21f079d22389f34095eff78fbd793a 100644 (file)
@@ -48,6 +48,7 @@ perfdhcp_LDADD = libperfdhcp.la
 perfdhcp_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
 perfdhcp_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
 perfdhcp_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
+perfdhcp_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
 perfdhcp_LDADD += $(top_builddir)/src/bin/cfgrpt/libcfgrpt.la
 
 # ... and the documentation
index 36ac901e941d07b16697d0d20b5409fabd926d8c..f144364bba15840b074ff01812f602dab3afb340 100644 (file)
@@ -43,6 +43,7 @@ endif
 
 run_unittests_LDADD  = $(top_builddir)/src/bin/perfdhcp/libperfdhcp.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
+run_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
 run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
 run_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
index 7996b9118455543d584eb254761ad0befd9848ac..31db397858b4aec74da06df53480d8c9b7d26eb8 100644 (file)
@@ -31,6 +31,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
 run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
 run_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
 run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libkea-asiodns.la
 
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
index c515d66e0f78e70ef3768b40ef104508d0d1ed7e..9f785e8e55fa3d22d1b9848bc07ed19a676dbec0 100644 (file)
 #include <botan/x509stor.h>
 
 #include <util/encode/base64.h>
+#include <hooks/hooks_manager.h>
+#include <hooks/callout_handle.h>
 #include <cryptolink/botan_common.h>
 
 #include <cstring>
 #include <fstream>
 
+namespace {
+
+///< index of "validate_certificate" hook point
+int hook_point_validate_certificate =
+    isc::hooks::HooksManager::registerHook("validate_certificate");
+
+} // anonymous namespace
+
 namespace isc {
 namespace cryptolink {
 
@@ -1338,6 +1348,33 @@ Asym::exportkey(const std::string& filename,
 
 bool
 Asym::validate() const {
+    // Hook only certificate validation
+    if (getAsymKeyKind() != CERT) {
+        return (impl_->validate());
+    }
+
+    // Call the hook if available
+    using namespace isc::hooks;
+    if (HooksManager::calloutsPresent(hook_point_validate_certificate)) {
+        // Callout handle (static in order to reuse it)
+        CalloutHandlePtr callout_handle_ = HooksManager::createCalloutHandle();
+
+        // Delete add previous arguments
+        callout_handle_->deleteAllArguments();
+
+        // Pass the certificate (der in a vector)
+        callout_handle_->setArgument("certificate", exportkey(CERT, ASN1));
+
+        // Call the callouts
+        HooksManager::callCallouts(hook_point_validate_certificate,
+                                   *callout_handle_);
+
+        // Callouts decided to skip the action. This means that
+        // validation failed, so return false.
+        return (!callout_handle_->getSkip());
+    }
+
+    // No hooks
     return (impl_->validate());
 }
 
index 957aa79bb2c2efd3c89b6fbe30b79a4d90d4aa1b..644a71ff6c4079d8c563e49ea04aeadec654aea6 100644 (file)
 #include <openssl/x509.h>
 
 #include <util/encode/base64.h>
+#include <hooks/hooks_manager.h>
+#include <hooks/callout_handle.h>
 #include <cryptolink/openssl_common.h>
 
 #include <cstdio>
 #include <cstring>
 
+namespace {
+
+///< index of "validate_certificate" hook point
+int hook_point_validate_certificate =
+    isc::hooks::HooksManager::registerHook("validate_certificate");
+
+} // anonymous namespace
+
 namespace isc {
 namespace cryptolink {
 
@@ -1565,6 +1575,33 @@ Asym::exportkey(const std::string& filename,
 
 bool
 Asym::validate() const {
+    // Hook only certificate validation
+    if (getAsymKeyKind() != CERT) {
+        return (impl_->validate());
+    }
+
+    // Call the hook if available
+    using namespace isc::hooks;
+    if (HooksManager::calloutsPresent(hook_point_validate_certificate)) {
+        // Callout handle
+        CalloutHandlePtr callout_handle_ = HooksManager::createCalloutHandle();
+
+        // Delete add previous arguments
+        callout_handle_->deleteAllArguments();
+
+        // Pass the certificate (der in a vector)
+        callout_handle_->setArgument("certificate", exportkey(CERT, ASN1));
+
+        // Call the callouts
+        HooksManager::callCallouts(hook_point_validate_certificate,
+                                   *callout_handle_);
+
+        // Callouts decided to skip the action. This means that
+        // validation failed, so return false.
+        return (!callout_handle_->getSkip());
+    }
+
+    // No hooks
     return (impl_->validate());
 }
 
index d209f4ef19a5555f6890137726bf434c6891c888..4f40874a52e32ef6d7a7266d8469ad55c9b27dc0 100644 (file)
@@ -30,6 +30,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
 run_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
 endif
 
 noinst_PROGRAMS = $(TESTS) from_bind9 to_bind9
index c2658e4d09711e2ca262e88ef1f930c8cacf16ed..ee6fb17a2fde01f590ef2039f393cb6ceb33b31b 100644 (file)
@@ -25,6 +25,9 @@
 
 #include <util/encode/base64.h>
 
+#include <hooks/hooks_manager.h>
+#include <hooks/callout_handle.h>
+
 #include <cryptolink/cryptolink.h>
 #include <cryptolink/crypto_asym.h>
 
@@ -898,3 +901,51 @@ TEST(AsymTest, doubleVerify) {
     rsa_verify->update(data_buf.getData(), data_buf.getLength());
     EXPECT_TRUE(rsa_verify->verify(sig.getData(), sig.getLength(), BASIC));
 }
+
+namespace {
+
+std::string callout_name("");
+std::vector<uint8_t> callout_cert;
+std::vector<std::string> callout_argument_names;
+
+int
+validate_certificate_callout(isc::hooks::CalloutHandle& callout_handle) {
+    callout_name = "validate-certificate";
+    callout_handle.getArgument("certificate", callout_cert);
+    callout_argument_names = callout_handle.getArgumentNames();
+    return (0);
+}
+
+}
+
+//
+// Hooks
+//
+TEST(AsymTest, callout) {
+    using namespace isc::hooks;
+
+    // Initialize Hooks Manager
+    std::vector<std::string> libraries; // no libraries
+    HooksManager::loadLibraries(libraries);
+
+    // Install validate_certificate_callout
+    LibraryHandle& preCLH = HooksManager::preCalloutsLibraryHandle();
+    EXPECT_NO_THROW(preCLH.registerCallout("validate_certificate",
+                                          validate_certificate_callout));
+
+    // Get a certificate and validate it
+    CryptoLink& crypto = CryptoLink::getCryptoLink();
+    boost::shared_ptr<Asym> cert(crypto.createAsym(certfile, "",
+                                                  RSA_, SHA1,
+                                                  CERT, ASN1),
+                                deleteAsym);
+    EXPECT_TRUE(cert->validate());
+
+    // Check that callouts were indeed called
+    EXPECT_EQ("validate-certificate", callout_name);
+    std::vector<uint8_t> bin = cert->exportkey(CERT, ASN1);
+    ASSERT_EQ(bin.size(), callout_cert.size());
+    EXPECT_TRUE(std::memcmp(&bin[0], &callout_cert[0], bin.size()) == 0);
+    ASSERT_EQ(1, callout_argument_names.size());
+    EXPECT_TRUE(callout_argument_names[0].compare("certificate") == 0);
+}
index a2181cf47d029018e0af15a401a16ebd240ea1ad..f04e8c8d9c970c4eb5fc4c33fdda657bfcde6583 100644 (file)
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <gtest/gtest.h>
+#include <log/logger_support.h>
 #include <util/unittests/run_all.h>
 
+#include <gtest/gtest.h>
+
 int
 main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
-
+    isc::log::initLogger();
     return (isc::util::unittests::run_all());
 }
index 186396ce8c774ad76abaefb721d8de3f4b10a46a..c5b334996087204ca7a3f70119b0f6cd52835c50 100644 (file)
@@ -111,6 +111,7 @@ libdhcp___unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
 libdhcp___unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
 libdhcp___unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
 libdhcp___unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
+libdhcp___unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
 libdhcp___unittests_LDADD += $(GTEST_LDADD)
 endif
 
index 5c7c4db3d1155305bcfd37b896d868dc4e7a380b..2d4c92bb116c76a862b1282529162e045bcf1b14 100644 (file)
@@ -46,6 +46,7 @@ libkea_dhcp_ddns_la_LIBADD  =
 libkea_dhcp_ddns_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la
 libkea_dhcp_ddns_la_LIBADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la
 libkea_dhcp_ddns_la_LIBADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
+libkea_dhcp_ddns_la_LIBADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
 libkea_dhcp_ddns_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la
 libkea_dhcp_ddns_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la
 libkea_dhcp_ddns_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
index 9e49e1426cc220171ee9bdae3b7503b7ccb651f0..ae3d76a02f5b0705395d935e508fda8641f877e8 100644 (file)
@@ -158,6 +158,7 @@ libkea_dns___la_CPPFLAGS = $(AM_CPPFLAGS)
 libkea_dns___la_LIBADD = $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la
 libkea_dns___la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la
 libkea_dns___la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+libkea_dns___la_LIBADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
 
 # The following files used to be generated, but they are now part of the git tree:
 # rrclass.h rrtype.h rrparamregistry.cc rdataclass.h rdataclass.cc