]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
layer/iterate: do DNS 0x20 unless in safe mode
authorMarek Vavruša <marek.vavrusa@nic.cz>
Wed, 24 Jun 2015 17:33:21 +0000 (19:33 +0200)
committerMarek Vavruša <marek.vavrusa@nic.cz>
Wed, 24 Jun 2015 17:34:00 +0000 (19:34 +0200)
DNS 0x20 https://tools.ietf.org/html/draft-vixie-dnsext-dns0x20-00
is a way to add more randomness into queries to make spoofing tougher
this implementation provides up to 32 bits of randomness to QNAME,
which is more than enough for most names (it is possible to add a
maximum of 1 bit of entropy per alphanumeric character, so it's not very
efficient with shorter names)

fixes #27

lib/layer/iterate.c
lib/rplan.h

index af27d5c58f31a5d951b3df567ac37614ba60f5a4..865ded6c114bd678a50410729900533f1ecaca20 100644 (file)
@@ -14,6 +14,7 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <ctype.h>
 #include <sys/time.h>
 
 #include <libknot/descriptor.h>
@@ -56,11 +57,39 @@ static const knot_dname_t *minimized_qname(struct kr_query *query, uint16_t *qty
        return qname;
 }
 
+/* Randomize QNAME letter case.
+ * This adds 32 bits of randomness at maximum, but that's more than an average domain name length.
+ * https://tools.ietf.org/html/draft-vixie-dnsext-dns0x20-00
+ */
+static void randomized_qname_case(knot_dname_t *qname, unsigned secret)
+{
+       unsigned k = 0;
+       while (*qname != '\0') {
+               for (unsigned i = *qname; i--;) {
+                       int chr = qname[i + 1];
+                       if (isalpha(chr)) {
+                               if (secret & (1 << k)) {
+                                       qname[i + 1] ^= 0x20;
+                               }
+                               k = (k + 1) % (sizeof(secret) * CHAR_BIT);
+                       }
+               }
+               qname = (uint8_t *)knot_wire_next_label(qname, NULL);
+       }
+}
+
 /** Answer is paired to query. */
 static bool is_paired_to_query(const knot_pkt_t *answer, struct kr_query *query)
 {
        uint16_t qtype = query->stype;
-       const knot_dname_t *qname = minimized_qname(query, &qtype);
+       const knot_dname_t *qname_min = minimized_qname(query, &qtype);
+
+       /* Construct expected randomized QNAME */
+       uint8_t qname[KNOT_DNAME_MAXLEN];
+       knot_dname_to_wire(qname, qname_min, sizeof(qname));
+       if (!(query->flags & QUERY_CACHED)) {
+               randomized_qname_case(qname, query->secret);
+       }
 
        return query->id      == knot_wire_get_id(answer->wire) &&
               (query->sclass == KNOT_CLASS_ANY || query->sclass  == knot_pkt_qclass(answer)) &&
@@ -353,6 +382,11 @@ int kr_make_query(struct kr_query *query, knot_pkt_t *pkt)
                return ret;
        }
 
+       /* Randomize query case (if not in safemode) */
+       query->secret = (query->flags & QUERY_SAFEMODE) ? 0 : kr_rand_uint(UINT32_MAX);
+       knot_dname_t *qname_raw = (knot_dname_t *)knot_pkt_qname(pkt);
+       randomized_qname_case(qname_raw, query->secret);
+
        /* Query built, expect answer. */
        query->id = kr_rand_uint(UINT16_MAX);
        knot_wire_set_id(pkt->wire, query->id);
@@ -428,6 +462,10 @@ static int resolve(knot_layer_t *ctx, knot_pkt_t *pkt)
                return KNOT_STATE_DONE;
        }
 
+       /* Packet cleared, normalize QNAME. */
+       knot_dname_t *qname_raw = (knot_dname_t *)knot_pkt_qname(pkt);
+       knot_dname_to_lower(qname_raw);
+
        /* Check response code. */
 #ifndef NDEBUG
        lookup_table_t *rcode = lookup_by_id(knot_rcode_names, knot_wire_get_rcode(pkt->wire));
index 447a3956e35139edf1ce78f2e8ebeb76b131d7b1..d632fcd13b84add60b3382f7bbe8069248f3b837 100644 (file)
@@ -61,6 +61,7 @@ struct kr_query {
        uint16_t sclass;
        uint16_t id;
        uint16_t flags;
+       unsigned secret;
 };
 
 /**