]> granicus.if.org Git - pdns/commitdiff
Add support for GSS and TSIG for AXFR
authorAki Tuomi <cmouse@cmouse.fi>
Sat, 23 May 2015 13:59:45 +0000 (16:59 +0300)
committerAki Tuomi <cmouse@cmouse.fi>
Thu, 28 May 2015 14:42:23 +0000 (17:42 +0300)
pdns/saxfr.cc

index b2fbf89252aed3e472aa6333d5e9f9aed0ec8369..e572b0bcd6fc5fef0df99049258b7962283c78d1 100644 (file)
@@ -1,6 +1,7 @@
 #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++) {
@@ -33,19 +66,137 @@ try
         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");
@@ -60,6 +211,8 @@ try
   NSEC3PARAMRecordContent ns3pr;
 
   while(soacount<2) {
+    TSIGRecordContent trc;
+
     if(sock.read((char *) &len, 2) != 2)
       throw PDNSException("tcp read failed");
 
@@ -74,8 +227,21 @@ try
       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;
@@ -134,6 +300,7 @@ try
       }while(chopOff(shorter));
 
     }
+
     delete[] creply;
   }
 
@@ -159,6 +326,9 @@ try
   }
 
 }
+catch(PDNSException &e2) {
+  cerr<<"Fatal: "<<e2.reason<<endl;
+}
 catch(std::exception &e)
 {
   cerr<<"Fatal: "<<e.what()<<endl;