2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include "ednscookies.hh"
28 #ifdef HAVE_CRYPTO_SHORTHASH
32 EDNSCookiesOpt::EDNSCookiesOpt(const std::string
& option
)
34 getEDNSCookiesOptFromString(option
.c_str(), option
.length());
37 EDNSCookiesOpt::EDNSCookiesOpt(const char* option
, unsigned int len
)
39 getEDNSCookiesOptFromString(option
, len
);
42 bool EDNSCookiesOpt::makeFromString(const std::string
& option
)
44 getEDNSCookiesOptFromString(option
.c_str(), option
.length());
45 return isWellFormed();
48 bool EDNSCookiesOpt::makeFromString(const char* option
, unsigned int len
)
50 getEDNSCookiesOptFromString(option
, len
);
51 return isWellFormed();
54 string
EDNSCookiesOpt::makeOptString() const
60 if (server
.length() != 0)
65 void EDNSCookiesOpt::getEDNSCookiesOptFromString(const char* option
, unsigned int len
)
71 client
= string(option
, 8);
73 server
= string(option
+ 8, len
- 8);
77 bool EDNSCookiesOpt::isValid([[maybe_unused
]] const string
& secret
, [[maybe_unused
]] const ComboAddress
& source
) const
79 #ifdef HAVE_CRYPTO_SHORTHASH
80 if (server
.length() != 16 || client
.length() != 8) {
83 if (server
[0] != '\x01') {
84 // Version is not 1, can't verify
88 memcpy(&ts
, &server
[4], sizeof(ts
));
90 // coverity[store_truncates_time_t]
91 uint32_t now
= static_cast<uint32_t>(time(nullptr));
92 // RFC 9018 section 4.3:
94 // SHOULD allow cookies within a 1-hour period in the past and a
95 // 5-minute period into the future
96 if (rfc1982LessThan(now
+ 300, ts
) && rfc1982LessThan(ts
+ 3600, now
)) {
99 if (secret
.length() != crypto_shorthash_KEYBYTES
) {
103 string toHash
= client
+ server
.substr(0, 8) + source
.toByteString();
105 hashResult
.resize(8);
107 reinterpret_cast<unsigned char*>(&hashResult
[0]),
108 reinterpret_cast<const unsigned char*>(&toHash
[0]),
110 reinterpret_cast<const unsigned char*>(&secret
[0]));
111 return constantTimeStringEquals(server
.substr(8), hashResult
);
117 bool EDNSCookiesOpt::shouldRefresh() const
119 if (server
.size() < 16) {
123 memcpy(&ts
, &server
[4], sizeof(ts
));
125 // coverity[store_truncates_time_t]
126 uint32_t now
= static_cast<uint32_t>(time(nullptr));
127 // RFC 9018 section 4.3:
129 // SHOULD allow cookies within a 1-hour period in the past and a
130 // 5-minute period into the future
131 // If this is not the case, we need to refresh
132 if (rfc1982LessThan(now
+ 300, ts
) && rfc1982LessThan(ts
+ 3600, now
)) {
136 // RFC 9018 section 4.3:
137 // The DNS server SHOULD generate a new Server Cookie at least if the
138 // received Server Cookie from the client is more than half an hour old
139 return rfc1982LessThan(ts
+ 1800, now
);
142 bool EDNSCookiesOpt::makeServerCookie([[maybe_unused
]] const string
& secret
, [[maybe_unused
]] const ComboAddress
& source
)
144 #ifdef HAVE_CRYPTO_SHORTHASH
145 static_assert(EDNSCookieSecretSize
== crypto_shorthash_KEYBYTES
* 2, "The EDNSCookieSecretSize is not twice crypto_shorthash_KEYBYTES");
147 if (isValid(secret
, source
) && !shouldRefresh()) {
151 if (secret
.length() != crypto_shorthash_KEYBYTES
) {
157 server
= "\x01"; // Version
158 server
.resize(4, '\0'); // 3 reserved bytes
159 // coverity[store_truncates_time_t]
160 uint32_t now
= htonl(static_cast<uint32_t>(time(nullptr)));
161 server
+= string(reinterpret_cast<const char*>(&now
), sizeof(now
));
164 string toHash
= client
;
166 toHash
+= source
.toByteString();
169 reinterpret_cast<unsigned char*>(&server
[8]),
170 reinterpret_cast<const unsigned char*>(&toHash
[0]),
172 reinterpret_cast<const unsigned char*>(&secret
[0]));