]>
Commit | Line | Data |
---|---|---|
12471842 PL |
1 | /* |
2 | * This file is part of PowerDNS or dnsdist. | |
3 | * Copyright -- PowerDNS.COM B.V. and its contributors | |
4 | * | |
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. | |
8 | * | |
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. | |
12 | * | |
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. | |
17 | * | |
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. | |
21 | */ | |
22 | ||
ab06937b BH |
23 | /** two modes: |
24 | ||
25 | anonymizing and stripping tcpdumps of irrelevant traffic, so operators can send non-privacy violating dumps | |
26 | for analysis. | |
27 | ||
28 | algorithm: | |
29 | ||
9be88092 | 30 | read a packet, check if it has the QR bit set. |
ab06937b BH |
31 | |
32 | If the question has the response bit set, obfuscate the destination IP address | |
33 | otherwise, obfuscate the response IP address | |
ab06937b BH |
34 | */ |
35 | ||
11212b81 | 36 | |
870a0fe4 AT |
37 | #ifdef HAVE_CONFIG_H |
38 | #include "config.h" | |
39 | #endif | |
e41aadeb | 40 | |
8fd7cf73 | 41 | #include "namespaces.hh" |
ab06937b | 42 | #include "statbag.hh" |
8fd7cf73 OM |
43 | StatBag S; |
44 | ||
45 | #ifdef HAVE_IPCIPHER | |
ab06937b | 46 | #include "dnspcap.hh" |
40ff5c7d | 47 | #include "iputils.hh" |
152b0801 | 48 | #include "ipcipher.hh" |
152b0801 | 49 | #include <boost/program_options.hpp> |
50 | #include "base64.hh" | |
ab06937b | 51 | |
ab06937b | 52 | |
152b0801 | 53 | namespace po = boost::program_options; |
54 | po::variables_map g_vm; | |
55 | ||
56 | ||
ab06937b BH |
57 | class IPObfuscator |
58 | { | |
59 | public: | |
abb11ca4 | 60 | virtual ~IPObfuscator() = default; |
152b0801 | 61 | virtual uint32_t obf4(uint32_t orig)=0; |
62 | virtual struct in6_addr obf6(const struct in6_addr& orig)=0; | |
63 | }; | |
64 | ||
65 | class IPSeqObfuscator : public IPObfuscator | |
66 | { | |
67 | public: | |
68 | IPSeqObfuscator() : d_romap(d_ipmap), d_ro6map(d_ip6map), d_counter(0) | |
ab06937b BH |
69 | { |
70 | } | |
71 | ||
152b0801 | 72 | static std::unique_ptr<IPObfuscator> make() |
73 | { | |
2bbc9eb0 | 74 | return std::make_unique<IPSeqObfuscator>(); |
152b0801 | 75 | } |
488bcb39 | 76 | |
152b0801 | 77 | uint32_t obf4(uint32_t orig) override |
ab06937b BH |
78 | { |
79 | if(d_romap.count(orig)) | |
80 | return d_ipmap[orig]; | |
81 | else { | |
82 | return d_ipmap[orig]=d_counter++; | |
83 | } | |
84 | } | |
85 | ||
152b0801 | 86 | struct in6_addr obf6(const struct in6_addr& orig) override |
cbb0be81 | 87 | { |
7856684d | 88 | uint32_t val; |
cbb0be81 | 89 | if(d_ro6map.count(orig)) |
7856684d | 90 | val=d_ip6map[orig]; |
cbb0be81 | 91 | else { |
7856684d | 92 | val=d_ip6map[orig]=d_counter++; |
cbb0be81 | 93 | } |
7856684d | 94 | struct in6_addr ret; |
647efa8b | 95 | |
7856684d | 96 | val=htonl(val); |
647efa8b | 97 | memset(&ret, 0, sizeof(ret)); |
7856684d | 98 | memcpy(((char*)&ret)+12, &val, 4); |
99 | return ret; | |
cbb0be81 PL |
100 | } |
101 | ||
ab06937b BH |
102 | private: |
103 | map<uint32_t, uint32_t> d_ipmap; | |
7856684d | 104 | const decltype(d_ipmap)& d_romap; |
105 | ||
106 | struct cmp { | |
107 | bool operator()(const struct in6_addr&a , const struct in6_addr&b) const | |
108 | { | |
109 | return memcmp(&a, &b, sizeof(a)) < 0; | |
110 | } | |
111 | }; | |
cbb0be81 | 112 | // For IPv6 addresses |
7856684d | 113 | map<struct in6_addr, uint32_t, cmp> d_ip6map; |
114 | const decltype(d_ip6map)& d_ro6map; | |
115 | ||
cbb0be81 | 116 | // The counter that we'll convert to an IP address |
ab06937b BH |
117 | uint32_t d_counter; |
118 | }; | |
119 | ||
152b0801 | 120 | class IPCipherObfuscator : public IPObfuscator |
121 | { | |
122 | public: | |
123 | IPCipherObfuscator(const std::string& key, bool decrypt) : d_key(key), d_decrypt(decrypt) | |
124 | { | |
125 | if(d_key.size()!=16) { | |
126 | throw std::runtime_error("IPCipher requires a 128 bit key"); | |
127 | } | |
128 | } | |
129 | ||
224085cc | 130 | static std::unique_ptr<IPObfuscator> make(const std::string& key, bool decrypt) |
152b0801 | 131 | { |
2bbc9eb0 | 132 | return std::make_unique<IPCipherObfuscator>(key, decrypt); |
152b0801 | 133 | } |
488bcb39 | 134 | |
152b0801 | 135 | uint32_t obf4(uint32_t orig) override |
136 | { | |
137 | ComboAddress ca; | |
138 | ca.sin4.sin_family = AF_INET; | |
139 | ca.sin4.sin_addr.s_addr = orig; | |
140 | ca = d_decrypt ? decryptCA(ca, d_key) : encryptCA(ca, d_key); | |
141 | return ca.sin4.sin_addr.s_addr; | |
142 | ||
143 | } | |
144 | ||
145 | struct in6_addr obf6(const struct in6_addr& orig) override | |
146 | { | |
147 | ComboAddress ca; | |
148 | ca.sin4.sin_family = AF_INET6; | |
149 | ca.sin6.sin6_addr = orig; | |
150 | ca = d_decrypt ? decryptCA(ca, d_key) : encryptCA(ca, d_key); | |
151 | return ca.sin6.sin6_addr; | |
152 | } | |
153 | ||
154 | private: | |
155 | std::string d_key; | |
156 | bool d_decrypt; | |
157 | }; | |
158 | ||
159 | ||
050e6877 | 160 | static void usage() { |
e795f590 | 161 | cerr<<"Syntax: dnswasher INFILE1 [INFILE2..] OUTFILE"<<endl; |
3d414a24 PL |
162 | } |
163 | ||
ab06937b BH |
164 | int main(int argc, char** argv) |
165 | try | |
166 | { | |
152b0801 | 167 | po::options_description desc("Allowed options"); |
168 | desc.add_options() | |
169 | ("help,h", "produce help message") | |
170 | ("version", "show version number") | |
171 | ("key,k", po::value<string>(), "base64 encoded 128 bit key for ipcipher") | |
172 | ("passphrase,p", po::value<string>(), "passphrase for ipcipher (will be used to derive key)") | |
173 | ("decrypt,d", "decrypt IP addresses with ipcipher"); | |
488bcb39 | 174 | |
152b0801 | 175 | po::options_description alloptions; |
176 | po::options_description hidden("hidden options"); | |
177 | hidden.add_options() | |
178 | ("infiles", po::value<vector<string>>(), "PCAP source file(s)") | |
179 | ("outfile", po::value<string>(), "outfile"); | |
3d414a24 | 180 | |
152b0801 | 181 | |
182 | alloptions.add(desc).add(hidden); | |
183 | po::positional_options_description p; | |
184 | p.add("infiles", 1); | |
185 | p.add("outfile", 1); | |
186 | ||
187 | po::store(po::command_line_parser(argc, argv).options(alloptions).positional(p).run(), g_vm); | |
188 | po::notify(g_vm); | |
189 | ||
190 | if(g_vm.count("help")) { | |
191 | usage(); | |
192 | cout<<desc<<endl; | |
193 | exit(EXIT_SUCCESS); | |
3d414a24 PL |
194 | } |
195 | ||
152b0801 | 196 | if(g_vm.count("version")) { |
197 | cout<<"dnswasher "<<VERSION<<endl; | |
198 | exit(EXIT_SUCCESS); | |
199 | } | |
200 | ||
201 | if(!g_vm.count("outfile")) { | |
202 | cout<<"Missing outfile"<<endl; | |
3d414a24 | 203 | usage(); |
152b0801 | 204 | exit(EXIT_FAILURE); |
205 | } | |
206 | ||
207 | bool doDecrypt = g_vm.count("decrypt"); | |
488bcb39 | 208 | |
152b0801 | 209 | PcapPacketWriter pw(g_vm["outfile"].as<string>()); |
210 | std::unique_ptr<IPObfuscator> ipo; | |
211 | ||
212 | if(!g_vm.count("key") && !g_vm.count("passphrase")) | |
213 | ipo = IPSeqObfuscator::make(); | |
214 | else if(g_vm.count("key") && !g_vm.count("passphrase")) { | |
215 | string key; | |
216 | if(B64Decode(g_vm["key"].as<string>(), key) < 0) { | |
217 | cerr<<"Invalidly encoded base64 key provided"<<endl; | |
218 | exit(EXIT_FAILURE); | |
219 | } | |
7d0fefb4 | 220 | ipo = IPCipherObfuscator::make(std::move(key), doDecrypt); |
152b0801 | 221 | } |
222 | else if(!g_vm.count("key") && g_vm.count("passphrase")) { | |
223 | string key = makeIPCipherKey(g_vm["passphrase"].as<string>()); | |
488bcb39 | 224 | |
7d0fefb4 | 225 | ipo = IPCipherObfuscator::make(std::move(key), doDecrypt); |
152b0801 | 226 | } |
227 | else { | |
228 | cerr<<"Can't specify both 'key' and 'passphrase'"<<endl; | |
229 | exit(EXIT_FAILURE); | |
e2928fdb | 230 | } |
3d414a24 | 231 | |
152b0801 | 232 | for(const auto& inf : g_vm["infiles"].as<vector<string>>()) { |
233 | PcapPacketReader pr(inf); | |
e795f590 | 234 | pw.setPPR(pr); |
235 | ||
236 | while(pr.getUDPPacket()) { | |
237 | if(ntohs(pr.d_udp->uh_dport)==53 || (ntohs(pr.d_udp->uh_sport)==53 && pr.d_len > sizeof(dnsheader))) { | |
238 | dnsheader* dh=(dnsheader*)pr.d_payload; | |
488bcb39 | 239 | |
e795f590 | 240 | if (pr.d_ip->ip_v == 4){ |
241 | uint32_t *src=(uint32_t*)&pr.d_ip->ip_src; | |
242 | uint32_t *dst=(uint32_t*)&pr.d_ip->ip_dst; | |
488bcb39 | 243 | |
e795f590 | 244 | if(dh->qr) |
152b0801 | 245 | *dst=ipo->obf4(*dst); |
e795f590 | 246 | else |
152b0801 | 247 | *src=ipo->obf4(*src); |
488bcb39 | 248 | |
e795f590 | 249 | pr.d_ip->ip_sum=0; |
250 | } else if (pr.d_ip->ip_v == 6) { | |
7856684d | 251 | auto src=&pr.d_ip6->ip6_src; |
252 | auto dst=&pr.d_ip6->ip6_dst; | |
488bcb39 | 253 | |
e795f590 | 254 | if(dh->qr) |
152b0801 | 255 | *dst=ipo->obf6(*dst); |
e795f590 | 256 | else |
152b0801 | 257 | *src=ipo->obf6(*src); |
726b013f | 258 | // IPv6 checksum does not cover source/destination addresses |
e795f590 | 259 | } |
260 | pw.write(); | |
cbb0be81 | 261 | } |
ab06937b | 262 | } |
e795f590 | 263 | cerr<<"Saw "<<pr.d_correctpackets<<" correct packets, "<<pr.d_runts<<" runts, "<< pr.d_oversized<<" oversize, "<< |
264 | pr.d_nonetheripudp<<" unknown encaps"<<endl; | |
ab06937b | 265 | } |
ab06937b | 266 | } |
adc10f99 | 267 | catch(std::exception& e) |
ab06937b BH |
268 | { |
269 | cerr<<"Fatal: "<<e.what()<<endl; | |
270 | } | |
e41aadeb RG |
271 | |
272 | #else | |
273 | int main() | |
274 | { | |
275 | cerr<<"dnswasher requires ipcipher support, which is not available"<<endl; | |
276 | exit(1); | |
277 | } | |
278 | #endif /* HAVE_IPCIPHER */ |