.. table:: List of available parameters
- +-------------------+----------+---------+---------------------+--------------------------------+
- | Name | Scope | Type | Default value | Description |
- | | | | | |
- +===================+==========+=========+=====================+================================+
- | client-keytab | global / | string | empty | the Kerberos **client** key |
- | | server | | | table |
- +-------------------+----------+---------+---------------------+--------------------------------+
- | credentials-cache | global / | string | empty | the Kerberos credentials cache |
- | | server | | | |
- +-------------------+----------+---------+---------------------+--------------------------------+
- | server-principal | global / | string | empty | the Kerberos principal name of |
- | | server | | | the DNS server that will |
- | | | | | receive updates |
- +-------------------+----------+---------+---------------------+--------------------------------+
- | client-principal | global / | string | empty | the Kerberos principal name of |
- | | server | | | the Kea D2 service |
- +-------------------+----------+---------+---------------------+--------------------------------+
- | gss-replay-flag | global / | true / | true | require the GSS anti replay |
- | | server | false | | service (GSS_C_REPLAY_FLAG) |
- +-------------------+----------+---------+---------------------+--------------------------------+
- | gss-sequence-flag | global / | true / | false | require the GSS sequence |
- | | server | false | | service (GSS_C_SEQUENCE_FLAG) |
- +-------------------+----------+---------+---------------------+--------------------------------+
- | tkey-protocol | global / | string | "TCP" | the protocol used to establish |
- | | server | "TCP" / | | the security context with the |
- | | | "UDP" | | DNS servers |
- +-------------------+----------+---------+---------------------+--------------------------------+
- | tkey-lifetime | global / | uint32 | | 3600 seconds | the lifetime of GSS-TSIG keys |
- | | server | | | ( 1 hour ) | |
- +-------------------+----------+---------+---------------------+--------------------------------+
- | rekey-interval | global / | uint32 | | 2700 seconds | the time interval the keys are |
- | | server | | | ( 45 minutes ) | checked for rekeying |
- +-------------------+----------+---------+---------------------+--------------------------------+
- | retry-interval | global / | uint32 | | 120 seconds | the time interval to retry to |
- | | server | | | ( 2 minutes ) | create a key if any error |
- | | | | | occurred previously |
- +-------------------+----------+---------+---------------------+--------------------------------+
- | fallback | global / | true / | false | the behavior to fallback to |
- | | server | false | | non-GSS-TSIG when GSS-TSIG |
- | | | | | should be used but no GSS-TSIG |
- | | | | | key is available. |
- +-------------------+----------+---------+---------------------+--------------------------------+
- | exchange-timeout | global / | uint32 | | 3000 milliseconds | the time used to wait for the |
- | | server | | | ( 3 seconds ) | GSS-TSIG TKEY exchange to |
- | | | | | finish before it timeouts |
- +-------------------+----------+---------+---------------------+--------------------------------+
- | user-context | global / | string | empty | the user-provided data in JSON |
- | | server | | | format (not used by |
- | | | | | the GSS-TSIG hook) |
- +-------------------+----------+---------+---------------------+--------------------------------+
- | comment | global / | string | empty | ignored |
- | | server | | | |
- +-------------------+----------+---------+---------------------+--------------------------------+
- | id | server | string | empty | identifier to a DNS server |
- | | | | | (required) |
- +-------------------+----------+---------+---------------------+--------------------------------+
- | domain-names | server | list of | empty | the many-to-one relationship |
- | | | strings | | between D2 DNS servers and |
- | | | | | GSS-TSIG DNS servers |
- +-------------------+----------+---------+---------------------+--------------------------------+
- | ip-address | server | IPv4 / | empty | the IP address at which the |
- | | | IPv6 | | GSS-TSIG DNS server listens |
- | | | address | | for DDNS and TKEY requests |
- | | | | | (required) |
- +-------------------+----------+---------+---------------------+--------------------------------+
- | port | server | uint16 | 53 | the DNS transport port at |
- | | | | | which the GSS-TSIG DNS server |
- | | | | | listens for DDNS and TKEY |
- | | | | | requests |
- +-------------------+----------+---------+---------------------+--------------------------------+
+ +----------------------+----------+---------+---------------------+--------------------------------+
+ | Name | Scope | Type | Default value | Description |
+ | | | | | |
+ +======================+==========+=========+=====================+================================+
+ | client-keytab | global / | string | empty | the Kerberos **client** key |
+ | | server | | | table |
+ +----------------------+----------+---------+---------------------+--------------------------------+
+ | credentials-cache | global / | string | empty | the Kerberos credentials cache |
+ | | server | | | |
+ +----------------------+----------+---------+---------------------+--------------------------------+
+ | server-principal | global / | string | empty | the Kerberos principal name of |
+ | | server | | | the DNS server that will |
+ | | | | | receive updates |
+ +----------------------+----------+---------+---------------------+--------------------------------+
+ | client-principal | global / | string | empty | the Kerberos principal name of |
+ | | server | | | the Kea D2 service |
+ +----------------------+----------+---------+---------------------+--------------------------------+
+ | gss-replay-flag | global / | true / | true | require the GSS anti replay |
+ | | server | false | | service (GSS_C_REPLAY_FLAG) |
+ +----------------------+----------+---------+---------------------+--------------------------------+
+ | gss-sequence-flag | global / | true / | false | require the GSS sequence |
+ | | server | false | | service (GSS_C_SEQUENCE_FLAG) |
+ +----------------------+----------+---------+---------------------+--------------------------------+
+ | tkey-protocol | global / | string | "TCP" | the protocol used to establish |
+ | | server | "TCP" / | | the security context with the |
+ | | | "UDP" | | DNS servers |
+ +----------------------+----------+---------+---------------------+--------------------------------+
+ | tkey-lifetime | global / | uint32 | | 3600 seconds | the lifetime of GSS-TSIG keys |
+ | | server | | | ( 1 hour ) | |
+ +----------------------+----------+---------+---------------------+--------------------------------+
+ | rekey-interval | global / | uint32 | | 2700 seconds | the time interval the keys are |
+ | | server | | | ( 45 minutes ) | checked for rekeying |
+ +----------------------+----------+---------+---------------------+--------------------------------+
+ | retry-interval | global / | uint32 | | 120 seconds | the time interval to retry to |
+ | | server | | | ( 2 minutes ) | create a key if any error |
+ | | | | | occurred previously |
+ +----------------------+----------+---------+---------------------+--------------------------------+
+ | fallback | global / | true / | false | the behavior to fallback to |
+ | | server | false | | non-GSS-TSIG when GSS-TSIG |
+ | | | | | should be used but no GSS-TSIG |
+ | | | | | key is available. |
+ +----------------------+----------+---------+---------------------+--------------------------------+
+ | exchange-timeout | global / | uint32 | | 3000 milliseconds | the time used to wait for the |
+ | | server | | | ( 3 seconds ) | GSS-TSIG TKEY exchange to |
+ | | | | | finish before it timeouts |
+ +----------------------+----------+---------+---------------------+--------------------------------+
+ | ignore-bad-direction | global | true / | false | ignore invalid MIC / bad |
+ | | | false | | direction verify failures. |
+ +----------------------+----------+---------+---------------------+--------------------------------+
+ | user-context | global / | string | empty | the user-provided data in JSON |
+ | | server | | | format (not used by |
+ | | | | | the GSS-TSIG hook) |
+ +----------------------+----------+---------+---------------------+--------------------------------+
+ | comment | global / | string | empty | ignored |
+ | | server | | | |
+ +----------------------+----------+---------+---------------------+--------------------------------+
+ | id | server | string | empty | identifier to a DNS server |
+ | | | | | (required) |
+ +----------------------+----------+---------+---------------------+--------------------------------+
+ | domain-names | server | list of | empty | the many-to-one relationship |
+ | | | strings | | between D2 DNS servers and |
+ | | | | | GSS-TSIG DNS servers |
+ +----------------------+----------+---------+---------------------+--------------------------------+
+ | ip-address | server | IPv4 / | empty | the IP address at which the |
+ | | | IPv6 | | GSS-TSIG DNS server listens |
+ | | | address | | for DDNS and TKEY requests |
+ | | | | | (required) |
+ +----------------------+----------+---------+---------------------+--------------------------------+
+ | port | server | uint16 | 53 | the DNS transport port at |
+ | | | | | which the GSS-TSIG DNS server |
+ | | | | | listens for DDNS and TKEY |
+ | | | | | requests |
+ +----------------------+----------+---------+---------------------+--------------------------------+
The global parameters are described below:
level. The default and supported values for the per-server level parameter are the same as
for the global-level parameter.
+- ``ignore-bad-direction`` governs a workaround for Microsoft server bug.
+ When set explicitly to true DNS update responses sent when prerequisites
+ fail with the request signature are accepted as verified.
+
- ``user-context`` is an optional parameter (see :ref:`user-context`
for a general description of user contexts in Kea).
#ifndef GSS_TSIG_API_UTILS_H
#define GSS_TSIG_API_UTILS_H
+#include <gss_tsig_context.h>
#include <gtest/gtest.h>
#include <cstdlib>
has_ccname = true;
ccname = std::string(ccname_env);
}
+ GssApiSecCtx::ignore_bad_direction_ = false;
}
/// @brief Destructor.
} else {
unsetenv("KRB5CCNAME");
}
+ GssApiSecCtx::ignore_bad_direction_ = false;
}
/// @brief Set the keytab.
"\"tkey-lifetime\": 7200,\n"
"\"tkey-protocol\": \"UDP\",\n"
"\"exchange-timeout\": 2000,\n"
+ "\"ignore-bad-direction\": true,\n"
"\"servers\": [\n"
" {\n"
" \"domain-names\": [ ],\n"
EXPECT_EQ("FILE:/etc/krb5.keytab", cfg.getClientKeyTab());
EXPECT_EQ("FILE:/etc/ccache", cfg.getCredsCache());
EXPECT_EQ(86400, cfg.getMaxKeyLifetime());
+ EXPECT_TRUE(cfg.getIgnoreBadDirection());
+ EXPECT_TRUE(GssApiSecCtx::ignore_bad_direction_);
+ // Put this in the fixture if one is created...
+ GssApiSecCtx::ignore_bad_direction_ = false;
const DnsServerList& servers = cfg.getServerList();
ASSERT_EQ(2, servers.size());
expected += location;
EXPECT_THROW_MSG(cfg.configure(json), BadValue, expected);
+ config = "{ \"ignore-bad-direction\": 1 }";
+ ASSERT_NO_THROW(json = Element::fromJSON(config));
+ ASSERT_TRUE(json);
+ expected = "gss_tsig 'ignore-bad-direction' parameter is not a boolean";
+ expected += location;
+ EXPECT_THROW_MSG(cfg.configure(json), BadValue, expected);
+
config = "{ \"user-context\": [ ] }";
ASSERT_NO_THROW(json = Element::fromJSON(config));
ASSERT_TRUE(json);
EXPECT_THROW_MSG(cfg.configure(json), BadValue, expected);
}
+/// @brief Checks ignore bad direction default value.
+TEST(GssTsigCfgTest, IgnoreBadDirectionDefault) {
+ // Constructor default is false.
+ GssTsigCfg cfg;
+ ASSERT_FALSE(cfg.getIgnoreBadDirection());
+ ASSERT_FALSE(GssApiSecCtx::ignore_bad_direction_);
+ ConstElementPtr json = Element::createMap();
+ ASSERT_NO_THROW(cfg.configure(json));
+ EXPECT_FALSE(cfg.getIgnoreBadDirection());
+ EXPECT_FALSE(GssApiSecCtx::ignore_bad_direction_);
+}
+
/// @brief Check TKEY protocol default value.
TEST(GssTsigCfgTest, tkeyProtoDefault) {
// Constructor default is TCP.
EXPECT_EQ(GSS_S_BAD_SIG, srv_key->getSecCtx().getLastError());
}
+/// @brief Check that verify fail on bad direction.
+TEST_F(GssTsigContextTest, badDirection) {
+ GssTsigKeyPtr key;
+ string name = "1234.sig-foo.example.com.";
+ ASSERT_NO_THROW(key.reset(new GssTsigKey(name)));
+ ASSERT_TRUE(key);
+
+ setKeytab();
+ setAdministratorCCache();
+
+ // Server.
+ GssTsigKeyPtr srv_key;
+ ASSERT_NO_THROW(srv_key.reset(new GssTsigKey(name)));
+ ASSERT_TRUE(srv_key);
+ EXPECT_FALSE(srv_key->getSecCtx().get());
+ GssApiName srv_name("DNS/blu.example.nil@EXAMPLE.NIL");
+ OM_uint32 lifetime = 0;
+ GssApiCredPtr srv_cred(new GssApiCred(srv_name, GSS_C_ACCEPT, lifetime));
+
+ // Client.
+ GssApiName clnt_name;
+ GssApiCredPtr cred;
+ EXPECT_FALSE(key->getSecCtx().get());
+ OM_uint32 flags = GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG;
+ bool ret = false;
+
+ // Exchanges.
+ GssApiBuffer intoken0;
+ GssApiBuffer outtoken0;
+ ASSERT_NO_THROW(ret = key->getSecCtx().init(cred, srv_name, flags,
+ intoken0, outtoken0,
+ lifetime));
+ ASSERT_FALSE(outtoken0.empty());
+ GssApiBuffer outtoken1;
+ ASSERT_NO_THROW(ret = srv_key->getSecCtx().accept(*srv_cred, outtoken0,
+ clnt_name, outtoken1));
+ EXPECT_TRUE(ret);
+ GssApiBuffer outtoken2;
+ ASSERT_NO_THROW(ret = key->getSecCtx().init(cred, srv_name, flags,
+ outtoken1, outtoken2,
+ lifetime));
+ ASSERT_TRUE(ret);
+ EXPECT_TRUE(outtoken2.empty());
+
+ // Build the message to sign.
+ message_.clear(Message::RENDER);
+ message_.setQid(0x1234);
+ message_.setOpcode(Opcode::QUERY());
+ message_.setRcode(Rcode::NOERROR());
+ message_.setHeaderFlag(Message::HEADERFLAG_QR);
+ Name qname("foo.example.nil.");
+ message_.addQuestion(Question(qname, RRClass::IN(), RRType::A()));
+ MessageRenderer renderer;
+ OutputBuffer obuf(1024);
+ renderer.setBuffer(&obuf);
+
+ // Sign.
+ GssTsigContextPtr ctx;
+ ASSERT_NO_THROW(ctx.reset(new GssTsigContext(*key)));
+ ASSERT_TRUE(ctx);
+ EXPECT_NO_THROW(message_.toWire(renderer, ctx.get()));
+ // len is 33 + 62 + 28 = 123.
+ ASSERT_TRUE(obuf.getData());
+ EXPECT_EQ(123, obuf.getLength());
+
+ // Check the TSIG RR.
+ message_.clear(Message::PARSE);
+ InputBuffer ibuf(obuf.getData(), obuf.getLength());
+ EXPECT_NO_THROW(message_.fromWire(ibuf));
+ const TSIGRecord* tsig = message_.getTSIGRecord();
+ ASSERT_TRUE(tsig);
+
+ // Verify using the wrong key.
+ ASSERT_NO_THROW(ctx.reset(new GssTsigContext(*key)));
+ ASSERT_TRUE(ctx);
+ TSIGError error = TSIGError::NOERROR();
+ EXPECT_NO_THROW(error = ctx->verify(tsig, obuf.getData(),
+ obuf.getLength()));
+ // Error message is 'A token had an invalid Message Integrity Check (MIC)',
+ // and 'Packet was replayed in wrong direction'.
+ EXPECT_EQ(TSIGError::BAD_SIG(), error);
+ EXPECT_EQ(TSIGError::BAD_SIG(), ctx->getError());
+ EXPECT_EQ(TSIGContext::RECEIVED_REQUEST, ctx->getState());
+ EXPECT_EQ(GSS_S_BAD_SIG, key->getSecCtx().getLastError());
+}
+
+/// @brief Check that verify fail on bad direction instead ignored.
+TEST_F(GssTsigContextTest, ignoreBadDirection) {
+ GssTsigKeyPtr key;
+ string name = "1234.sig-foo.example.com.";
+ ASSERT_NO_THROW(key.reset(new GssTsigKey(name)));
+ ASSERT_TRUE(key);
+
+ setKeytab();
+ setAdministratorCCache();
+
+ // Server.
+ GssTsigKeyPtr srv_key;
+ ASSERT_NO_THROW(srv_key.reset(new GssTsigKey(name)));
+ ASSERT_TRUE(srv_key);
+ EXPECT_FALSE(srv_key->getSecCtx().get());
+ GssApiName srv_name("DNS/blu.example.nil@EXAMPLE.NIL");
+ OM_uint32 lifetime = 0;
+ GssApiCredPtr srv_cred(new GssApiCred(srv_name, GSS_C_ACCEPT, lifetime));
+
+ // Client.
+ GssApiName clnt_name;
+ GssApiCredPtr cred;
+ EXPECT_FALSE(key->getSecCtx().get());
+ OM_uint32 flags = GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG;
+ bool ret = false;
+
+ // Exchanges.
+ GssApiBuffer intoken0;
+ GssApiBuffer outtoken0;
+ ASSERT_NO_THROW(ret = key->getSecCtx().init(cred, srv_name, flags,
+ intoken0, outtoken0,
+ lifetime));
+ ASSERT_FALSE(outtoken0.empty());
+ GssApiBuffer outtoken1;
+ ASSERT_NO_THROW(ret = srv_key->getSecCtx().accept(*srv_cred, outtoken0,
+ clnt_name, outtoken1));
+ EXPECT_TRUE(ret);
+ GssApiBuffer outtoken2;
+ ASSERT_NO_THROW(ret = key->getSecCtx().init(cred, srv_name, flags,
+ outtoken1, outtoken2,
+ lifetime));
+ ASSERT_TRUE(ret);
+ EXPECT_TRUE(outtoken2.empty());
+
+ // Build the message to sign.
+ message_.clear(Message::RENDER);
+ message_.setQid(0x1234);
+ message_.setOpcode(Opcode::QUERY());
+ message_.setRcode(Rcode::NOERROR());
+ message_.setHeaderFlag(Message::HEADERFLAG_QR);
+ Name qname("foo.example.nil.");
+ message_.addQuestion(Question(qname, RRClass::IN(), RRType::A()));
+ MessageRenderer renderer;
+ OutputBuffer obuf(1024);
+ renderer.setBuffer(&obuf);
+
+ // Sign.
+ GssTsigContextPtr ctx;
+ ASSERT_NO_THROW(ctx.reset(new GssTsigContext(*key)));
+ ASSERT_TRUE(ctx);
+ EXPECT_NO_THROW(message_.toWire(renderer, ctx.get()));
+ // len is 33 + 62 + 28 = 123.
+ ASSERT_TRUE(obuf.getData());
+ EXPECT_EQ(123, obuf.getLength());
+
+ // Check the TSIG RR.
+ message_.clear(Message::PARSE);
+ InputBuffer ibuf(obuf.getData(), obuf.getLength());
+ EXPECT_NO_THROW(message_.fromWire(ibuf));
+ const TSIGRecord* tsig = message_.getTSIGRecord();
+ ASSERT_TRUE(tsig);
+
+ // Verify using the wrong key.
+ ASSERT_NO_THROW(ctx.reset(new GssTsigContext(*key)));
+ ASSERT_TRUE(ctx);
+ TSIGError error = TSIGError::NOERROR();
+ GssApiSecCtx::ignore_bad_direction_ = true;
+ EXPECT_NO_THROW(error = ctx->verify(tsig, obuf.getData(),
+ obuf.getLength()));
+ // No longer fail even the signature was in the wrong direction.
+ EXPECT_EQ(TSIGError::NOERROR(), error);
+ EXPECT_TRUE(ctx->lastHadSignature());
+ EXPECT_EQ(TSIGError::NOERROR(), ctx->getError());
+ EXPECT_EQ(TSIGContext::RECEIVED_REQUEST, ctx->getState());
+}
+
}