Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Kea
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
Operations
Operations
Incidents
Packages & Registries
Packages & Registries
Container Registry
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Sebastian Schrader
Kea
Commits
f4ad7554
Commit
f4ad7554
authored
Apr 26, 2011
by
JINMEI Tatuya
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[trac812] ensure TSIGContext::sign() provides strong exception guarantee
added new test for that. with some other cleanups.
parent
dabf2052
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
223 additions
and
56 deletions
+223
-56
src/lib/dns/tests/tsig_unittest.cc
src/lib/dns/tests/tsig_unittest.cc
+36
-0
src/lib/dns/tsig.cc
src/lib/dns/tsig.cc
+13
-38
src/lib/dns/tsig.h
src/lib/dns/tsig.h
+15
-18
src/lib/dns/tsigerror.cc
src/lib/dns/tsigerror.cc
+31
-0
src/lib/util/unittests/Makefile.am
src/lib/util/unittests/Makefile.am
+1
-0
src/lib/util/unittests/newhook.cc
src/lib/util/unittests/newhook.cc
+51
-0
src/lib/util/unittests/newhook.h
src/lib/util/unittests/newhook.h
+76
-0
No files found.
src/lib/dns/tests/tsig_unittest.cc
View file @
f4ad7554
...
...
@@ -14,6 +14,7 @@
#include <time.h>
#include <string>
#include <stdexcept>
#include <vector>
#include <boost/scoped_ptr.hpp>
...
...
@@ -24,6 +25,7 @@
#include <util/buffer.h>
#include <util/encode/base64.h>
#include <util/unittests/newhook.h>
#include <dns/message.h>
#include <dns/messagerenderer.h>
...
...
@@ -279,6 +281,40 @@ TEST_F(TSIGTest, signBadData) {
EXPECT_THROW
(
tsig_ctx
->
sign
(
0
,
&
dummy_data
,
0
),
InvalidParameter
);
}
#ifdef ENABLE_CUSTOM_OPERATOR_NEW
// We enable this test only when we enable custom new/delete at build time
// We could enable/disable the test runtime using the gtest filter, but
// we'd basically like to minimize the number of disabled tests (they
// should generally be considered tests that temporarily fail and should
// be fixed).
TEST_F
(
TSIGTest
,
signExceptionSafety
)
{
// Check sign() provides the strong exception guarantee for the simpler
// case (with a key error and empty MAC). The general case is more
// complicated and involves more memory allocation, so the test result
// won't be reliable.
tsig_verify_ctx
->
verifyTentative
(
createMessageAndSign
(
qid
,
test_name
,
tsig_ctx
.
get
()),
TSIGError
::
BAD_KEY
());
// At this point the state should be changed to "CHECKED"
ASSERT_EQ
(
TSIGContext
::
CHECKED
,
tsig_verify_ctx
->
getState
());
try
{
int
dummydata
;
isc
::
util
::
unittests
::
force_throw_on_new
=
true
;
isc
::
util
::
unittests
::
throw_size_on_new
=
sizeof
(
TSIGRecord
);
tsig_verify_ctx
->
sign
(
0
,
&
dummydata
,
sizeof
(
dummydata
));
isc
::
util
::
unittests
::
force_throw_on_new
=
false
;
ASSERT_FALSE
(
true
)
<<
"Expected throw on new, but it didn't happen"
;
}
catch
(
const
std
::
bad_alloc
&
)
{
isc
::
util
::
unittests
::
force_throw_on_new
=
false
;
// sign() threw, so the state should still be "CHECKED".
EXPECT_EQ
(
TSIGContext
::
CHECKED
,
tsig_verify_ctx
->
getState
());
}
isc
::
util
::
unittests
::
force_throw_on_new
=
false
;
}
#endif // ENABLE_CUSTOM_OPERATOR_NEW
// Same test as "sign" but use a different algorithm just to confirm we don't
// naively hardcode constants specific to a particular algorithm.
// Test data generated by
...
...
src/lib/dns/tsig.cc
View file @
f4ad7554
...
...
@@ -19,7 +19,6 @@
#include <cassert> // for the tentative verifyTentative()
#include <vector>
#include <boost/lexical_cast.hpp>
#include <boost/shared_ptr.hpp>
#include <exceptions/exceptions.h>
...
...
@@ -67,6 +66,12 @@ gettimeofdayWrapper() {
namespace
{
typedef
boost
::
shared_ptr
<
HMAC
>
HMACPtr
;
}
const
RRClass
&
TSIGRecord
::
getClass
()
{
return
(
RRClass
::
ANY
());
}
struct
TSIGContext
::
TSIGContextImpl
{
TSIGContextImpl
(
const
TSIGKey
&
key
)
:
...
...
@@ -79,12 +84,6 @@ struct TSIGContext::TSIGContextImpl {
TSIGError
error_
;
uint64_t
previous_timesigned_
;
// only meaningful for response with BADTIME
};
}
const
RRClass
&
TSIGRecord
::
getClass
()
{
return
(
RRClass
::
ANY
());
}
TSIGContext
::
TSIGContext
(
const
TSIGKey
&
key
)
:
impl_
(
new
TSIGContextImpl
(
key
))
{
...
...
@@ -123,12 +122,12 @@ TSIGContext::sign(const uint16_t qid, const void* const data,
// For errors related to key or MAC, return an unsigned response as
// specified in Section 4.3 of RFC2845.
if
(
error
==
TSIGError
::
BAD_SIG
()
||
error
==
TSIGError
::
BAD_KEY
())
{
impl_
->
previous_digest_
.
clear
();
impl_
->
state_
=
SIGNED
;
ConstTSIGRecordPtr
tsig
(
new
TSIGRecord
(
any
::
TSIG
(
impl_
->
key_
.
getAlgorithmName
(),
now
,
DEFAULT_FUDGE
,
NULL
,
0
,
qid
,
error
.
getCode
(),
0
,
NULL
)));
impl_
->
previous_digest_
.
clear
();
impl_
->
state_
=
SIGNED
;
return
(
tsig
);
}
...
...
@@ -191,16 +190,17 @@ TSIGContext::sign(const uint16_t qid, const void* const data,
const
uint16_t
otherlen
=
variables
.
getLength
();
// Get the final digest, update internal state, then finish.
impl_
->
previous_digest_
=
hmac
->
sign
();
impl_
->
state_
=
SIGNED
;
vector
<
uint8_t
>
digest
=
hmac
->
sign
();
ConstTSIGRecordPtr
tsig
(
new
TSIGRecord
(
any
::
TSIG
(
impl_
->
key_
.
getAlgorithmName
(),
time_signed
,
DEFAULT_FUDGE
,
impl_
->
previous_digest_
.
size
(),
&
impl_
->
previous_digest_
[
0
],
digest
.
size
(),
&
digest
[
0
],
qid
,
error
.
getCode
(),
otherlen
,
otherlen
==
0
?
NULL
:
variables
.
getData
())));
// Exception free from now on.
impl_
->
previous_digest_
.
swap
(
digest
);
impl_
->
state_
=
SIGNED
;
return
(
tsig
);
}
...
...
@@ -222,30 +222,5 @@ TSIGContext::verifyTentative(ConstTSIGRecordPtr tsig, TSIGError error) {
impl_
->
state_
=
CHECKED
;
}
namespace
{
const
char
*
const
tsigerror_text
[]
=
{
"BADSIG"
,
"BADKEY"
,
"BADTIME"
};
}
TSIGError
::
TSIGError
(
Rcode
rcode
)
:
code_
(
rcode
.
getCode
())
{
if
(
code_
>
MAX_RCODE_FOR_TSIGERROR
)
{
isc_throw
(
OutOfRange
,
"Invalid RCODE for TSIG Error: "
<<
rcode
);
}
}
string
TSIGError
::
toText
()
const
{
if
(
code_
<=
MAX_RCODE_FOR_TSIGERROR
)
{
return
(
Rcode
(
code_
).
toText
());
}
else
if
(
code_
<=
BAD_TIME_CODE
)
{
return
(
tsigerror_text
[
code_
-
(
MAX_RCODE_FOR_TSIGERROR
+
1
)]);
}
else
{
return
(
boost
::
lexical_cast
<
string
>
(
code_
));
}
}
}
// namespace dns
}
// namespace isc
src/lib/dns/tsig.h
View file @
f4ad7554
...
...
@@ -106,22 +106,22 @@ typedef boost::shared_ptr<const TSIGRecord> ConstTSIGRecordPtr;
/// This class supports all these cases.
///
/// A \c TSIGContext object is generally constructed with a TSIG key to be
/// used for the session, and keeps tr
u
ck of various kinds of session specific
/// used for the session, and keeps tr
a
ck of various kinds of session specific
/// information, such as the original digest while waiting for a response or
/// verification error information that is to be used for a subsequent
/// response.
///
/// This class has two main methods, \c sign() and \c verify().
/// The \c sign() method signs given data (which is supposed to be a complete
/// DNS message
to be signed) using the TSIG key and other related information
/// associated with the \c TSIGContext object.
/// DNS message
without the TSIG itself) using the TSIG key and other
///
related information
associated with the \c TSIGContext object.
/// The \c verify() method verifies a given DNS message that contains a TSIG
/// RR using the key and other internal information.
///
/// In general, a DNS client that wants to send a signed query will construct
/// a \c TSIGContext object with the TSIG key that the client is intending to
/// use, and sign the query with the context. The client
keeps the context,
///
and verifies
the response with it.
/// use, and sign the query with the context. The client
will keeps the
///
context, and verify
the response with it.
///
/// On the other hand, a DNS server will construct a \c TSIGContext object
/// with the information of the TSIG RR included in a query with a set of
...
...
@@ -134,7 +134,7 @@ typedef boost::shared_ptr<const TSIGRecord> ConstTSIGRecordPtr;
///
/// When multiple messages belong to the same TSIG session, either side
/// (signer or verifier) will keep using the same context. It records
/// the latest session state (such as the previous digest) so that
continues
/// the latest session state (such as the previous digest) so that
repeated
/// calls to \c sign() or \c verify() work correctly in terms of the TSIG
/// protocol.
///
...
...
@@ -204,7 +204,8 @@ public:
/// generally expected to be a complete, wire-format DNS message
/// that doesn't contain a TSIG RR, based on the TSIG key and
/// other context information of \c TSIGContext, and returns a
/// result in the form of an \c rdata::any::TSIG object.
/// result in the form of a (pointer object pointing to)
/// \c TSIGRecord object.
///
/// The caller of this method will use the returned value to render a
/// complete TSIG RR into the message that has been signed so that it
...
...
@@ -220,9 +221,9 @@ public:
/// description), and doesn't inspect it in any way. For example, it
/// doesn't check whether the data length is sane for a valid DNS message.
/// This is also the reason why this method takes the \c qid parameter,
/// which will be used as the original ID of the resulting
\c TSIG object
///
(RR), even though this value should be stored in the first two octets
/// (in wire format) of the given data.
/// which will be used as the original ID of the resulting
///
\c TSIGRecordx object, even though this value should be stored in the
///
first two octets
(in wire format) of the given data.
///
/// \note This method still checks and rejects empty data (\c NULL pointer
/// data or the specified data length is 0) in order to avoid catastrophic
...
...
@@ -230,13 +231,9 @@ public:
/// for HMAC computation, but obviously it doesn't make sense for a DNS
/// message.
///
/// This method can throw exceptions (see the list), but does not provide
/// the strong exception guarantee. That is, if an exception is thrown,
/// the internal state of the \c TSIGContext object can be changed, in
/// which case it's unlikely that the context can be used for (re)signing
/// or (re)verifying subsequent messages any more. If the caller wants
/// to catch the exception and try to recover from it, it must drop the
/// TSIG session and start a new session with a new context.
/// This method provides the strong exception guarantee; unless the method
/// returns (without an exception being thrown), the internal state of
/// the \c TSIGContext won't be modified.
///
/// \exception InvalidParameter \c data is NULL or \c data_len is 0
/// \exception cryptolink::LibraryError Some unexpected error in the
...
...
@@ -244,7 +241,7 @@ public:
/// \exception std::bad_alloc Temporary resource allocation failure
///
/// \param qid The QID to be as the value of the original ID field of
/// the resulting TSIG
/// the resulting TSIG
record
/// \param data Points to the wire-format data to be signed
/// \param data_len The length of \c data in bytes
///
...
...
src/lib/dns/tsigerror.cc
View file @
f4ad7554
...
...
@@ -13,11 +13,42 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <ostream>
#include <string>
#include <boost/lexical_cast.hpp>
#include <exceptions/exceptions.h>
#include <dns/rcode.h>
#include <dns/tsigerror.h>
namespace
isc
{
namespace
dns
{
namespace
{
const
char
*
const
tsigerror_text
[]
=
{
"BADSIG"
,
"BADKEY"
,
"BADTIME"
};
}
TSIGError
::
TSIGError
(
Rcode
rcode
)
:
code_
(
rcode
.
getCode
())
{
if
(
code_
>
MAX_RCODE_FOR_TSIGERROR
)
{
isc_throw
(
OutOfRange
,
"Invalid RCODE for TSIG Error: "
<<
rcode
);
}
}
std
::
string
TSIGError
::
toText
()
const
{
if
(
code_
<=
MAX_RCODE_FOR_TSIGERROR
)
{
return
(
Rcode
(
code_
).
toText
());
}
else
if
(
code_
<=
BAD_TIME_CODE
)
{
return
(
tsigerror_text
[
code_
-
(
MAX_RCODE_FOR_TSIGERROR
+
1
)]);
}
else
{
return
(
boost
::
lexical_cast
<
std
::
string
>
(
code_
));
}
}
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
const
TSIGError
&
error
)
{
return
(
os
<<
error
.
toText
());
...
...
src/lib/util/unittests/Makefile.am
View file @
f4ad7554
...
...
@@ -3,5 +3,6 @@ AM_CXXFLAGS = $(B10_CXXFLAGS)
lib_LTLIBRARIES
=
libutil_unittests.la
libutil_unittests_la_SOURCES
=
fork.h fork.cc
libutil_unittests_la_SOURCES
+=
newhook.h newhook.cc
CLEANFILES
=
*
.gcno
*
.gcda
src/lib/util/unittests/newhook.cc
0 → 100644
View file @
f4ad7554
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <stdlib.h>
#include <new>
#include <stdexcept>
#include "newhook.h"
#ifdef ENABLE_CUSTOM_OPERATOR_NEW
void
*
operator
new
(
size_t
size
)
throw
(
std
::
bad_alloc
)
{
if
(
isc
::
util
::
unittests
::
force_throw_on_new
&&
size
==
isc
::
util
::
unittests
::
throw_size_on_new
)
{
throw
std
::
bad_alloc
();
}
void
*
p
=
malloc
(
size
);
if
(
p
==
NULL
)
{
throw
std
::
bad_alloc
();
}
return
(
p
);
}
void
operator
delete
(
void
*
p
)
throw
()
{
if
(
p
!=
NULL
)
{
free
(
p
);
}
}
#endif
namespace
isc
{
namespace
util
{
namespace
unittests
{
bool
force_throw_on_new
=
false
;
size_t
throw_size_on_new
=
0
;
}
}
}
src/lib/util/unittests/newhook.h
0 → 100644
View file @
f4ad7554
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef __UTIL_UNITTESTS_NEWHOOK_H
#define __UTIL_UNITTESTS_NEWHOOK_H 1
/**
* @file newhook.h
* @short Enable the use of special operator new that throws for testing.
*
* This small utility allows a test case to force the global operator new
* to throw for a given size to test a case where memory allocation fails
* (which normally doesn't happen). To enable the feature, everything must
* be built with defining ENABLE_CUSTOM_OPERATOR_NEW beforehand, and set
* \c force_throw_on_new to \c true and \c throw_size_on_new to the size
* of data that should trigger the exception, immediately before starting
* the specific test that needs the exception.
*
* Example:
* \code #include <util/unittests/newhook.h>
* ...
* TEST(SomeTest, newException) {
* isc::util::unittests::force_throw_on_new = true;
* isc::util::unittests::throw_size_on_new = sizeof(Foo);
* try {
* // this will do 'new Foo()' internally and should throw
* createFoo();
* isc::util::unittests::force_throw_on_new = false;
* ASSERT_FALSE(true) << "Expected throw on new";
* } catch (const std::bad_alloc&) {
* isc::util::unittests::force_throw_on_new = false;
* // do some integrity check, etc, if necessary
* }
* } \endcode
*
* Replacing the global operator new (and delete) is a dangerous technique,
* and triggering an exception solely based on the allocation size is not
* reliable, so this feature is disabled by default two-fold: The
* ENABLE_CUSTOM_OPERATOR_NEW build time variable, and run-time
* \c force_throw_on_new.
*/
namespace
isc
{
namespace
util
{
namespace
unittests
{
/// Switch to enable the use of special operator new
///
/// This is set to \c false by default.
extern
bool
force_throw_on_new
;
/// The allocation size that triggers an exception in the special operator new
///
/// The default value is 0. The value of this variable has no meaning
/// unless the use of the special operator is enabled at build time and
/// via \c force_throw_on_new.
extern
size_t
throw_size_on_new
;
}
}
}
#endif // __UTIL_UNITTESTS_NEWHOOK_H
// Local Variables:
// mode: c++
// End:
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment