CVE-2016-2115: s3:winbindd: use lp_client_ipc_{min,max}_protocol()
[samba.git] / source4 / dns_server / dns_update.c
index d6b764087e25968bbc0139de35eb09b40d2f910b..60a4b366a01514831a72e47e81035090c7cfcdd7 100644 (file)
 #include "librpc/gen_ndr/ndr_dnsp.h"
 #include <ldb.h>
 #include "param/param.h"
+#include "param/loadparm.h"
 #include "dsdb/samdb/samdb.h"
 #include "dsdb/common/util.h"
 #include "smbd/service_task.h"
 #include "dns_server/dns_server.h"
-#include "dns_server/dns_update.h"
+#include "auth/auth.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_DNS
 
 static WERROR dns_rr_to_dnsp(TALLOC_CTX *mem_ctx,
                             const struct dns_res_rec *rrec,
@@ -78,6 +82,9 @@ static WERROR check_one_prerequisite(struct dns_server *dns,
                        /*
                         */
                        werror = dns_lookup_records(dns, mem_ctx, dn, &ans, &acount);
+                       if (W_ERROR_EQUAL(werror, WERR_DNS_ERROR_NAME_DOES_NOT_EXIST)) {
+                               return DNS_ERR(NAME_ERROR);
+                       }
                        W_ERROR_NOT_OK_RETURN(werror);
 
                        if (acount == 0) {
@@ -87,13 +94,16 @@ static WERROR check_one_prerequisite(struct dns_server *dns,
                        /*
                         */
                        werror = dns_lookup_records(dns, mem_ctx, dn, &ans, &acount);
+                       if (W_ERROR_EQUAL(werror, WERR_DNS_ERROR_NAME_DOES_NOT_EXIST)) {
+                               return DNS_ERR(NXRRSET);
+                       }
                        if (W_ERROR_EQUAL(werror, DNS_ERR(NAME_ERROR))) {
                                return DNS_ERR(NXRRSET);
                        }
                        W_ERROR_NOT_OK_RETURN(werror);
 
                        for (i = 0; i < acount; i++) {
-                               if (ans[i].wType == pr->rr_type) {
+                               if (ans[i].wType == (enum dns_record_type) pr->rr_type) {
                                        found = true;
                                        break;
                                }
@@ -127,14 +137,15 @@ static WERROR check_one_prerequisite(struct dns_server *dns,
                        /*
                         */
                        werror = dns_lookup_records(dns, mem_ctx, dn, &ans, &acount);
+                       if (W_ERROR_EQUAL(werror, WERR_DNS_ERROR_NAME_DOES_NOT_EXIST)) {
+                               werror = WERR_OK;
+                       }
                        if (W_ERROR_EQUAL(werror, DNS_ERR(NAME_ERROR))) {
                                werror = WERR_OK;
-                               ans = NULL;
-                               acount = 0;
                        }
 
                        for (i = 0; i < acount; i++) {
-                               if (ans[i].wType == pr->rr_type) {
+                               if (ans[i].wType == (enum dns_record_type) pr->rr_type) {
                                        found = true;
                                        break;
                                }
@@ -159,6 +170,9 @@ static WERROR check_one_prerequisite(struct dns_server *dns,
        *final_result = false;
 
        werror = dns_lookup_records(dns, mem_ctx, dn, &ans, &acount);
+       if (W_ERROR_EQUAL(werror, WERR_DNS_ERROR_NAME_DOES_NOT_EXIST)) {
+               return DNS_ERR(NXRRSET);
+       }
        if (W_ERROR_EQUAL(werror, DNS_ERR(NAME_ERROR))) {
                return DNS_ERR(NXRRSET);
        }
@@ -285,9 +299,7 @@ static WERROR dns_rr_to_dnsp(TALLOC_CTX *mem_ctx,
                             const struct dns_res_rec *rrec,
                             struct dnsp_DnssrvRpcRecord *r)
 {
-       char *tmp;
-       char *txt_record_txt;
-       char *saveptr = NULL;
+       enum ndr_err_code ndr_err;
 
        if (rrec->rr_type == DNS_QTYPE_ALL) {
                return DNS_ERR(FORMAT_ERROR);
@@ -295,11 +307,9 @@ static WERROR dns_rr_to_dnsp(TALLOC_CTX *mem_ctx,
 
        ZERO_STRUCTP(r);
 
-       r->wType = rrec->rr_type;
+       r->wType = (enum dns_record_type) rrec->rr_type;
        r->dwTtlSeconds = rrec->ttl;
        r->rank = DNS_RANK_ZONE;
-       /* TODO: Autogenerate this somehow */
-       r->dwSerial = 110;
 
        /* If we get QCLASS_ANY, we're done here */
        if (rrec->rr_class == DNS_QCLASS_ANY) {
@@ -331,6 +341,10 @@ static WERROR dns_rr_to_dnsp(TALLOC_CTX *mem_ctx,
                                rrec->rdata.srv_record.target);
                W_ERROR_HAVE_NO_MEMORY(r->data.srv.nameTarget);
                break;
+       case DNS_QTYPE_PTR:
+               r->data.ptr = talloc_strdup(mem_ctx, rrec->rdata.ptr_record);
+               W_ERROR_HAVE_NO_MEMORY(r->data.ptr);
+               break;
        case DNS_QTYPE_MX:
                r->data.mx.wPriority = rrec->rdata.mx_record.preference;
                r->data.mx.nameTarget = talloc_strdup(mem_ctx,
@@ -338,28 +352,11 @@ static WERROR dns_rr_to_dnsp(TALLOC_CTX *mem_ctx,
                W_ERROR_HAVE_NO_MEMORY(r->data.mx.nameTarget);
                break;
        case DNS_QTYPE_TXT:
-               r->data.txt.count = 0;
-               r->data.txt.str = talloc_array(mem_ctx, const char *,
-                                              r->data.txt.count);
-               W_ERROR_HAVE_NO_MEMORY(r->data.txt.str);
-
-               txt_record_txt = talloc_strdup(r->data.txt.str,
-                                              rrec->rdata.txt_record.txt);
-               W_ERROR_HAVE_NO_MEMORY(txt_record_txt);
-
-               tmp = strtok_r(txt_record_txt, "\"", &saveptr);
-               while (tmp) {
-                       if (strcmp(tmp, " ") == 0) {
-                               tmp = strtok_r(NULL, "\"", &saveptr);
-                               continue;
-                       }
-                       r->data.txt.str = talloc_realloc(mem_ctx, r->data.txt.str, const char *,
-                                                       r->data.txt.count+1);
-                       r->data.txt.str[r->data.txt.count] = talloc_strdup(r->data.txt.str, tmp);
-                       W_ERROR_HAVE_NO_MEMORY(r->data.txt.str[r->data.txt.count]);
-
-                       r->data.txt.count++;
-                       tmp = strtok_r(NULL, "\"", &saveptr);
+               ndr_err = ndr_dnsp_string_list_copy(mem_ctx,
+                                                   &rrec->rdata.txt_record.txt,
+                                                   &r->data.txt);
+               if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+                       return WERR_NOMEM;
                }
 
                break;
@@ -377,13 +374,16 @@ done:
 static WERROR handle_one_update(struct dns_server *dns,
                                TALLOC_CTX *mem_ctx,
                                const struct dns_name_question *zone,
-                               const struct dns_res_rec *update)
+                               const struct dns_res_rec *update,
+                               const struct dns_server_tkey *tkey)
 {
        struct dnsp_DnssrvRpcRecord *recs = NULL;
        uint16_t rcount = 0;
        struct ldb_dn *dn;
        uint16_t i;
+       uint16_t first = 0;
        WERROR werror;
+       bool tombstoned = false;
        bool needs_add = false;
 
        DEBUG(2, ("Looking at record: \n"));
@@ -393,21 +393,13 @@ static WERROR handle_one_update(struct dns_server *dns,
 
        switch (update->rr_type) {
        case DNS_QTYPE_A:
-               break;
        case DNS_QTYPE_NS:
-               break;
        case DNS_QTYPE_CNAME:
-               break;
        case DNS_QTYPE_SOA:
-               break;
        case DNS_QTYPE_PTR:
-               break;
        case DNS_QTYPE_MX:
-               break;
        case DNS_QTYPE_AAAA:
-               break;
        case DNS_QTYPE_SRV:
-               break;
        case DNS_QTYPE_TXT:
                break;
        default:
@@ -419,24 +411,31 @@ static WERROR handle_one_update(struct dns_server *dns,
        werror = dns_name2dn(dns, mem_ctx, update->name, &dn);
        W_ERROR_NOT_OK_RETURN(werror);
 
-       werror = dns_lookup_records(dns, mem_ctx, dn, &recs, &rcount);
-       if (W_ERROR_EQUAL(werror, DNS_ERR(NAME_ERROR))) {
-               recs = NULL;
-               rcount = 0;
+       werror = dns_common_lookup(dns->samdb, mem_ctx, dn,
+                                  &recs, &rcount, &tombstoned);
+       if (W_ERROR_EQUAL(werror, WERR_DNS_ERROR_NAME_DOES_NOT_EXIST)) {
                needs_add = true;
                werror = WERR_OK;
        }
        W_ERROR_NOT_OK_RETURN(werror);
 
+       if (tombstoned) {
+               /*
+                * we need to keep the existing tombstone record
+                * and ignore it
+                */
+               first = rcount;
+       }
+
        if (update->rr_class == zone->question_class) {
                if (update->rr_type == DNS_QTYPE_CNAME) {
                        /*
                         * If there is a record in the directory
                         * that's not a CNAME, ignore update
                         */
-                       for (i = 0; i < rcount; i++) {
+                       for (i = first; i < rcount; i++) {
                                if (recs[i].wType != DNS_TYPE_CNAME) {
-                                       DEBUG(0, ("Skipping update\n"));
+                                       DEBUG(5, ("Skipping update\n"));
                                        return WERR_OK;
                                }
                                break;
@@ -447,13 +446,14 @@ static WERROR handle_one_update(struct dns_server *dns,
                         * per name, so replace everything with the new CNAME
                         */
 
-                       rcount = 1;
+                       rcount = first;
                        recs = talloc_realloc(mem_ctx, recs,
-                                       struct dnsp_DnssrvRpcRecord, rcount);
+                                       struct dnsp_DnssrvRpcRecord, rcount + 1);
                        W_ERROR_HAVE_NO_MEMORY(recs);
 
-                       werror = dns_rr_to_dnsp(recs, update, &recs[0]);
+                       werror = dns_rr_to_dnsp(recs, update, &recs[rcount]);
                        W_ERROR_NOT_OK_RETURN(werror);
+                       rcount += 1;
 
                        werror = dns_replace_records(dns, mem_ctx, dn,
                                                     needs_add, recs, rcount);
@@ -465,9 +465,9 @@ static WERROR handle_one_update(struct dns_server *dns,
                         * If there is a CNAME record for this name,
                         * ignore update
                         */
-                       for (i = 0; i < rcount; i++) {
+                       for (i = first; i < rcount; i++) {
                                if (recs[i].wType == DNS_TYPE_CNAME) {
-                                       DEBUG(0, ("Skipping update\n"));
+                                       DEBUG(5, ("Skipping update\n"));
                                        return WERR_OK;
                                }
                        }
@@ -480,7 +480,7 @@ static WERROR handle_one_update(struct dns_server *dns,
                         * serial number is smaller than existing SOA's,
                         * ignore update
                         */
-                       for (i = 0; i < rcount; i++) {
+                       for (i = first; i < rcount; i++) {
                                if (recs[i].wType == DNS_TYPE_SOA) {
                                        uint16_t n, o;
 
@@ -491,7 +491,7 @@ static WERROR handle_one_update(struct dns_server *dns,
                                         * logic for RFC2136
                                         */
                                        if (n <= o) {
-                                               DEBUG(0, ("Skipping update\n"));
+                                               DEBUG(5, ("Skipping update\n"));
                                                return WERR_OK;
                                        }
                                        found = true;
@@ -499,7 +499,7 @@ static WERROR handle_one_update(struct dns_server *dns,
                                }
                        }
                        if (!found) {
-                               DEBUG(0, ("Skipping update\n"));
+                               DEBUG(5, ("Skipping update\n"));
                                return WERR_OK;
                        }
 
@@ -511,7 +511,9 @@ static WERROR handle_one_update(struct dns_server *dns,
                                        continue;
                                }
 
-                               ZERO_STRUCT(recs[i]);
+                               recs[i] = (struct dnsp_DnssrvRpcRecord) {
+                                       .wType = DNS_TYPE_TOMBSTONE,
+                               };
                        }
 
                        werror = dns_replace_records(dns, mem_ctx, dn,
@@ -528,7 +530,7 @@ static WERROR handle_one_update(struct dns_server *dns,
                werror = dns_rr_to_dnsp(recs, update, &recs[rcount]);
                W_ERROR_NOT_OK_RETURN(werror);
 
-               for (i = 0; i < rcount; i++) {
+               for (i = first; i < rcount; i++) {
                        if (!dns_records_match(&recs[i], &recs[rcount])) {
                                continue;
                        }
@@ -550,7 +552,7 @@ static WERROR handle_one_update(struct dns_server *dns,
        } else if (update->rr_class == DNS_QCLASS_ANY) {
                if (update->rr_type == DNS_QTYPE_ALL) {
                        if (dns_name_equal(update->name, zone->name)) {
-                               for (i = 0; i < rcount; i++) {
+                               for (i = first; i < rcount; i++) {
 
                                        if (recs[i].wType == DNS_TYPE_SOA) {
                                                continue;
@@ -560,12 +562,16 @@ static WERROR handle_one_update(struct dns_server *dns,
                                                continue;
                                        }
 
-                                       ZERO_STRUCT(recs[i]);
+                                       recs[i] = (struct dnsp_DnssrvRpcRecord) {
+                                               .wType = DNS_TYPE_TOMBSTONE,
+                                       };
                                }
 
                        } else {
-                               for (i = 0; i < rcount; i++) {
-                                       ZERO_STRUCT(recs[i]);
+                               for (i = first; i < rcount; i++) {
+                                       recs[i] = (struct dnsp_DnssrvRpcRecord) {
+                                               .wType = DNS_TYPE_TOMBSTONE,
+                                       };
                                }
                        }
 
@@ -579,9 +585,11 @@ static WERROR handle_one_update(struct dns_server *dns,
                                return WERR_OK;
                        }
                }
-               for (i = 0; i < rcount; i++) {
-                       if (recs[i].wType == update->rr_type) {
-                               ZERO_STRUCT(recs[i]);
+               for (i = first; i < rcount; i++) {
+                       if (recs[i].wType == (enum dns_record_type) update->rr_type) {
+                               recs[i] = (struct dnsp_DnssrvRpcRecord) {
+                                       .wType = DNS_TYPE_TOMBSTONE,
+                               };
                        }
                }
 
@@ -606,7 +614,7 @@ static WERROR handle_one_update(struct dns_server *dns,
                        werror = dns_rr_to_dnsp(ns_rec, update, ns_rec);
                        W_ERROR_NOT_OK_RETURN(werror);
 
-                       for (i = 0; i < rcount; i++) {
+                       for (i = first; i < rcount; i++) {
                                if (dns_records_match(ns_rec, &recs[i])) {
                                        found = true;
                                        break;
@@ -623,9 +631,11 @@ static WERROR handle_one_update(struct dns_server *dns,
                werror = dns_rr_to_dnsp(del_rec, update, del_rec);
                W_ERROR_NOT_OK_RETURN(werror);
 
-               for (i = 0; i < rcount; i++) {
+               for (i = first; i < rcount; i++) {
                        if (dns_records_match(del_rec, &recs[i])) {
-                               ZERO_STRUCT(recs[i]);
+                               recs[i] = (struct dnsp_DnssrvRpcRecord) {
+                                       .wType = DNS_TYPE_TOMBSTONE,
+                               };
                        }
                }
 
@@ -641,7 +651,8 @@ static WERROR handle_updates(struct dns_server *dns,
                             TALLOC_CTX *mem_ctx,
                             const struct dns_name_question *zone,
                             const struct dns_res_rec *prereqs, uint16_t pcount,
-                            struct dns_res_rec *updates, uint16_t upd_count)
+                            struct dns_res_rec *updates, uint16_t upd_count,
+                            struct dns_server_tkey *tkey)
 {
        struct ldb_dn *zone_dn = NULL;
        WERROR werror = WERR_OK;
@@ -649,40 +660,91 @@ static WERROR handle_updates(struct dns_server *dns,
        uint16_t ri;
        TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
 
+       if (tkey != NULL) {
+               ret = ldb_set_opaque(dns->samdb, "sessionInfo", tkey->session_info);
+               if (ret != LDB_SUCCESS) {
+                       DEBUG(1, ("unable to set session info\n"));
+                       werror = DNS_ERR(SERVER_FAILURE);
+                       goto failed;
+               }
+       }
+
        werror = dns_name2dn(dns, tmp_ctx, zone->name, &zone_dn);
-       W_ERROR_NOT_OK_RETURN(werror);
+       W_ERROR_NOT_OK_GOTO(werror, failed);
 
        ret = ldb_transaction_start(dns->samdb);
        if (ret != LDB_SUCCESS) {
-               return DNS_ERR(SERVER_FAILURE);
+               werror = DNS_ERR(SERVER_FAILURE);
+               goto failed;
        }
 
        werror = check_prerequisites(dns, tmp_ctx, zone, prereqs, pcount);
        W_ERROR_NOT_OK_GOTO(werror, failed);
 
-       DEBUG(0, ("update count is %u\n", upd_count));
+       DEBUG(1, ("update count is %u\n", upd_count));
 
        for (ri = 0; ri < upd_count; ri++) {
                werror = handle_one_update(dns, tmp_ctx, zone,
-                                          &updates[ri]);
+                                          &updates[ri], tkey);
                W_ERROR_NOT_OK_GOTO(werror, failed);
        }
 
        ldb_transaction_commit(dns->samdb);
        TALLOC_FREE(tmp_ctx);
+
+       if (tkey != NULL) {
+               ldb_set_opaque(dns->samdb, "sessionInfo",
+                              system_session(dns->task->lp_ctx));
+       }
+
        return WERR_OK;
 
 failed:
        ldb_transaction_cancel(dns->samdb);
+
+       if (tkey != NULL) {
+               ldb_set_opaque(dns->samdb, "sessionInfo",
+                              system_session(dns->task->lp_ctx));
+       }
+
        TALLOC_FREE(tmp_ctx);
        return werror;
 
 }
 
+static WERROR dns_update_allowed(struct dns_server *dns,
+                                const struct dns_request_state *state,
+                                struct dns_server_tkey **tkey)
+{
+       if (lpcfg_allow_dns_updates(dns->task->lp_ctx) == DNS_UPDATE_ON) {
+               DEBUG(2, ("All updates allowed.\n"));
+               return WERR_OK;
+       }
+
+       if (lpcfg_allow_dns_updates(dns->task->lp_ctx) == DNS_UPDATE_OFF) {
+               DEBUG(2, ("Updates disabled.\n"));
+               return DNS_ERR(REFUSED);
+       }
+
+       if (state->authenticated == false ) {
+               DEBUG(2, ("Update not allowed for unsigned packet.\n"));
+               return DNS_ERR(REFUSED);
+       }
+
+        *tkey = dns_find_tkey(dns->tkeys, state->key_name);
+       if (*tkey == NULL) {
+               DEBUG(0, ("Authenticated, but key not found. Something is wrong.\n"));
+               return DNS_ERR(REFUSED);
+       }
+
+       return WERR_OK;
+}
+
+
 WERROR dns_server_process_update(struct dns_server *dns,
-                                struct dns_request_state *state,
+                                const struct dns_request_state *state,
                                 TALLOC_CTX *mem_ctx,
-                                struct dns_name_packet *in,
+                                const struct dns_name_packet *in,
                                 struct dns_res_rec **prereqs,    uint16_t *prereq_count,
                                 struct dns_res_rec **updates,    uint16_t *update_count,
                                 struct dns_res_rec **additional, uint16_t *arcount)
@@ -691,6 +753,7 @@ WERROR dns_server_process_update(struct dns_server *dns,
        const struct dns_server_zone *z;
        size_t host_part_len = 0;
        WERROR werror = DNS_ERR(NOT_IMPLEMENTED);
+       struct dns_server_tkey *tkey = NULL;
 
        if (in->qdcount != 1) {
                return DNS_ERR(FORMAT_ERROR);
@@ -719,13 +782,13 @@ WERROR dns_server_process_update(struct dns_server *dns,
        }
 
        if (z == NULL) {
-               DEBUG(0, ("We're not authorative for this zone\n"));
+               DEBUG(1, ("We're not authoritative for this zone\n"));
                return DNS_ERR(NOTAUTH);
        }
 
        if (host_part_len != 0) {
                /* TODO: We need to delegate this one */
-               DEBUG(0, ("Would have to delegate zones.\n"));
+               DEBUG(1, ("Would have to delegate zone '%s'.\n", zone->name));
                return DNS_ERR(NOT_IMPLEMENTED);
        }
 
@@ -735,12 +798,9 @@ WERROR dns_server_process_update(struct dns_server *dns,
                                     *prereq_count);
        W_ERROR_NOT_OK_RETURN(werror);
 
-       /* TODO: Check if update is allowed, we probably want "always",
-        * key-based GSSAPI, key-based bind-style TSIG and "never" as
-        * smb.conf options. */
-       if (lpcfg_allow_dns_updates(dns->task->lp_ctx) != DNS_UPDATE_ON) {
-               DEBUG(0, ("Update not allowed.\n"));
-               return DNS_ERR(REFUSED);
+       werror = dns_update_allowed(dns, state, &tkey);
+       if (!W_ERROR_IS_OK(werror)) {
+               return werror;
        }
 
        *update_count = in->nscount;
@@ -748,9 +808,8 @@ WERROR dns_server_process_update(struct dns_server *dns,
        werror = update_prescan(in->questions, *updates, *update_count);
        W_ERROR_NOT_OK_RETURN(werror);
 
-
        werror = handle_updates(dns, mem_ctx, in->questions, *prereqs,
-                               *prereq_count, *updates, *update_count);
+                               *prereq_count, *updates, *update_count, tkey);
        W_ERROR_NOT_OK_RETURN(werror);
 
        return werror;