tsig.cc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  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. TSIGError error = TSIGError::NOERROR()) :
  51. state_(INIT), key_(key), error_(error),
  52. previous_timesigned_(0), digest_len_(0)
  53. {
  54. if (error == TSIGError::NOERROR()) {
  55. // In normal (NOERROR) case, the key should be valid, and we
  56. // should be able to pre-create a corresponding HMAC object,
  57. // which will be likely to be used for sign or verify later.
  58. // We do this in the constructor so that we can know the expected
  59. // digest length in advance. The creation should normally succeed,
  60. // but the key information could be still broken, which could
  61. // trigger an exception inside the cryptolink module. We ignore
  62. // it at this moment; a subsequent sign/verify operation will try
  63. // to create the HMAC, which would also fail.
  64. try {
  65. hmac_.reset(CryptoLink::getCryptoLink().createHMAC(
  66. key_.getSecret(), key_.getSecretLength(),
  67. key_.getAlgorithm()),
  68. deleteHMAC);
  69. } catch (const Exception&) {
  70. return;
  71. }
  72. digest_len_ = hmac_->getOutputLength();
  73. }
  74. }
  75. // This helper method is used from verify(). It's expected to be called
  76. // just before verify() returns. It updates internal state based on
  77. // the verification result and return the TSIGError to be returned to
  78. // the caller of verify(), so that verify() can call this method within
  79. // its 'return' statement.
  80. TSIGError postVerifyUpdate(TSIGError error, const void* digest,
  81. uint16_t digest_len)
  82. {
  83. if (state_ == INIT) {
  84. state_ = RECEIVED_REQUEST;
  85. } else if (state_ == SENT_REQUEST && error == TSIGError::NOERROR()) {
  86. state_ = VERIFIED_RESPONSE;
  87. }
  88. if (digest != NULL) {
  89. previous_digest_.assign(static_cast<const uint8_t*>(digest),
  90. static_cast<const uint8_t*>(digest) +
  91. digest_len);
  92. }
  93. error_ = error;
  94. return (error);
  95. }
  96. // A shortcut method to create an HMAC object for sign/verify. If one
  97. // has been successfully created in the constructor, return it; otherwise
  98. // create a new one and return it. In the former case, the ownership is
  99. // transferred to the caller; the stored HMAC will be reset after the
  100. // call.
  101. HMACPtr createHMAC() {
  102. if (hmac_) {
  103. HMACPtr ret = HMACPtr();
  104. ret.swap(hmac_);
  105. return (ret);
  106. }
  107. return (HMACPtr(CryptoLink::getCryptoLink().createHMAC(
  108. key_.getSecret(), key_.getSecretLength(),
  109. key_.getAlgorithm()),
  110. deleteHMAC));
  111. }
  112. // The following three are helper methods to compute the digest for
  113. // TSIG sign/verify in order to unify the common code logic for sign()
  114. // and verify() and to keep these callers concise.
  115. // These methods take an HMAC object, which will be updated with the
  116. // calculated digest.
  117. // Note: All methods construct a local OutputBuffer as a work space with a
  118. // fixed initial buffer size to avoid intermediate buffer extension.
  119. // This should be efficient enough, especially for fundamentally expensive
  120. // operation like cryptographic sign/verify, but if the creation of the
  121. // buffer in each helper method is still identified to be a severe
  122. // performance bottleneck, we could have this class a buffer as a member
  123. // variable and reuse it throughout the object's lifetime. Right now,
  124. // we prefer keeping the scope for local things as small as possible.
  125. void digestPreviousMAC(HMACPtr hmac) const;
  126. void digestTSIGVariables(HMACPtr hmac, uint16_t rrclass, uint32_t rrttl,
  127. uint64_t time_signed, uint16_t fudge,
  128. uint16_t error, uint16_t otherlen,
  129. const void* otherdata,
  130. bool time_variables_only) const;
  131. void digestDNSMessage(HMACPtr hmac, uint16_t qid, const void* data,
  132. size_t data_len) const;
  133. State state_;
  134. const TSIGKey key_;
  135. vector<uint8_t> previous_digest_;
  136. TSIGError error_;
  137. uint64_t previous_timesigned_; // only meaningful for response with BADTIME
  138. size_t digest_len_;
  139. HMACPtr hmac_;
  140. };
  141. void
  142. TSIGContext::TSIGContextImpl::digestPreviousMAC(HMACPtr hmac) const {
  143. // We should have ensured the digest size fits 16 bits within this class
  144. // implementation.
  145. assert(previous_digest_.size() <= 0xffff);
  146. OutputBuffer buffer(sizeof(uint16_t) + previous_digest_.size());
  147. const uint16_t previous_digest_len(previous_digest_.size());
  148. buffer.writeUint16(previous_digest_len);
  149. if (previous_digest_len != 0) {
  150. buffer.writeData(&previous_digest_[0], previous_digest_len);
  151. }
  152. hmac->update(buffer.getData(), buffer.getLength());
  153. }
  154. void
  155. TSIGContext::TSIGContextImpl::digestTSIGVariables(
  156. HMACPtr hmac, uint16_t rrclass, uint32_t rrttl, uint64_t time_signed,
  157. uint16_t fudge, uint16_t error, uint16_t otherlen, const void* otherdata,
  158. bool time_variables_only) const
  159. {
  160. // It's bit complicated, but we can still predict the necessary size of
  161. // the data to be digested. So we precompute it to avoid possible
  162. // reallocation inside OutputBuffer (not absolutely necessary, but this
  163. // is a bit more efficient)
  164. size_t data_size = 8;
  165. if (!time_variables_only) {
  166. data_size += 10 + key_.getKeyName().getLength() +
  167. key_.getAlgorithmName().getLength();
  168. }
  169. OutputBuffer buffer(data_size);
  170. if (!time_variables_only) {
  171. key_.getKeyName().toWire(buffer);
  172. buffer.writeUint16(rrclass);
  173. buffer.writeUint32(rrttl);
  174. key_.getAlgorithmName().toWire(buffer);
  175. }
  176. buffer.writeUint16(time_signed >> 32);
  177. buffer.writeUint32(time_signed & 0xffffffff);
  178. buffer.writeUint16(fudge);
  179. if (!time_variables_only) {
  180. buffer.writeUint16(error);
  181. buffer.writeUint16(otherlen);
  182. }
  183. hmac->update(buffer.getData(), buffer.getLength());
  184. if (!time_variables_only && otherlen > 0) {
  185. hmac->update(otherdata, otherlen);
  186. }
  187. }
  188. // In digestDNSMessage, we exploit some minimum knowledge of DNS message
  189. // format:
  190. // - the header section has a fixed length of 12 octets (MESSAGE_HEADER_LEN)
  191. // - the offset in the header section to the ID field is 0
  192. // - the offset in the header section to the ARCOUNT field is 10 (and the field
  193. // length is 2 octets)
  194. // We could construct a separate Message object from the given data, adjust
  195. // fields via the Message interfaces and then render it back to a separate
  196. // buffer, but that would be overkilling. The DNS message header has a
  197. // fixed length and necessary modifications are quite straightforward, so
  198. // we do the job using lower level interfaces.
  199. namespace {
  200. const size_t MESSAGE_HEADER_LEN = 12;
  201. }
  202. void
  203. TSIGContext::TSIGContextImpl::digestDNSMessage(HMACPtr hmac,
  204. uint16_t qid, const void* data,
  205. size_t data_len) const
  206. {
  207. OutputBuffer buffer(MESSAGE_HEADER_LEN);
  208. const uint8_t* msgptr = static_cast<const uint8_t*>(data);
  209. // Install the original ID
  210. buffer.writeUint16(qid);
  211. msgptr += sizeof(uint16_t);
  212. // Copy the rest of the header except the ARCOUNT field.
  213. buffer.writeData(msgptr, 8);
  214. msgptr += 8;
  215. // Install the adjusted ARCOUNT (we don't care even if the value is bogus
  216. // and it underflows; it would simply result in verification failure)
  217. buffer.writeUint16(InputBuffer(msgptr, sizeof(uint16_t)).readUint16() - 1);
  218. msgptr += 2;
  219. // Digest the header and the rest of the DNS message
  220. hmac->update(buffer.getData(), buffer.getLength());
  221. hmac->update(msgptr, data_len - MESSAGE_HEADER_LEN);
  222. }
  223. TSIGContext::TSIGContext(const TSIGKey& key) : impl_(new TSIGContextImpl(key))
  224. {
  225. }
  226. TSIGContext::TSIGContext(const Name& key_name, const Name& algorithm_name,
  227. const TSIGKeyRing& keyring) : impl_(NULL)
  228. {
  229. const TSIGKeyRing::FindResult result(keyring.find(key_name,
  230. algorithm_name));
  231. if (result.code == TSIGKeyRing::NOTFOUND) {
  232. // If not key is found, create a dummy key with the specified key
  233. // parameters and empty secret. In the common scenario this will
  234. // be used in subsequent response with a TSIG indicating a BADKEY
  235. // error.
  236. impl_ = new TSIGContextImpl(TSIGKey(key_name, algorithm_name,
  237. NULL, 0), TSIGError::BAD_KEY());
  238. } else {
  239. impl_ = new TSIGContextImpl(*result.key);
  240. }
  241. }
  242. TSIGContext::~TSIGContext() {
  243. delete impl_;
  244. }
  245. size_t
  246. TSIGContext::getTSIGLength() const {
  247. //
  248. // The space required for an TSIG record is:
  249. //
  250. // n1 bytes for the (key) name
  251. // 2 bytes for the type
  252. // 2 bytes for the class
  253. // 4 bytes for the ttl
  254. // 2 bytes for the rdlength
  255. // n2 bytes for the algorithm name
  256. // 6 bytes for the time signed
  257. // 2 bytes for the fudge
  258. // 2 bytes for the MAC size
  259. // x bytes for the MAC
  260. // 2 bytes for the original id
  261. // 2 bytes for the error
  262. // 2 bytes for the other data length
  263. // y bytes for the other data (at most)
  264. // ---------------------------------
  265. // 26 + n1 + n2 + x + y bytes
  266. //
  267. // Normally the digest length ("x") is the length of the underlying
  268. // hash output. If a key related error occurred, however, the
  269. // corresponding TSIG will be "unsigned", and the digest length will be 0.
  270. const size_t digest_len =
  271. (impl_->error_ == TSIGError::BAD_KEY() ||
  272. impl_->error_ == TSIGError::BAD_SIG()) ? 0 : impl_->digest_len_;
  273. // Other Len ("y") is normally 0; if BAD_TIME error occurred, the
  274. // subsequent TSIG will contain 48 bits of the server current time.
  275. const size_t other_len = (impl_->error_ == TSIGError::BAD_TIME()) ? 6 : 0;
  276. return (26 + impl_->key_.getKeyName().getLength() +
  277. impl_->key_.getAlgorithmName().getLength() +
  278. digest_len + other_len);
  279. }
  280. TSIGContext::State
  281. TSIGContext::getState() const {
  282. return (impl_->state_);
  283. }
  284. TSIGError
  285. TSIGContext::getError() const {
  286. return (impl_->error_);
  287. }
  288. ConstTSIGRecordPtr
  289. TSIGContext::sign(const uint16_t qid, const void* const data,
  290. const size_t data_len)
  291. {
  292. if (impl_->state_ == VERIFIED_RESPONSE) {
  293. isc_throw(TSIGContextError,
  294. "TSIG sign attempt after verifying a response");
  295. }
  296. if (data == NULL || data_len == 0) {
  297. isc_throw(InvalidParameter, "TSIG sign error: empty data is given");
  298. }
  299. TSIGError error(TSIGError::NOERROR());
  300. const uint64_t now = getTSIGTime();
  301. // For responses adjust the error code.
  302. if (impl_->state_ == RECEIVED_REQUEST) {
  303. error = impl_->error_;
  304. }
  305. // For errors related to key or MAC, return an unsigned response as
  306. // specified in Section 4.3 of RFC2845.
  307. if (error == TSIGError::BAD_SIG() || error == TSIGError::BAD_KEY()) {
  308. ConstTSIGRecordPtr tsig(new TSIGRecord(
  309. impl_->key_.getKeyName(),
  310. any::TSIG(impl_->key_.getAlgorithmName(),
  311. now, DEFAULT_FUDGE, 0, NULL,
  312. qid, error.getCode(), 0, NULL)));
  313. impl_->previous_digest_.clear();
  314. impl_->state_ = SENT_RESPONSE;
  315. return (tsig);
  316. }
  317. HMACPtr hmac(impl_->createHMAC());
  318. // If the context has previous MAC (either the Request MAC or its own
  319. // previous MAC), digest it.
  320. if (impl_->state_ != INIT) {
  321. impl_->digestPreviousMAC(hmac);
  322. }
  323. // Digest the message (without TSIG)
  324. hmac->update(data, data_len);
  325. // Digest TSIG variables.
  326. // First, prepare some non constant variables.
  327. const uint64_t time_signed = (error == TSIGError::BAD_TIME()) ?
  328. impl_->previous_timesigned_ : now;
  329. // For BADTIME error, we include 6 bytes of other data.
  330. // (6 bytes = size of time signed value)
  331. const uint16_t otherlen = (error == TSIGError::BAD_TIME()) ? 6 : 0;
  332. OutputBuffer otherdatabuf(otherlen);
  333. if (error == TSIGError::BAD_TIME()) {
  334. otherdatabuf.writeUint16(now >> 32);
  335. otherdatabuf.writeUint32(now & 0xffffffff);
  336. }
  337. const void* const otherdata =
  338. (otherlen == 0) ? NULL : otherdatabuf.getData();
  339. // Then calculate the digest. If state_ is SENT_RESPONSE we are sending
  340. // a continued message in the same TCP stream so skip digesting
  341. // variables except for time related variables (RFC2845 4.4).
  342. impl_->digestTSIGVariables(hmac, TSIGRecord::getClass().getCode(),
  343. TSIGRecord::TSIG_TTL, time_signed,
  344. DEFAULT_FUDGE, error.getCode(),
  345. otherlen, otherdata,
  346. impl_->state_ == SENT_RESPONSE);
  347. // Get the final digest, update internal state, then finish.
  348. vector<uint8_t> digest = hmac->sign();
  349. assert(digest.size() <= 0xffff); // cryptolink API should have ensured it.
  350. ConstTSIGRecordPtr tsig(new TSIGRecord(
  351. impl_->key_.getKeyName(),
  352. any::TSIG(impl_->key_.getAlgorithmName(),
  353. time_signed, DEFAULT_FUDGE,
  354. digest.size(), &digest[0],
  355. qid, error.getCode(), otherlen,
  356. otherdata)));
  357. // Exception free from now on.
  358. impl_->previous_digest_.swap(digest);
  359. impl_->state_ = (impl_->state_ == INIT) ? SENT_REQUEST : SENT_RESPONSE;
  360. return (tsig);
  361. }
  362. TSIGError
  363. TSIGContext::verify(const TSIGRecord* const record, const void* const data,
  364. const size_t data_len)
  365. {
  366. if (impl_->state_ == SENT_RESPONSE) {
  367. isc_throw(TSIGContextError,
  368. "TSIG verify attempt after sending a response");
  369. }
  370. // This case happens when we sent a signed request and have received an
  371. // unsigned response. According to RFC2845 Section 4.6 this case should be
  372. // considered a "format error" (although the specific error code
  373. // wouldn't matter much for the caller).
  374. if (record == NULL) {
  375. return (impl_->postVerifyUpdate(TSIGError::FORMERR(), NULL, 0));
  376. }
  377. const any::TSIG& tsig_rdata = record->getRdata();
  378. // Reject some obviously invalid data
  379. if (data_len < MESSAGE_HEADER_LEN + record->getLength()) {
  380. isc_throw(InvalidParameter,
  381. "TSIG verify: data length is invalid: " << data_len);
  382. }
  383. if (data == NULL) {
  384. isc_throw(InvalidParameter, "TSIG verify: empty data is invalid");
  385. }
  386. // Check key: whether we first verify it with a known key or we verify
  387. // it using the consistent key in the context. If the check fails we are
  388. // done with BADKEY.
  389. if (impl_->state_ == INIT && impl_->error_ == TSIGError::BAD_KEY()) {
  390. return (impl_->postVerifyUpdate(TSIGError::BAD_KEY(), NULL, 0));
  391. }
  392. if (impl_->key_.getKeyName() != record->getName() ||
  393. impl_->key_.getAlgorithmName() != tsig_rdata.getAlgorithm()) {
  394. return (impl_->postVerifyUpdate(TSIGError::BAD_KEY(), NULL, 0));
  395. }
  396. // Check time: the current time must be in the range of
  397. // [time signed - fudge, time signed + fudge]. Otherwise verification
  398. // fails with BADTIME. (RFC2845 Section 4.6.2)
  399. // Note: for simplicity we don't explicitly catch the case of too small
  400. // current time causing underflow. With the fact that fudge is quite
  401. // small and (for now) non configurable, it shouldn't be a real concern
  402. // in practice.
  403. const uint64_t now = getTSIGTime();
  404. if (tsig_rdata.getTimeSigned() + DEFAULT_FUDGE < now ||
  405. tsig_rdata.getTimeSigned() - DEFAULT_FUDGE > now) {
  406. const void* digest = NULL;
  407. size_t digest_len = 0;
  408. if (impl_->state_ == INIT) {
  409. digest = tsig_rdata.getMAC();
  410. digest_len = tsig_rdata.getMACSize();
  411. impl_->previous_timesigned_ = tsig_rdata.getTimeSigned();
  412. }
  413. return (impl_->postVerifyUpdate(TSIGError::BAD_TIME(), digest,
  414. digest_len));
  415. }
  416. // TODO: signature length check based on RFC4635
  417. // (Right now we enforce the standard signature length in libcryptolink)
  418. // Handling empty MAC. While RFC2845 doesn't explicitly prohibit other
  419. // cases, it can only reasonably happen in a response with BADSIG or
  420. // BADKEY. We reject other cases as if it were BADSIG to avoid unexpected
  421. // acceptance of a bogus signature. This behavior follows the BIND 9
  422. // implementation.
  423. if (tsig_rdata.getMACSize() == 0) {
  424. TSIGError error = TSIGError(tsig_rdata.getError());
  425. if (error != TSIGError::BAD_SIG() && error != TSIGError::BAD_KEY()) {
  426. error = TSIGError::BAD_SIG();
  427. }
  428. return (impl_->postVerifyUpdate(error, NULL, 0));
  429. }
  430. HMACPtr hmac(impl_->createHMAC());
  431. // If the context has previous MAC (either the Request MAC or its own
  432. // previous MAC), digest it.
  433. if (impl_->state_ != INIT) {
  434. impl_->digestPreviousMAC(hmac);
  435. }
  436. //
  437. // Digest DNS message (excluding the trailing TSIG RR and adjusting the
  438. // QID and ARCOUNT header fields)
  439. //
  440. impl_->digestDNSMessage(hmac, tsig_rdata.getOriginalID(),
  441. data, data_len - record->getLength());
  442. // Digest TSIG variables. If state_ is VERIFIED_RESPONSE, it's a
  443. // continuation of the same TCP stream and skip digesting them except
  444. // for time related variables (RFC2845 4.4).
  445. // Note: we use the constant values for RR class and TTL specified
  446. // in RFC2845, not received values (we reject other values in constructing
  447. // the TSIGRecord).
  448. impl_->digestTSIGVariables(hmac, TSIGRecord::getClass().getCode(),
  449. TSIGRecord::TSIG_TTL,
  450. tsig_rdata.getTimeSigned(),
  451. tsig_rdata.getFudge(), tsig_rdata.getError(),
  452. tsig_rdata.getOtherLen(),
  453. tsig_rdata.getOtherData(),
  454. impl_->state_ == VERIFIED_RESPONSE);
  455. // Verify the digest with the received signature.
  456. if (hmac->verify(tsig_rdata.getMAC(), tsig_rdata.getMACSize())) {
  457. return (impl_->postVerifyUpdate(TSIGError::NOERROR(),
  458. tsig_rdata.getMAC(),
  459. tsig_rdata.getMACSize()));
  460. }
  461. return (impl_->postVerifyUpdate(TSIGError::BAD_SIG(), NULL, 0));
  462. }
  463. } // namespace dns
  464. } // namespace isc