tsig_250.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. // Copyright (C) 2010 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. // $Id$
  15. #include <string>
  16. #include <sstream>
  17. #include <vector>
  18. #include <boost/lexical_cast.hpp>
  19. #include <dns/buffer.h>
  20. #include <dns/messagerenderer.h>
  21. #include <dns/rdata.h>
  22. #include <dns/rdataclass.h>
  23. #include <dns/util/base64.h>
  24. using namespace std;
  25. using namespace boost;
  26. // BEGIN_ISC_NAMESPACE
  27. // BEGIN_RDATA_NAMESPACE
  28. /// This is a straightforward representation of TSIG RDATA fields.
  29. struct TSIG::TSIGImpl {
  30. TSIGImpl(const Name& algorithm, uint64_t time_signed, uint16_t fudge,
  31. vector<uint8_t>& mac, uint16_t original_id, uint16_t error,
  32. vector<uint8_t>& other_data) :
  33. algorithm_(algorithm), time_signed_(time_signed), fudge_(fudge),
  34. mac_(mac), original_id_(original_id), error_(error),
  35. other_data_(other_data)
  36. {}
  37. TSIGImpl(const Name& algorithm, uint64_t time_signed, uint16_t fudge,
  38. size_t macsize, const void* mac, uint16_t original_id,
  39. uint16_t error, size_t other_len, const void* other_data) :
  40. algorithm_(algorithm), time_signed_(time_signed), fudge_(fudge),
  41. mac_(static_cast<const uint8_t*>(mac),
  42. static_cast<const uint8_t*>(mac) + macsize),
  43. original_id_(original_id), error_(error),
  44. other_data_(static_cast<const uint8_t*>(other_data),
  45. static_cast<const uint8_t*>(other_data) + other_len)
  46. {}
  47. template <typename Output>
  48. void toWireCommon(Output& output) const;
  49. const Name algorithm_;
  50. const uint64_t time_signed_;
  51. const uint16_t fudge_;
  52. const vector<uint8_t> mac_;
  53. const uint16_t original_id_;
  54. const uint16_t error_;
  55. const vector<uint8_t> other_data_;
  56. };
  57. namespace {
  58. string
  59. getToken(istringstream& iss, const string& full_input) {
  60. string token;
  61. iss >> token;
  62. if (iss.bad() || iss.fail()) {
  63. isc_throw(InvalidRdataText, "Invalid TSIG text: parse error" <<
  64. full_input);
  65. }
  66. return (token);
  67. }
  68. // This helper function converts a string token to an *unsigned* integer.
  69. // NumType is a *signed* integral type (e.g. int32_t) that is sufficiently
  70. // wide to store resulting integers.
  71. // BitSize is the maximum number of bits that the resulting integer can take.
  72. // This function first checks whether the given token can be converted to
  73. // an integer of NumType type. It then confirms the conversion result is
  74. // within the valid range, i.e., [0, 2^NumType - 1]. The second check is
  75. // necessary because lexical_cast<T> where T is an unsigned integer type
  76. // doesn't correctly reject negative numbers when compiled with SunStudio.
  77. template <typename NumType, int BitSize>
  78. NumType
  79. tokenToNum(const string& num_token) {
  80. NumType num;
  81. try {
  82. num = lexical_cast<NumType>(num_token);
  83. } catch (const boost::bad_lexical_cast& ex) {
  84. isc_throw(InvalidRdataText, "Invalid TSIG numeric parameter: " <<
  85. num_token);
  86. }
  87. if (num < 0 || num >= (static_cast<NumType>(1) << BitSize)) {
  88. isc_throw(InvalidRdataText, "Numeric TSIG parameter out of range: " <<
  89. num);
  90. }
  91. return (num);
  92. }
  93. }
  94. /// \brief Constructor from string.
  95. ///
  96. /// \c tsig_str must be formatted as follows:
  97. /// \code <Alg> <Time> <Fudge> <MACsize> [<MAC>] <OrigID> <Error> <OtherLen> [<OtherData>]
  98. /// \endcode
  99. /// where
  100. /// - <Alg> is a valid textual representation of domain name.
  101. /// - <Time> is an unsigned 48-bit decimal integer.
  102. /// - <MACSize>, <OrigID>, and <OtherLen> are an unsigned 16-bit decimal
  103. /// integer.
  104. /// - <Error> is an unsigned 16-bit decimal integer or a valid mnemonic for
  105. /// the Error field specified in RFC2845. Currently, "BADSIG", "BADKEY",
  106. /// and "BADTIME" are supported (case sensitive). In future versions
  107. /// other representations that are compatible with the DNS RCODE will be
  108. /// supported.
  109. /// - <MAC> and <OtherData> is a BASE-64 encoded string that does not contain
  110. /// space characters.
  111. /// When <MACSize> and <OtherLen> is 0, <MAC> and <OtherData> must not
  112. /// appear in \c tsgi_str, respectively.
  113. /// - The decoded data of <MAC> is <MACSize> bytes of binary stream.
  114. /// - The decoded data of <OtherData> is <OtherLen> bytes of binary stream.
  115. ///
  116. /// An example of valid string is:
  117. /// \code "hmac-sha256. 853804800 300 3 AAAA 2845 0 0" \endcode
  118. /// In this example <OtherData> is missing because <OtherLen> is 0.
  119. ///
  120. /// Note that RFC2845 does not define the standard presentation format
  121. /// of %TSIG RR, so the above syntax is implementation specific.
  122. /// This is, however, compatible with the format acceptable to BIND 9's
  123. /// RDATA parser.
  124. ///
  125. /// <b>Exceptions</b>
  126. ///
  127. /// If <Alg> is not a valid domain name, a corresponding exception from
  128. /// the \c Name class will be thrown;
  129. /// if <MAC> or <OtherData> is not validly encoded in BASE-64, an exception
  130. /// of class \c isc::BadValue will be thrown;
  131. /// if %any of the other bullet points above is not met, an exception of
  132. /// class \c InvalidRdataText will be thrown.
  133. /// This constructor internally involves resource allocation, and if it fails
  134. /// a corresponding standard exception will be thrown.
  135. TSIG::TSIG(const std::string& tsig_str) : impl_(NULL) {
  136. istringstream iss(tsig_str);
  137. const Name algorithm(getToken(iss, tsig_str));
  138. const int64_t time_signed = tokenToNum<int64_t, 48>(getToken(iss,
  139. tsig_str));
  140. const int32_t fudge = tokenToNum<int32_t, 16>(getToken(iss, tsig_str));
  141. const int32_t macsize = tokenToNum<int32_t, 16>(getToken(iss, tsig_str));
  142. const string mac_txt = (macsize > 0) ? getToken(iss, tsig_str) : "";
  143. vector<uint8_t> mac;
  144. decodeBase64(mac_txt, mac);
  145. if (mac.size() != macsize) {
  146. isc_throw(InvalidRdataText, "TSIG MAC size and data are inconsistent");
  147. }
  148. const int32_t orig_id = tokenToNum<int32_t, 16>(getToken(iss, tsig_str));
  149. const string error_txt = getToken(iss, tsig_str);
  150. int32_t error = 0;
  151. // XXX: In the initial implementation we hardcode the mnemonics.
  152. // We'll soon generalize this.
  153. if (error_txt == "BADSIG") {
  154. error = 16;
  155. } else if (error_txt == "BADKEY") {
  156. error = 17;
  157. } else if (error_txt == "BADTIME") {
  158. error = 18;
  159. } else {
  160. error = tokenToNum<int32_t, 16>(error_txt);
  161. }
  162. const int32_t otherlen = tokenToNum<int32_t, 16>(getToken(iss, tsig_str));
  163. const string otherdata_txt = (otherlen > 0) ? getToken(iss, tsig_str) : "";
  164. vector<uint8_t> other_data;
  165. decodeBase64(otherdata_txt, other_data);
  166. if (!iss.eof()) {
  167. isc_throw(InvalidRdataText, "Unexpected input for TSIG RDATA: " <<
  168. tsig_str);
  169. }
  170. impl_ = new TSIGImpl(algorithm, time_signed, fudge, mac, orig_id,
  171. error, other_data);
  172. }
  173. /// \brief Constructor from wire-format data.
  174. ///
  175. /// When a read operation on \c buffer fails (e.g., due to a corrupted
  176. /// message) a corresponding exception from the \c InputBuffer class will
  177. /// be thrown.
  178. /// If the wire-format data does not begin with a valid domain name,
  179. /// a corresponding exception from the \c Name class will be thrown.
  180. /// In addition, this constructor internally involves resource allocation,
  181. /// and if it fails a corresponding standard exception will be thrown.
  182. ///
  183. /// According to RFC3597, the Algorithm field must be a non compressed form
  184. /// of domain name. But this implementation accepts a %TSIG RR even if that
  185. /// field is compressed.
  186. ///
  187. /// \param buffer A buffer storing the wire format data.
  188. /// \param rdata_len The length of the RDATA in bytes, normally expected
  189. /// to be the value of the RDLENGTH field of the corresponding RR.
  190. /// But this constructor does not use this parameter; if necessary, the caller
  191. /// must check consistency between the length parameter and the actual
  192. /// RDATA length.
  193. TSIG::TSIG(InputBuffer& buffer, size_t) : impl_(NULL) {
  194. Name algorithm(buffer);
  195. uint8_t time_signed_buf[6];
  196. buffer.readData(time_signed_buf, sizeof(time_signed_buf));
  197. const uint64_t time_signed =
  198. (static_cast<uint64_t>(time_signed_buf[0]) << 40 |
  199. static_cast<uint64_t>(time_signed_buf[1]) << 32 |
  200. static_cast<uint64_t>(time_signed_buf[2]) << 24 |
  201. static_cast<uint64_t>(time_signed_buf[3]) << 16 |
  202. static_cast<uint64_t>(time_signed_buf[4]) << 8 |
  203. static_cast<uint64_t>(time_signed_buf[5]));
  204. const uint16_t fudge = buffer.readUint16();
  205. const uint16_t mac_size = buffer.readUint16();
  206. vector<uint8_t> mac(mac_size);
  207. if (mac_size > 0) {
  208. buffer.readData(&mac[0], mac_size);
  209. }
  210. const uint16_t original_id = buffer.readUint16();
  211. const uint16_t error = buffer.readUint16();
  212. const uint16_t other_len = buffer.readUint16();
  213. vector<uint8_t> other_data(other_len);
  214. if (other_len > 0) {
  215. buffer.readData(&other_data[0], other_len);
  216. }
  217. impl_ = new TSIGImpl(algorithm, time_signed, fudge, mac, original_id,
  218. error, other_data);
  219. }
  220. TSIG::TSIG(const Name& algorithm, uint64_t time_signed, uint16_t fudge,
  221. uint16_t mac_size, const void* mac, uint16_t original_id,
  222. uint16_t error, uint16_t other_len, const void* other_data) :
  223. impl_(NULL)
  224. {
  225. // Time Signed is a 48-bit value.
  226. if ((time_signed >> 48) != 0) {
  227. isc_throw(OutOfRange, "TSIG Time Signed is too large: " <<
  228. time_signed);
  229. }
  230. if ((mac_size == 0 && mac != NULL) || (mac_size > 0 && mac == NULL)) {
  231. isc_throw(InvalidParameter, "TSIG MAC size and data inconsistent");
  232. }
  233. if ((other_len == 0 && other_data != NULL) ||
  234. (other_len > 0 && other_data == NULL)) {
  235. isc_throw(InvalidParameter,
  236. "TSIG Other data length and data inconsistent");
  237. }
  238. impl_ = new TSIGImpl(algorithm, time_signed, fudge, mac_size, mac,
  239. original_id, error, other_len, other_data);
  240. }
  241. /// \brief The copy constructor.
  242. ///
  243. /// It internally allocates a resource, and if it fails a corresponding
  244. /// standard exception will be thrown.
  245. /// This constructor never throws an exception otherwise.
  246. TSIG::TSIG(const TSIG& source) : Rdata(), impl_(new TSIGImpl(*source.impl_))
  247. {}
  248. TSIG&
  249. TSIG::operator=(const TSIG& source) {
  250. if (impl_ == source.impl_) {
  251. return (*this);
  252. }
  253. TSIGImpl* newimpl = new TSIGImpl(*source.impl_);
  254. delete impl_;
  255. impl_ = newimpl;
  256. return (*this);
  257. }
  258. TSIG::~TSIG() {
  259. delete impl_;
  260. }
  261. /// \brief Convert the \c TSIG to a string.
  262. ///
  263. /// The output of this method is formatted as described in the "from string"
  264. /// constructor (\c TSIG(const std::string&))).
  265. ///
  266. /// If internal resource allocation fails, a corresponding
  267. /// standard exception will be thrown.
  268. ///
  269. /// \return A \c string object that represents the \c TSIG object.
  270. std::string
  271. TSIG::toText() const {
  272. string result;
  273. result += impl_->algorithm_.toText() + " " +
  274. lexical_cast<string>(impl_->time_signed_) + " " +
  275. lexical_cast<string>(impl_->fudge_) + " " +
  276. lexical_cast<string>(impl_->mac_.size()) + " ";
  277. if (impl_->mac_.size() > 0) {
  278. result += encodeBase64(impl_->mac_) + " ";
  279. }
  280. result += lexical_cast<string>(impl_->original_id_) + " ";
  281. if (impl_->error_ == 16) { // XXX: we'll soon introduce generic converter.
  282. result += "BADSIG ";
  283. } else if (impl_->error_ == 17) {
  284. result += "BADKEY ";
  285. } else if (impl_->error_ == 18) {
  286. result += "BADTIME ";
  287. } else {
  288. result += lexical_cast<string>(impl_->error_) + " ";
  289. }
  290. result += lexical_cast<string>(impl_->other_data_.size());
  291. if (impl_->other_data_.size() > 0) {
  292. result += " " + encodeBase64(impl_->other_data_);
  293. }
  294. return (result);
  295. }
  296. // Common sequence of toWire() operations used for the two versions of
  297. // toWire().
  298. template <typename Output>
  299. void
  300. TSIG::TSIGImpl::toWireCommon(Output& output) const {
  301. output.writeUint16(time_signed_ >> 32);
  302. output.writeUint32(time_signed_ & 0xffffffff);
  303. output.writeUint16(fudge_);
  304. const uint16_t mac_size = mac_.size();
  305. output.writeUint16(mac_size);
  306. if (mac_size > 0) {
  307. output.writeData(&mac_[0], mac_size);
  308. }
  309. output.writeUint16(original_id_);
  310. output.writeUint16(error_);
  311. const uint16_t other_len = other_data_.size();
  312. output.writeUint16(other_len);
  313. if (other_len > 0) {
  314. output.writeData(&other_data_[0], other_len);
  315. }
  316. }
  317. /// \brief Render the \c TSIG in the wire format without name compression.
  318. ///
  319. /// If internal resource allocation fails, a corresponding
  320. /// standard exception will be thrown.
  321. /// This method never throws an exception otherwise.
  322. ///
  323. /// \param buffer An output buffer to store the wire data.
  324. void
  325. TSIG::toWire(OutputBuffer& buffer) const {
  326. impl_->algorithm_.toWire(buffer);
  327. impl_->toWireCommon<OutputBuffer>(buffer);
  328. }
  329. /// \brief Render the \c TSIG in the wire format with taking into account
  330. /// compression.
  331. ///
  332. /// As specified in RFC3597, the Algorithm field (a domain name) will not
  333. /// be compressed. However, the domain name could be a target of compression
  334. /// of other compressible names (though pretty unlikely), the offset
  335. /// information of the algorithm name may be recorded in \c renderer.
  336. ///
  337. /// If internal resource allocation fails, a corresponding
  338. /// standard exception will be thrown.
  339. /// This method never throws an exception otherwise.
  340. ///
  341. /// \param renderer DNS message rendering context that encapsulates the
  342. /// output buffer and name compression information.
  343. void
  344. TSIG::toWire(MessageRenderer& renderer) const {
  345. renderer.writeName(impl_->algorithm_, false);
  346. impl_->toWireCommon<MessageRenderer>(renderer);
  347. }
  348. // A helper function commonly used for TSIG::compare().
  349. int
  350. vectorComp(const vector<uint8_t>& v1, const vector<uint8_t>& v2) {
  351. const size_t this_size = v1.size();
  352. const size_t other_size = v2.size();
  353. if (this_size != other_size) {
  354. return (this_size < other_size ? -1 : 1);
  355. }
  356. if (this_size > 0) {
  357. return (memcmp(&v1[0], &v2[0], this_size));
  358. }
  359. return (0);
  360. }
  361. /// \brief Compare two instances of \c TSIG RDATA.
  362. ///
  363. /// This method compares \c this and the \c other \c TSIG objects
  364. /// in terms of the DNSSEC sorting order as defined in RFC4034, and returns
  365. /// the result as an integer.
  366. ///
  367. /// This method is expected to be used in a polymorphic way, and the
  368. /// parameter to compare against is therefore of the abstract \c Rdata class.
  369. /// However, comparing two \c Rdata objects of different RR types
  370. /// is meaningless, and \c other must point to a \c TSIG object;
  371. /// otherwise, the standard \c bad_cast exception will be thrown.
  372. /// This method never throws an exception otherwise.
  373. ///
  374. /// \param other the right-hand operand to compare against.
  375. /// \return < 0 if \c this would be sorted before \c other.
  376. /// \return 0 if \c this is identical to \c other in terms of sorting order.
  377. /// \return > 0 if \c this would be sorted after \c other.
  378. int
  379. TSIG::compare(const Rdata& other) const {
  380. const TSIG& other_tsig = dynamic_cast<const TSIG&>(other);
  381. const int ncmp = compareNames(impl_->algorithm_,
  382. other_tsig.impl_->algorithm_);
  383. if (ncmp != 0) {
  384. return (ncmp);
  385. }
  386. if (impl_->time_signed_ != other_tsig.impl_->time_signed_) {
  387. return (impl_->time_signed_ < other_tsig.impl_->time_signed_ ? -1 : 1);
  388. }
  389. if (impl_->fudge_ != other_tsig.impl_->fudge_) {
  390. return (impl_->fudge_ < other_tsig.impl_->fudge_ ? -1 : 1);
  391. }
  392. const int vcmp = vectorComp(impl_->mac_, other_tsig.impl_->mac_);
  393. if (vcmp != 0) {
  394. return (vcmp);
  395. }
  396. if (impl_->original_id_ != other_tsig.impl_->original_id_) {
  397. return (impl_->original_id_ < other_tsig.impl_->original_id_ ? -1 : 1);
  398. }
  399. if (impl_->error_ != other_tsig.impl_->error_) {
  400. return (impl_->error_ < other_tsig.impl_->error_ ? -1 : 1);
  401. }
  402. return (vectorComp(impl_->other_data_, other_tsig.impl_->other_data_));
  403. }
  404. const Name&
  405. TSIG::getAlgorithm() const {
  406. return (impl_->algorithm_);
  407. }
  408. uint64_t
  409. TSIG::getTimeSigned() const {
  410. return (impl_->time_signed_);
  411. }
  412. uint16_t
  413. TSIG::getFudge() const {
  414. return (impl_->fudge_);
  415. }
  416. uint16_t
  417. TSIG::getMACSize() const {
  418. return (impl_->mac_.size());
  419. }
  420. const void*
  421. TSIG::getMAC() const {
  422. if (impl_->mac_.size() > 0) {
  423. return (&impl_->mac_[0]);
  424. } else {
  425. return (NULL);
  426. }
  427. }
  428. uint16_t
  429. TSIG::getOriginalID() const {
  430. return (impl_->original_id_);
  431. }
  432. uint16_t
  433. TSIG::getError() const {
  434. return (impl_->error_);
  435. }
  436. uint16_t
  437. TSIG::getOtherLen() const {
  438. return (impl_->other_data_.size());
  439. }
  440. const void*
  441. TSIG::getOtherData() const {
  442. if (impl_->other_data_.size() > 0) {
  443. return (&impl_->other_data_[0]);
  444. } else {
  445. return (NULL);
  446. }
  447. }
  448. // END_RDATA_NAMESPACE
  449. // END_ISC_NAMESPACE