]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
JSON API: refuse modifying out-of-zone records 1285/head
authorChristian Hofstaedtler <christian@hofstaedtler.name>
Mon, 17 Feb 2014 13:49:37 +0000 (14:49 +0100)
committerChristian Hofstaedtler <christian@hofstaedtler.name>
Mon, 17 Feb 2014 13:49:37 +0000 (14:49 +0100)
pdns/ws-auth.cc
regression-tests.api/test_RecursorConfig.py
regression-tests.api/test_Zones.py

index 2aed7a07485883fa9b56df48c41bd003d8fa9680..06678def026d841ea92d651d90fbc840488d52bc 100644 (file)
@@ -522,6 +522,10 @@ static void apiServerZoneRRset(HttpRequest* req, HttpResponse* resp) {
   qtype = stringFromJson(document, "type");
   changetype = toUpper(stringFromJson(document, "changetype"));
 
+  string dotsuffix = "." + zonename;
+  if(!iends_with(qname, dotsuffix) && qname != zonename)
+    throw ApiException("RRset "+qname+" IN "+qtype.getName()+": Name is out of zone");
+
   if (changetype == "DELETE") {
     // delete all matching qname/qtype RRs
     di.backend->replaceRRSet(di.id, qname, qtype, vector<DNSResourceRecord>());
@@ -546,13 +550,16 @@ static void apiServerZoneRRset(HttpRequest* req, HttpResponse* resp) {
       if(rr.qtype.getCode() == QType::MX || rr.qtype.getCode() == QType::SRV)
         rr.content = lexical_cast<string>(rr.priority)+" "+rr.content;
 
+      if(rr.qname != qname || rr.qtype != qtype)
+        throw ApiException("Record "+rr.qname+" IN "+rr.qtype.getName()+" "+rr.content+": Record bundled with wrong RRset");
+
       try {
         shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(rr.qtype.getCode(), 1, rr.content));
         string tmp = drc->serialize(rr.qname);
       }
       catch(std::exception& e)
       {
-        throw ApiException("Record "+rr.qname+" IN " +rr.qtype.getName()+ " " + rr.content+": "+e.what());
+        throw ApiException("Record "+rr.qname+" IN "+rr.qtype.getName()+" "+rr.content+": "+e.what());
       }
     }
     // Actually store the change.
index 30ec071691e49f9c64bd156c0d3ad3efa53bdaf5..ede214af75b7a3490f6b6b29c29959af1aa6d745 100644 (file)
@@ -20,3 +20,13 @@ class RecursorConfig(ApiTestCase):
         self.assertSuccessJson(r)
         data = r.json()
         self.assertEquals("127.0.0.1/32", data["value"][0])
+
+    def test_ConfigAllowFromReplaceError(self):
+        """Test the error case, should return 422."""
+        payload = {'value': ["abcdefgh"]}
+        r = self.session.put(
+            self.url("/servers/localhost/config/allow-from"),
+            data=json.dumps(payload),
+            headers={'content-type': 'application/json'})
+        self.assertEquals(r.status_code, 422)
+        data = r.json()
index 7e98a8ffe9107b08b536f332f74596cc19620bd3..1a89ca3abf9061db0a395acc6e586f0b8a639898 100644 (file)
@@ -211,6 +211,98 @@ class AuthZones(ApiTestCase):
             headers={'content-type': 'application/json'})
         self.assertSuccessJson(r)
 
+    def test_ZoneRRUpdateQTypeMismatch(self):
+        payload, zone = self.create_zone()
+        name = payload['name']
+        # replace with qtype mismatch
+        payload = {
+            'changetype': 'replace',
+            'name': name,
+            'type': 'A',
+            'records': [
+                {
+                    "name": name,
+                    "type": "NS",
+                    "priority": 0,
+                    "ttl": 3600,
+                    "content": "ns1.bar.com",
+                    "disabled": False
+                }
+            ]
+        }
+        r = self.session.patch(
+            self.url("/servers/localhost/zones/" + name + "/rrset"),
+            data=json.dumps(payload),
+            headers={'content-type': 'application/json'})
+        self.assertEquals(r.status_code, 422)
+
+    def test_ZoneRRUpdateQNameMismatch(self):
+        payload, zone = self.create_zone()
+        name = payload['name']
+        # replace with qname mismatch
+        payload = {
+            'changetype': 'replace',
+            'name': name,
+            'type': 'NS',
+            'records': [
+                {
+                    "name": 'blah.'+name,
+                    "type": "NS",
+                    "priority": 0,
+                    "ttl": 3600,
+                    "content": "ns1.bar.com",
+                    "disabled": False
+                }
+            ]
+        }
+        r = self.session.patch(
+            self.url("/servers/localhost/zones/" + name + "/rrset"),
+            data=json.dumps(payload),
+            headers={'content-type': 'application/json'})
+        self.assertEquals(r.status_code, 422)
+
+    def test_ZoneRRUpdateOutOfZone(self):
+        payload, zone = self.create_zone()
+        name = payload['name']
+        # replace with qname mismatch
+        payload = {
+            'changetype': 'replace',
+            'name': 'not-in-zone',
+            'type': 'NS',
+            'records': [
+                {
+                    "name": name,
+                    "type": "NS",
+                    "priority": 0,
+                    "ttl": 3600,
+                    "content": "ns1.bar.com",
+                    "disabled": False
+                }
+            ]
+        }
+        r = self.session.patch(
+            self.url("/servers/localhost/zones/" + name + "/rrset"),
+            data=json.dumps(payload),
+            headers={'content-type': 'application/json'})
+        self.assertEquals(r.status_code, 422)
+        self.assertIn('out of zone', r.json()['error'])
+
+    def test_ZoneRRDeleteOutOfZone(self):
+        payload, zone = self.create_zone()
+        name = payload['name']
+        # replace with qname mismatch
+        payload = {
+            'changetype': 'delete',
+            'name': 'not-in-zone',
+            'type': 'NS'
+        }
+        r = self.session.patch(
+            self.url("/servers/localhost/zones/" + name + "/rrset"),
+            data=json.dumps(payload),
+            headers={'content-type': 'application/json'})
+        self.assertEquals(r.status_code, 422)
+        self.assertIn('out of zone', r.json()['error'])
+
 
 @unittest.skipIf(not isRecursor(), "Not applicable")
 class RecursorZones(ApiTestCase):