Browse Source

[1357] Implement TSIGContext::lastHadSignature

Michal 'vorner' Vaner 12 years ago
parent
commit
dbe1509449

+ 13 - 4
src/lib/dns/python/tests/tsig_python_test.py

@@ -122,15 +122,23 @@ class TSIGContextTest(unittest.TestCase):
         # 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.
@@ -354,7 +362,7 @@ class TSIGContextTest(unittest.TestCase):
 
         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))
@@ -454,7 +462,8 @@ class TSIGContextTest(unittest.TestCase):
         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(),

+ 24 - 0
src/lib/dns/python/tsig_python.cc

@@ -66,6 +66,7 @@ PyObject* TSIGContext_getState(s_TSIGContext* self);
 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.
@@ -89,6 +90,9 @@ PyMethodDef TSIGContext_methods[] = {
     { "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 }
 };
 
@@ -234,6 +238,26 @@ TSIGContext_verify(s_TSIGContext* self, PyObject* args) {
 
     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 {

+ 18 - 2
src/lib/dns/tests/tsig_unittest.cc

@@ -218,11 +218,17 @@ void
 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) {
@@ -231,6 +237,9 @@ 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) {
@@ -354,10 +363,17 @@ TEST_F(TSIGTest, verifyBadData) {
                                   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
@@ -806,7 +822,7 @@ TEST_F(TSIGTest, nosigThenValidate) {
         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");

+ 17 - 1
src/lib/dns/tsig.cc

@@ -61,7 +61,8 @@ struct TSIGContext::TSIGContextImpl {
     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
@@ -152,6 +153,10 @@ struct TSIGContext::TSIGContextImpl {
     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
@@ -433,6 +438,9 @@ TSIGContext::verify(const TSIGRecord* const record, const void* const data,
         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.
@@ -520,5 +528,13 @@ TSIGContext::verify(const TSIGRecord* const record, const void* const data,
     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