# And there should be no error code.
self.assertEqual(TSIGError(Rcode.NOERROR()), self.tsig_ctx.get_error())
+ # No message signed yet
+ self.assertRaises(TSIGContextError, self.tsig_ctx.last_had_signature)
+
# Note: intentionally use camelCase so that we can easily copy-paste
# corresponding C++ tests.
def commonVerifyChecks(self, ctx, record, data, expected_error,
expected_new_state=\
- TSIGContext.STATE_VERIFIED_RESPONSE):
+ TSIGContext.STATE_VERIFIED_RESPONSE,
+ last_should_throw=False):
self.assertEqual(expected_error, ctx.verify(record, data))
self.assertEqual(expected_error, ctx.get_error())
self.assertEqual(expected_new_state, ctx.get_state())
-
+ if last_should_throw:
+ self.assertRaises(TSIGContextError, ctx.last_had_signature)
+ else:
+ self.assertEqual(record is not None,
+ ctx.last_had_signature())
def test_from_keyring(self):
# Construct a TSIG context with an empty key ring. Key shouldn't be
# found, and the BAD_KEY error should be recorded.
tsig = self.createMessageAndSign(self.qid, self.test_name,
self.tsig_ctx, 0, RRType.SOA())
-
+
fix_current_time(0x4da8b9d6 + 301)
self.assertEqual(TSIGError.BAD_TIME,
self.tsig_verify_ctx.verify(tsig, DUMMY_DATA))
self.createMessageAndSign(self.qid, self.test_name, self.tsig_ctx)
self.commonVerifyChecks(self.tsig_ctx, None, DUMMY_DATA,
- TSIGError.FORMERR, TSIGContext.STATE_SENT_REQUEST)
+ TSIGError.FORMERR, TSIGContext.STATE_SENT_REQUEST,
+ True)
self.createMessageFromFile("tsig_verify5.wire")
self.commonVerifyChecks(self.tsig_ctx, self.message.get_tsig_record(),
PyObject* TSIGContext_getError(s_TSIGContext* self);
PyObject* TSIGContext_sign(s_TSIGContext* self, PyObject* args);
PyObject* TSIGContext_verify(s_TSIGContext* self, PyObject* args);
+PyObject* TSIGContext_lastHadSignature(s_TSIGContext* self);
// These are the functions we export
// For a minimal support, we don't need them.
{ "verify",
reinterpret_cast<PyCFunction>(TSIGContext_verify), METH_VARARGS,
"Verify a DNS message." },
+ { "last_had_signature",
+ reinterpret_cast<PyCFunction>(TSIGContext_lastHadSignature), METH_NOARGS,
+ "Return if the last verified message contained a signature" },
{ NULL, NULL, 0, NULL }
};
return (NULL);
}
+
+PyObject*
+TSIGContext_lastHadSignature(s_TSIGContext* self) {
+ try {
+ long result = self->cppobj->lastHadSignature();
+ return (PyBool_FromLong(result));
+ } catch (const TSIGContextError& ex) {
+ PyErr_SetString(po_TSIGContextError, ex.what());
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Unexpected failure in TSIG lastHadSignature: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError,
+ "Unexpected failure in TSIG lastHadSignature");
+ }
+
+ return (NULL);
+}
} // end of unnamed namespace
namespace isc {
commonVerifyChecks(TSIGContext& ctx, const TSIGRecord* record,
const void* data, size_t data_len, TSIGError expected_error,
TSIGContext::State expected_new_state =
- TSIGContext::VERIFIED_RESPONSE)
+ TSIGContext::VERIFIED_RESPONSE,
+ bool last_should_throw = false)
{
EXPECT_EQ(expected_error, ctx.verify(record, data, data_len));
EXPECT_EQ(expected_error, ctx.getError());
EXPECT_EQ(expected_new_state, ctx.getState());
+ if (last_should_throw) {
+ EXPECT_THROW(ctx.lastHadSignature(), TSIGContextError);
+ } else {
+ EXPECT_EQ(record != NULL, ctx.lastHadSignature());
+ }
}
TEST_F(TSIGTest, initialState) {
// And there should be no error code.
EXPECT_EQ(TSIGError(Rcode::NOERROR()), tsig_ctx->getError());
+
+ // Nothing verified yet
+ EXPECT_THROW(tsig_ctx->lastHadSignature(), TSIGContextError);
}
TEST_F(TSIGTest, constructFromKeyRing) {
12 + dummy_record.getLength() - 1),
InvalidParameter);
+ // Still nothing verified
+ EXPECT_THROW(tsig_ctx->lastHadSignature(), TSIGContextError);
+
// And the data must not be NULL.
EXPECT_THROW(tsig_ctx->verify(&dummy_record, NULL,
12 + dummy_record.getLength()),
InvalidParameter);
+
+ // Still nothing verified
+ EXPECT_THROW(tsig_ctx->lastHadSignature(), TSIGContextError);
+
}
#ifdef ENABLE_CUSTOM_OPERATOR_NEW
SCOPED_TRACE("Verify a response without TSIG that should exist");
commonVerifyChecks(*tsig_ctx, NULL, &dummy_data[0],
dummy_data.size(), TSIGError::FORMERR(),
- TSIGContext::SENT_REQUEST);
+ TSIGContext::SENT_REQUEST, true);
}
createMessageFromFile("tsig_verify5.wire");
TSIGContextImpl(const TSIGKey& key,
TSIGError error = TSIGError::NOERROR()) :
state_(INIT), key_(key), error_(error),
- previous_timesigned_(0), digest_len_(0)
+ previous_timesigned_(0), digest_len_(0),
+ last_sig_dist_(-1)
{
if (error == TSIGError::NOERROR()) {
// In normal (NOERROR) case, the key should be valid, and we
uint64_t previous_timesigned_; // only meaningful for response with BADTIME
size_t digest_len_;
HMACPtr hmac_;
+ // This is the distance from the last verified signed message. Value of 0
+ // means the last message was signed. Special value -1 means there was no
+ // signed message yet.
+ int last_sig_dist_;
};
void
isc_throw(InvalidParameter, "TSIG verify: empty data is invalid");
}
+ // This message is signed and we won't throw any more.
+ impl_->last_sig_dist_ = 0;
+
// Check key: whether we first verify it with a known key or we verify
// it using the consistent key in the context. If the check fails we are
// done with BADKEY.
return (impl_->postVerifyUpdate(TSIGError::BAD_SIG(), NULL, 0));
}
+bool
+TSIGContext::lastHadSignature() const {
+ if (impl_->last_sig_dist_ == -1) {
+ isc_throw(TSIGContextError, "No message was verified yet");
+ }
+ return (impl_->last_sig_dist_ == 0);
+}
+
} // namespace dns
} // namespace isc