#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
+#include "base64.hh"
#include "dnsparser.hh"
#include "sstuff.hh"
#include "misc.hh"
#include "base32.hh"
#include "dnssecinfra.hh"
#include <boost/foreach.hpp>
+#include "dns_random.hh"
+#include "gss_context.hh"
StatBag S;
+bool validateTSIG(const string& message, const TSIGHashEnum& algo, const string& key, const string& secret, const TSIGRecordContent *trc) {
+ int64_t now = time(0);
+ if(abs((int64_t)trc->d_time - now) > trc->d_fudge) {
+ cerr<<"TSIG (key '"<<key<<"') time delta "<< abs(trc->d_time - now)<<" > 'fudge' "<<trc->d_fudge<<endl;
+ return false;
+ }
+ if (algo == TSIG_GSS) {
+ // authorization is done later
+ GssContext gssctx(key);
+ if (!gssctx.valid()) {
+ cerr<<"no context"<<endl;
+ return false;
+ }
+ if (!gssctx.verify(message, trc->d_mac)) {
+ cerr<<"invalid mac"<<endl;
+ return false;
+ }
+ return true;
+ }
+ return calculateHMAC(secret, message, algo) == trc->d_mac;
+}
+
+
int main(int argc, char** argv)
try
{
if(argc < 4) {
- cerr<<"Syntax: saxfr IP-address port zone [showdetails] [showflags] [unhash]"<<endl;;
+ cerr<<"Syntax: saxfr IP-address port zone [showdetails] [showflags] [unhash] [gss:remote-principal] [tsig:keyname:algo:secret]"<<endl;
exit(EXIT_FAILURE);
}
bool showdetails=false;
bool showflags=false;
bool unhash=false;
+ bool gss=false;
+ bool tsig=false;
+ TSIGHashEnum tsig_algo;
+ string tsig_key;
+ string tsig_secret;
+ string tsigprevious;
+ string remote_principal;
if (argc > 4) {
for(int i=4; i<argc; i++) {
showflags=true;
if (strcmp(argv[i], "unhash") == 0)
unhash=true;
+ if (strncmp(argv[i], "gss:",4) == 0) {
+ gss=true;
+ tsig=true;
+ tsig_algo=TSIG_GSS;
+ remote_principal = string(argv[i]+4);
+ if (remote_principal.empty()) {
+ cerr<<"Remote principal is required"<<endl;
+ exit(EXIT_FAILURE);
+ }
+ }
+ if (strncmp(argv[i], "tsig:",5) == 0) {
+ vector<string> parts;
+ tsig=true;
+ stringtok(parts, argv[i], ":");
+ if (parts.size()!=4) {
+ cerr<<"Invalid syntax for tsig"<<endl;
+ exit(EXIT_FAILURE);
+ }
+ if (!getTSIGHashEnum(parts[2], tsig_algo)) {
+ cerr<<"Cannot understand TSIG algorithm '"<<parts[1]<<"'"<<endl;
+ exit(EXIT_FAILURE);
+ }
+ tsig_key = parts[1];
+ if (tsig_key.size()==0) {
+ cerr<<"Key name must be set for tsig"<<endl;
+ exit(EXIT_FAILURE);
+ }
+ if (B64Decode(parts[3], tsig_secret)) {
+ cerr<<"Secret must be base64 encoded"<<endl;
+ exit(EXIT_FAILURE);
+ }
+ if (tsig_secret.size()==0) {
+ cerr<<"Secret must be set for tsig"<<endl;
+ exit(EXIT_FAILURE);
+ }
+ }
}
}
reportAllTypes();
+ dns_random_init("0123456789abcdef");
vector<uint8_t> packet;
+ uint16_t len;
+ ComboAddress dest(argv[1] + (*argv[1]=='@'), atoi(argv[2]));
+ Socket sock(dest.sin4.sin_family, SOCK_STREAM);
+ sock.connect(dest);
+
+ if (gss) {
+#ifndef ENABLE_GSS_TSIG
+ cerr<<"No GSS support compiled in"<<endl;
+ exit(EXIT_FAILURE);
+#else
+ string input,output;
+ GssContext gssctx;
+ gssctx.generateLabel(argv[3]);
+ gssctx.setPeerPrincipal(remote_principal);
+
+ while(gssctx.init(input, output) && gssctx.valid() == false) {
+ input="";
+ DNSPacketWriter pwtkey(packet, gssctx.getLabel(), QType::TKEY, QClass::ANY);
+ TKEYRecordContent tkrc;
+ tkrc.d_algo = "gss-tsig";
+ tkrc.d_inception = time((time_t*)NULL);
+ tkrc.d_expiration = tkrc.d_inception+15;
+ tkrc.d_mode = 3;
+ tkrc.d_error = 0;
+ tkrc.d_keysize = output.size();
+ tkrc.d_key = output;
+ tkrc.d_othersize = 0;
+ pwtkey.getHeader()->id = dns_random(0xffff);
+ pwtkey.startRecord(gssctx.getLabel(), QType::TKEY, 3600, QClass::ANY, DNSPacketWriter::ADDITIONAL, false);
+ tkrc.toPacket(pwtkey);
+ pwtkey.commit();
+ BOOST_FOREACH(const string& msg, gssctx.getErrorStrings()) {
+ cerr<<msg<<endl;
+ }
+
+ len = htons(packet.size());
+ if(sock.write((char *) &len, 2) != 2)
+ throw PDNSException("tcp write failed");
+ sock.writen(string((char*)&*packet.begin(), (char*)&*packet.end()));
+ if(sock.read((char *) &len, 2) != 2)
+ throw PDNSException("tcp read failed");
+
+ len=ntohs(len);
+ char *creply = new char[len];
+ int n=0;
+ int numread;
+ while(n<len) {
+ numread=sock.read(creply+n, len-n);
+ if(numread<0)
+ throw PDNSException("tcp read failed");
+ n+=numread;
+ }
+
+ MOADNSParser mdp(string(creply, len));
+ if (mdp.d_header.rcode != 0) {
+ throw PDNSException(string("Remote server refused: ") + boost::lexical_cast<string>(mdp.d_header.rcode));
+ }
+ for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i!=mdp.d_answers.end(); ++i) {
+ if(i->first.d_type != QType::TKEY) continue;
+ // recover TKEY record
+ tkrc = TKEYRecordContent(i->first.d_content->getZoneRepresentation());
+ input = tkrc.d_key;
+ }
+ }
+
+ if (gssctx.valid() == false) {
+ cerr<<"Could not create GSS context"<<endl;
+ exit(EXIT_FAILURE);
+ }
+
+ tsig_key = gssctx.getLabel();
+#endif
+ }
+
DNSPacketWriter pw(packet, argv[3], 252);
+ pw.getHeader()->id = dns_random(0xffff);
+
+ if (tsig) {
+ TSIGRecordContent trc;
+ trc.d_algoName = getTSIGAlgoName(tsig_algo);
+ trc.d_time = time((time_t*)NULL);
+ trc.d_fudge = 300;
+ trc.d_origID=ntohs(pw.getHeader()->id);
+ trc.d_eRcode=0;
+ addTSIG(pw, &trc, tsig_key, tsig_secret, "", false);
+ }
- ComboAddress dest(argv[1] + (*argv[1]=='@'), atoi(argv[2]));
- Socket sock(dest.sin4.sin_family, SOCK_STREAM);
- sock.connect(dest);
- uint16_t len;
len = htons(packet.size());
if(sock.write((char *) &len, 2) != 2)
throw PDNSException("tcp write failed");
NSEC3PARAMRecordContent ns3pr;
while(soacount<2) {
+ TSIGRecordContent trc;
+
if(sock.read((char *) &len, 2) != 2)
throw PDNSException("tcp read failed");
n+=numread;
}
- MOADNSParser mdp(string(creply, len));
+ string packet = string(creply, len);
+
+ MOADNSParser mdp(packet);
+ if (mdp.d_header.rcode != 0) {
+ throw PDNSException(string("Remote server refused: ") + boost::lexical_cast<string>(mdp.d_header.rcode));
+ }
for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i!=mdp.d_answers.end(); ++i) {
+ if (i->first.d_type == QType::TSIG) {
+ string message;
+ if (!tsig) {
+ std::cerr<<"Unexpected TSIG signature in data"<<endl;
+ }
+ trc = TSIGRecordContent(i->first.d_content->getZoneRepresentation());
+ continue;
+ }
if(i->first.d_type == QType::SOA)
{
++soacount;
}while(chopOff(shorter));
}
+
delete[] creply;
}
}
}
+catch(PDNSException &e2) {
+ cerr<<"Fatal: "<<e2.reason<<endl;
+}
catch(std::exception &e)
{
cerr<<"Fatal: "<<e.what()<<endl;