tsig.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. // Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <sys/time.h>
  15. #include <stdint.h>
  16. #include <cassert>
  17. #include <vector>
  18. #include <boost/shared_ptr.hpp>
  19. #include <exceptions/exceptions.h>
  20. #include <util/buffer.h>
  21. #include <util/time_utilities.h>
  22. #include <dns/rdataclass.h>
  23. #include <dns/rrclass.h>
  24. #include <dns/tsig.h>
  25. #include <dns/tsigerror.h>
  26. #include <dns/tsigkey.h>
  27. #include <cryptolink/cryptolink.h>
  28. #include <cryptolink/crypto_hmac.h>
  29. using namespace std;
  30. using namespace isc::util;
  31. using namespace isc::cryptolink;
  32. using namespace isc::dns::rdata;
  33. namespace isc {
  34. namespace dns {
  35. namespace {
  36. typedef boost::shared_ptr<HMAC> HMACPtr;
  37. // TSIG uses 48-bit unsigned integer to represent time signed.
  38. // Since gettimeWrapper() returns a 64-bit *signed* integer, we
  39. // make sure it's stored in an unsigned 64-bit integer variable and
  40. // represents a value in the expected range. (In reality, however,
  41. // gettimeWrapper() will return a positive integer that will fit
  42. // in 48 bits)
  43. uint64_t
  44. getTSIGTime() {
  45. return (detail::gettimeWrapper() & 0x0000ffffffffffffULL);
  46. }
  47. }
  48. struct TSIGContext::TSIGContextImpl {
  49. TSIGContextImpl(const TSIGKey& key) :
  50. state_(INIT), key_(key), error_(Rcode::NOERROR()),
  51. previous_timesigned_(0)
  52. {}
  53. // This helper method is used from verify(). It's expected to be called
  54. // just before verify() returns. It updates internal state based on
  55. // the verification result and return the TSIGError to be returned to
  56. // the caller of verify(), so that verify() can call this method within
  57. // its 'return' statement.
  58. TSIGError postVerifyUpdate(TSIGError error, const void* digest,
  59. uint16_t digest_len)
  60. {
  61. if (state_ == INIT) {
  62. state_ = RECEIVED_REQUEST;
  63. } else if (state_ == SENT_REQUEST && error == TSIGError::NOERROR()) {
  64. state_ = VERIFIED_RESPONSE;
  65. }
  66. if (digest != NULL) {
  67. previous_digest_.assign(static_cast<const uint8_t*>(digest),
  68. static_cast<const uint8_t*>(digest) +
  69. digest_len);
  70. }
  71. error_ = error;
  72. return (error);
  73. }
  74. // The following three are helper methods to compute the digest for
  75. // TSIG sign/verify in order to unify the common code logic for sign()
  76. // and verify() and to keep these callers concise.
  77. // These methods take an HMAC object, which will be updated with the
  78. // calculated digest.
  79. // Note: All methods construct a local OutputBuffer as a work space with a
  80. // fixed initial buffer size to avoid intermediate buffer extension.
  81. // This should be efficient enough, especially for fundamentally expensive
  82. // operation like cryptographic sign/verify, but if the creation of the
  83. // buffer in each helper method is still identified to be a severe
  84. // performance bottleneck, we could have this class a buffer as a member
  85. // variable and reuse it throughout the object's lifetime. Right now,
  86. // we prefer keeping the scope for local things as small as possible.
  87. void digestPreviousMAC(HMACPtr hmac) const;
  88. void digestTSIGVariables(HMACPtr hmac, uint16_t rrclass, uint32_t rrttl,
  89. uint64_t time_signed, uint16_t fudge,
  90. uint16_t error, uint16_t otherlen,
  91. const void* otherdata,
  92. bool time_variables_only) const;
  93. void digestDNSMessage(HMACPtr hmac, uint16_t qid, const void* data,
  94. size_t data_len) const;
  95. State state_;
  96. const TSIGKey key_;
  97. vector<uint8_t> previous_digest_;
  98. TSIGError error_;
  99. uint64_t previous_timesigned_; // only meaningful for response with BADTIME
  100. };
  101. void
  102. TSIGContext::TSIGContextImpl::digestPreviousMAC(HMACPtr hmac) const {
  103. // We should have ensured the digest size fits 16 bits within this class
  104. // implementation.
  105. assert(previous_digest_.size() <= 0xffff);
  106. OutputBuffer buffer(sizeof(uint16_t) + previous_digest_.size());
  107. const uint16_t previous_digest_len(previous_digest_.size());
  108. buffer.writeUint16(previous_digest_len);
  109. if (previous_digest_len != 0) {
  110. buffer.writeData(&previous_digest_[0], previous_digest_len);
  111. }
  112. hmac->update(buffer.getData(), buffer.getLength());
  113. }
  114. void
  115. TSIGContext::TSIGContextImpl::digestTSIGVariables(
  116. HMACPtr hmac, uint16_t rrclass, uint32_t rrttl, uint64_t time_signed,
  117. uint16_t fudge, uint16_t error, uint16_t otherlen, const void* otherdata,
  118. bool time_variables_only) const
  119. {
  120. // It's bit complicated, but we can still predict the necessary size of
  121. // the data to be digested. So we precompute it to avoid possible
  122. // reallocation inside OutputBuffer (not absolutely necessary, but this
  123. // is a bit more efficient)
  124. size_t data_size = 8;
  125. if (!time_variables_only) {
  126. data_size += 10 + key_.getKeyName().getLength() +
  127. key_.getAlgorithmName().getLength();
  128. }
  129. OutputBuffer buffer(data_size);
  130. if (!time_variables_only) {
  131. key_.getKeyName().toWire(buffer);
  132. buffer.writeUint16(rrclass);
  133. buffer.writeUint32(rrttl);
  134. key_.getAlgorithmName().toWire(buffer);
  135. }
  136. buffer.writeUint16(time_signed >> 32);
  137. buffer.writeUint32(time_signed & 0xffffffff);
  138. buffer.writeUint16(fudge);
  139. if (!time_variables_only) {
  140. buffer.writeUint16(error);
  141. buffer.writeUint16(otherlen);
  142. }
  143. hmac->update(buffer.getData(), buffer.getLength());
  144. if (!time_variables_only && otherlen > 0) {
  145. hmac->update(otherdata, otherlen);
  146. }
  147. }
  148. // In digestDNSMessage, we exploit some minimum knowledge of DNS message
  149. // format:
  150. // - the header section has a fixed length of 12 octets (MESSAGE_HEADER_LEN)
  151. // - the offset in the header section to the ID field is 0
  152. // - the offset in the header section to the ARCOUNT field is 10 (and the field
  153. // length is 2 octets)
  154. // We could construct a separate Message object from the given data, adjust
  155. // fields via the Message interfaces and then render it back to a separate
  156. // buffer, but that would be overkilling. The DNS message header has a
  157. // fixed length and necessary modifications are quite straightforward, so
  158. // we do the job using lower level interfaces.
  159. namespace {
  160. const size_t MESSAGE_HEADER_LEN = 12;
  161. }
  162. void
  163. TSIGContext::TSIGContextImpl::digestDNSMessage(HMACPtr hmac,
  164. uint16_t qid, const void* data,
  165. size_t data_len) const
  166. {
  167. OutputBuffer buffer(MESSAGE_HEADER_LEN);
  168. const uint8_t* msgptr = static_cast<const uint8_t*>(data);
  169. // Install the original ID
  170. buffer.writeUint16(qid);
  171. msgptr += sizeof(uint16_t);
  172. // Copy the rest of the header except the ARCOUNT field.
  173. buffer.writeData(msgptr, 8);
  174. msgptr += 8;
  175. // Install the adjusted ARCOUNT (we don't care even if the value is bogus
  176. // and it underflows; it would simply result in verification failure)
  177. buffer.writeUint16(InputBuffer(msgptr, sizeof(uint16_t)).readUint16() - 1);
  178. msgptr += 2;
  179. // Digest the header and the rest of the DNS message
  180. hmac->update(buffer.getData(), buffer.getLength());
  181. hmac->update(msgptr, data_len - MESSAGE_HEADER_LEN);
  182. }
  183. TSIGContext::TSIGContext(const TSIGKey& key) : impl_(new TSIGContextImpl(key))
  184. {
  185. }
  186. TSIGContext::TSIGContext(const Name& key_name, const Name& algorithm_name,
  187. const TSIGKeyRing& keyring) : impl_(NULL)
  188. {
  189. const TSIGKeyRing::FindResult result(keyring.find(key_name,
  190. algorithm_name));
  191. if (result.code == TSIGKeyRing::NOTFOUND) {
  192. // If not key is found, create a dummy key with the specified key
  193. // parameters and empty secret. In the common scenario this will
  194. // be used in subsequent response with a TSIG indicating a BADKEY
  195. // error.
  196. impl_ = new TSIGContextImpl(TSIGKey(key_name, algorithm_name,
  197. NULL, 0));
  198. impl_->error_ = TSIGError::BAD_KEY();
  199. } else {
  200. impl_ = new TSIGContextImpl(*result.key);
  201. }
  202. }
  203. TSIGContext::~TSIGContext() {
  204. delete impl_;
  205. }
  206. TSIGContext::State
  207. TSIGContext::getState() const {
  208. return (impl_->state_);
  209. }
  210. TSIGError
  211. TSIGContext::getError() const {
  212. return (impl_->error_);
  213. }
  214. ConstTSIGRecordPtr
  215. TSIGContext::sign(const uint16_t qid, const void* const data,
  216. const size_t data_len)
  217. {
  218. if (impl_->state_ == VERIFIED_RESPONSE) {
  219. isc_throw(TSIGContextError,
  220. "TSIG sign attempt after verifying a response");
  221. }
  222. if (data == NULL || data_len == 0) {
  223. isc_throw(InvalidParameter, "TSIG sign error: empty data is given");
  224. }
  225. TSIGError error(TSIGError::NOERROR());
  226. const uint64_t now = getTSIGTime();
  227. // For responses adjust the error code.
  228. if (impl_->state_ == RECEIVED_REQUEST) {
  229. error = impl_->error_;
  230. }
  231. // For errors related to key or MAC, return an unsigned response as
  232. // specified in Section 4.3 of RFC2845.
  233. if (error == TSIGError::BAD_SIG() || error == TSIGError::BAD_KEY()) {
  234. ConstTSIGRecordPtr tsig(new TSIGRecord(
  235. impl_->key_.getKeyName(),
  236. any::TSIG(impl_->key_.getAlgorithmName(),
  237. now, DEFAULT_FUDGE, 0, NULL,
  238. qid, error.getCode(), 0, NULL)));
  239. impl_->previous_digest_.clear();
  240. impl_->state_ = SENT_RESPONSE;
  241. return (tsig);
  242. }
  243. HMACPtr hmac(CryptoLink::getCryptoLink().createHMAC(
  244. impl_->key_.getSecret(),
  245. impl_->key_.getSecretLength(),
  246. impl_->key_.getAlgorithm()),
  247. deleteHMAC);
  248. // If the context has previous MAC (either the Request MAC or its own
  249. // previous MAC), digest it.
  250. if (impl_->state_ != INIT) {
  251. impl_->digestPreviousMAC(hmac);
  252. }
  253. // Digest the message (without TSIG)
  254. hmac->update(data, data_len);
  255. // Digest TSIG variables.
  256. // First, prepare some non constant variables.
  257. const uint64_t time_signed = (error == TSIGError::BAD_TIME()) ?
  258. impl_->previous_timesigned_ : now;
  259. // For BADTIME error, we include 6 bytes of other data.
  260. // (6 bytes = size of time signed value)
  261. const uint16_t otherlen = (error == TSIGError::BAD_TIME()) ? 6 : 0;
  262. OutputBuffer otherdatabuf(otherlen);
  263. if (error == TSIGError::BAD_TIME()) {
  264. otherdatabuf.writeUint16(now >> 32);
  265. otherdatabuf.writeUint32(now & 0xffffffff);
  266. }
  267. const void* const otherdata =
  268. (otherlen == 0) ? NULL : otherdatabuf.getData();
  269. // Then calculate the digest. If state_ is SENT_RESPONSE we are sending
  270. // a continued message in the same TCP stream so skip digesting
  271. // variables except for time related variables (RFC2845 4.4).
  272. impl_->digestTSIGVariables(hmac, TSIGRecord::getClass().getCode(),
  273. TSIGRecord::TSIG_TTL, time_signed,
  274. DEFAULT_FUDGE, error.getCode(),
  275. otherlen, otherdata,
  276. impl_->state_ == SENT_RESPONSE);
  277. // Get the final digest, update internal state, then finish.
  278. vector<uint8_t> digest = hmac->sign();
  279. assert(digest.size() <= 0xffff); // cryptolink API should have ensured it.
  280. ConstTSIGRecordPtr tsig(new TSIGRecord(
  281. impl_->key_.getKeyName(),
  282. any::TSIG(impl_->key_.getAlgorithmName(),
  283. time_signed, DEFAULT_FUDGE,
  284. digest.size(), &digest[0],
  285. qid, error.getCode(), otherlen,
  286. otherdata)));
  287. // Exception free from now on.
  288. impl_->previous_digest_.swap(digest);
  289. impl_->state_ = (impl_->state_ == INIT) ? SENT_REQUEST : SENT_RESPONSE;
  290. return (tsig);
  291. }
  292. TSIGError
  293. TSIGContext::verify(const TSIGRecord* const record, const void* const data,
  294. const size_t data_len)
  295. {
  296. if (impl_->state_ == SENT_RESPONSE) {
  297. isc_throw(TSIGContextError,
  298. "TSIG verify attempt after sending a response");
  299. }
  300. // This case happens when we sent a signed request and have received an
  301. // unsigned response. According to RFC2845 Section 4.6 this case should be
  302. // considered a "format error" (although the specific error code
  303. // wouldn't matter much for the caller).
  304. if (record == NULL) {
  305. return (impl_->postVerifyUpdate(TSIGError::FORMERR(), NULL, 0));
  306. }
  307. const any::TSIG& tsig_rdata = record->getRdata();
  308. // Reject some obviously invalid data
  309. if (data_len < MESSAGE_HEADER_LEN + record->getLength()) {
  310. isc_throw(InvalidParameter,
  311. "TSIG verify: data length is invalid: " << data_len);
  312. }
  313. if (data == NULL) {
  314. isc_throw(InvalidParameter, "TSIG verify: empty data is invalid");
  315. }
  316. // Check key: whether we first verify it with a known key or we verify
  317. // it using the consistent key in the context. If the check fails we are
  318. // done with BADKEY.
  319. if (impl_->state_ == INIT && impl_->error_ == TSIGError::BAD_KEY()) {
  320. return (impl_->postVerifyUpdate(TSIGError::BAD_KEY(), NULL, 0));
  321. }
  322. if (impl_->key_.getKeyName() != record->getName() ||
  323. impl_->key_.getAlgorithmName() != tsig_rdata.getAlgorithm()) {
  324. return (impl_->postVerifyUpdate(TSIGError::BAD_KEY(), NULL, 0));
  325. }
  326. // Check time: the current time must be in the range of
  327. // [time signed - fudge, time signed + fudge]. Otherwise verification
  328. // fails with BADTIME. (RFC2845 Section 4.6.2)
  329. // Note: for simplicity we don't explicitly catch the case of too small
  330. // current time causing underflow. With the fact that fudge is quite
  331. // small and (for now) non configurable, it shouldn't be a real concern
  332. // in practice.
  333. const uint64_t now = getTSIGTime();
  334. if (tsig_rdata.getTimeSigned() + DEFAULT_FUDGE < now ||
  335. tsig_rdata.getTimeSigned() - DEFAULT_FUDGE > now) {
  336. const void* digest = NULL;
  337. size_t digest_len = 0;
  338. if (impl_->state_ == INIT) {
  339. digest = tsig_rdata.getMAC();
  340. digest_len = tsig_rdata.getMACSize();
  341. impl_->previous_timesigned_ = tsig_rdata.getTimeSigned();
  342. }
  343. return (impl_->postVerifyUpdate(TSIGError::BAD_TIME(), digest,
  344. digest_len));
  345. }
  346. // TODO: signature length check based on RFC4635
  347. // (Right now we enforce the standard signature length in libcryptolink)
  348. // Handling empty MAC. While RFC2845 doesn't explicitly prohibit other
  349. // cases, it can only reasonably happen in a response with BADSIG or
  350. // BADKEY. We reject other cases as if it were BADSIG to avoid unexpected
  351. // acceptance of a bogus signature. This behavior follows the BIND 9
  352. // implementation.
  353. if (tsig_rdata.getMACSize() == 0) {
  354. TSIGError error = TSIGError(tsig_rdata.getError());
  355. if (error != TSIGError::BAD_SIG() && error != TSIGError::BAD_KEY()) {
  356. error = TSIGError::BAD_SIG();
  357. }
  358. return (impl_->postVerifyUpdate(error, NULL, 0));
  359. }
  360. HMACPtr hmac(CryptoLink::getCryptoLink().createHMAC(
  361. impl_->key_.getSecret(),
  362. impl_->key_.getSecretLength(),
  363. impl_->key_.getAlgorithm()),
  364. deleteHMAC);
  365. // If the context has previous MAC (either the Request MAC or its own
  366. // previous MAC), digest it.
  367. if (impl_->state_ != INIT) {
  368. impl_->digestPreviousMAC(hmac);
  369. }
  370. //
  371. // Digest DNS message (excluding the trailing TSIG RR and adjusting the
  372. // QID and ARCOUNT header fields)
  373. //
  374. impl_->digestDNSMessage(hmac, tsig_rdata.getOriginalID(),
  375. data, data_len - record->getLength());
  376. // Digest TSIG variables. If state_ is VERIFIED_RESPONSE, it's a
  377. // continuation of the same TCP stream and skip digesting them except
  378. // for time related variables (RFC2845 4.4).
  379. // Note: we use the constant values for RR class and TTL specified
  380. // in RFC2845, not received values (we reject other values in constructing
  381. // the TSIGRecord).
  382. impl_->digestTSIGVariables(hmac, TSIGRecord::getClass().getCode(),
  383. TSIGRecord::TSIG_TTL,
  384. tsig_rdata.getTimeSigned(),
  385. tsig_rdata.getFudge(), tsig_rdata.getError(),
  386. tsig_rdata.getOtherLen(),
  387. tsig_rdata.getOtherData(),
  388. impl_->state_ == VERIFIED_RESPONSE);
  389. // Verify the digest with the received signature.
  390. if (hmac->verify(tsig_rdata.getMAC(), tsig_rdata.getMACSize())) {
  391. return (impl_->postVerifyUpdate(TSIGError::NOERROR(),
  392. tsig_rdata.getMAC(),
  393. tsig_rdata.getMACSize()));
  394. }
  395. return (impl_->postVerifyUpdate(TSIGError::BAD_SIG(), NULL, 0));
  396. }
  397. } // namespace dns
  398. } // namespace isc