1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-23 17:34:34 +03:00
samba-mirror/source4/rpc_server/dnsserver/dnsdb.c
Aaron Haslett 50d961c1a2 dns: dns record scavenging function (without task)
DNS record scavenging function with testing.  The logic of the custom match rule
in previous commit is inverted so that calculations using zone properties can
be taken out of the function's inner loop. Periodic task to come.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=10812

Signed-off-by: Aaron Haslett <aaronhaslett@catalyst.net.nz>
Reviewed-by: Gary Lockyer <gary@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
2018-07-12 04:31:55 +02:00

1137 lines
29 KiB
C

/*
Unix SMB/CIFS implementation.
DNS Server
Copyright (C) Amitay Isaacs 2011
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
#include "dnsserver.h"
#include "lib/util/dlinklist.h"
#include "librpc/gen_ndr/ndr_dnsp.h"
#include "librpc/gen_ndr/ndr_security.h"
#include "librpc/gen_ndr/ndr_misc.h"
#include "dsdb/samdb/samdb.h"
#include "libcli/security/security.h"
#include "dsdb/common/util.h"
/* There are only 2 fixed partitions for DNS */
struct dnsserver_partition *dnsserver_db_enumerate_partitions(TALLOC_CTX *mem_ctx,
struct dnsserver_serverinfo *serverinfo,
struct ldb_context *samdb)
{
struct dnsserver_partition *partitions, *p;
partitions = NULL;
/* Domain partition */
p = talloc_zero(mem_ctx, struct dnsserver_partition);
if (p == NULL) {
goto failed;
}
p->partition_dn = ldb_dn_new(p, samdb, serverinfo->pszDomainDirectoryPartition);
if (p->partition_dn == NULL) {
goto failed;
}
p->pszDpFqdn = samdb_dn_to_dns_domain(p, p->partition_dn);
p->dwDpFlags = DNS_DP_AUTOCREATED | DNS_DP_DOMAIN_DEFAULT | DNS_DP_ENLISTED;
p->is_forest = false;
DLIST_ADD_END(partitions, p);
/* Forest Partition */
p = talloc_zero(mem_ctx, struct dnsserver_partition);
if (p == NULL) {
goto failed;
}
p->partition_dn = ldb_dn_new(p, samdb, serverinfo->pszForestDirectoryPartition);
if (p->partition_dn == NULL) {
goto failed;
}
p->pszDpFqdn = samdb_dn_to_dns_domain(p, p->partition_dn);
p->dwDpFlags = DNS_DP_AUTOCREATED | DNS_DP_FOREST_DEFAULT | DNS_DP_ENLISTED;
p->is_forest = true;
DLIST_ADD_END(partitions, p);
return partitions;
failed:
return NULL;
}
/* Search for all dnsZone records */
struct dnsserver_zone *dnsserver_db_enumerate_zones(TALLOC_CTX *mem_ctx,
struct ldb_context *samdb,
struct dnsserver_partition *p)
{
TALLOC_CTX *tmp_ctx;
const char * const attrs[] = {"name", "dNSProperty", NULL};
struct ldb_dn *dn;
struct ldb_result *res;
struct dnsserver_zone *zones, *z;
int i, j, ret;
tmp_ctx = talloc_new(mem_ctx);
if (tmp_ctx == NULL) {
return NULL;
}
dn = ldb_dn_copy(tmp_ctx, p->partition_dn);
if (dn == NULL) {
goto failed;
}
if (!ldb_dn_add_child_fmt(dn, "CN=MicrosoftDNS")) {
goto failed;
}
ret = ldb_search(samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
attrs, "(objectClass=dnsZone)");
if (ret != LDB_SUCCESS) {
DEBUG(0, ("dnsserver: Failed to find DNS Zones in %s\n",
ldb_dn_get_linearized(dn)));
goto failed;
}
zones = NULL;
for(i=0; i<res->count; i++) {
char *name;
struct ldb_message_element *element = NULL;
struct dnsp_DnsProperty *props = NULL;
enum ndr_err_code err;
z = talloc_zero(mem_ctx, struct dnsserver_zone);
if (z == NULL) {
goto failed;
}
z->partition = p;
name = talloc_strdup(z,
ldb_msg_find_attr_as_string(res->msgs[i],
"name", NULL));
if (strcmp(name, "..TrustAnchors") == 0) {
talloc_free(z);
continue;
}
if (strcmp(name, "RootDNSServers") == 0) {
talloc_free(name);
z->name = talloc_strdup(z, ".");
} else {
z->name = name;
}
z->zone_dn = talloc_steal(z, res->msgs[i]->dn);
DLIST_ADD_END(zones, z);
DEBUG(2, ("dnsserver: Found DNS zone %s\n", z->name));
element = ldb_msg_find_element(res->msgs[i], "dNSProperty");
if(element != NULL){
props = talloc_zero_array(tmp_ctx,
struct dnsp_DnsProperty,
element->num_values);
for (j = 0; j < element->num_values; j++ ) {
err = ndr_pull_struct_blob(
&(element->values[j]),
mem_ctx,
&props[j],
(ndr_pull_flags_fn_t)
ndr_pull_dnsp_DnsProperty);
if (!NDR_ERR_CODE_IS_SUCCESS(err)){
goto failed;
}
}
z->tmp_props = props;
z->num_props = element->num_values;
}
}
return zones;
failed:
talloc_free(tmp_ctx);
return NULL;
}
/* Find DNS partition information */
struct dnsserver_partition_info *dnsserver_db_partition_info(TALLOC_CTX *mem_ctx,
struct ldb_context *samdb,
struct dnsserver_partition *p)
{
const char * const attrs[] = { "instanceType", "msDs-masteredBy", NULL };
const char * const attrs_none[] = { NULL };
struct ldb_result *res;
struct ldb_message_element *el;
struct ldb_dn *dn;
struct dnsserver_partition_info *partinfo;
int i, ret, instance_type;
TALLOC_CTX *tmp_ctx;
tmp_ctx = talloc_new(mem_ctx);
if (tmp_ctx == NULL) {
return NULL;
}
partinfo = talloc_zero(mem_ctx, struct dnsserver_partition_info);
if (partinfo == NULL) {
talloc_free(tmp_ctx);
return NULL;
}
/* Search for the active replica and state */
ret = ldb_search(samdb, tmp_ctx, &res, p->partition_dn, LDB_SCOPE_BASE,
attrs, NULL);
if (ret != LDB_SUCCESS || res->count != 1) {
goto failed;
}
/* Set the state of the partition */
instance_type = ldb_msg_find_attr_as_int(res->msgs[0], "instanceType", -1);
if (instance_type == -1) {
partinfo->dwState = DNS_DP_STATE_UNKNOWN;
} else if (instance_type & INSTANCE_TYPE_NC_COMING) {
partinfo->dwState = DNS_DP_STATE_REPL_INCOMING;
} else if (instance_type & INSTANCE_TYPE_NC_GOING) {
partinfo->dwState = DNS_DP_STATE_REPL_OUTGOING;
} else {
partinfo->dwState = DNS_DP_OKAY;
}
el = ldb_msg_find_element(res->msgs[0], "msDs-masteredBy");
if (el == NULL) {
partinfo->dwReplicaCount = 0;
partinfo->ReplicaArray = NULL;
} else {
partinfo->dwReplicaCount = el->num_values;
partinfo->ReplicaArray = talloc_zero_array(partinfo,
struct DNS_RPC_DP_REPLICA *,
el->num_values);
if (partinfo->ReplicaArray == NULL) {
goto failed;
}
for (i=0; i<el->num_values; i++) {
partinfo->ReplicaArray[i] = talloc_zero(partinfo,
struct DNS_RPC_DP_REPLICA);
if (partinfo->ReplicaArray[i] == NULL) {
goto failed;
}
partinfo->ReplicaArray[i]->pszReplicaDn = talloc_strdup(
partinfo,
(const char *)el->values[i].data);
if (partinfo->ReplicaArray[i]->pszReplicaDn == NULL) {
goto failed;
}
}
}
talloc_free(res);
/* Search for cross-reference object */
dn = ldb_dn_copy(tmp_ctx, ldb_get_config_basedn(samdb));
if (dn == NULL) {
goto failed;
}
ret = ldb_search(samdb, tmp_ctx, &res, dn, LDB_SCOPE_DEFAULT, attrs_none,
"(nCName=%s)", ldb_dn_get_linearized(p->partition_dn));
if (ret != LDB_SUCCESS || res->count != 1) {
goto failed;
}
partinfo->pszCrDn = talloc_strdup(partinfo, ldb_dn_get_linearized(res->msgs[0]->dn));
if (partinfo->pszCrDn == NULL) {
goto failed;
}
talloc_free(res);
talloc_free(tmp_ctx);
return partinfo;
failed:
talloc_free(tmp_ctx);
talloc_free(partinfo);
return NULL;
}
/* Increment serial number and update timestamp */
static unsigned int dnsserver_update_soa(TALLOC_CTX *mem_ctx,
struct ldb_context *samdb,
struct dnsserver_zone *z)
{
const char * const attrs[] = { "dnsRecord", NULL };
struct ldb_result *res;
struct dnsp_DnssrvRpcRecord rec;
struct ldb_message_element *el;
enum ndr_err_code ndr_err;
int ret, i, serial = -1;
ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs,
"(&(objectClass=dnsNode)(name=@))");
if (ret != LDB_SUCCESS || res->count == 0) {
return -1;
}
el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
if (el == NULL) {
return -1;
}
for (i=0; i<el->num_values; i++) {
ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec,
(ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
continue;
}
if (rec.wType == DNS_TYPE_SOA) {
serial = rec.data.soa.serial + 1;
rec.dwSerial = serial;
rec.dwTimeStamp = 0;
rec.data.soa.serial = serial;
ndr_err = ndr_push_struct_blob(&el->values[i], mem_ctx, &rec,
(ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
return -1;
}
break;
}
}
if (serial != -1) {
el->flags = LDB_FLAG_MOD_REPLACE;
ret = ldb_modify(samdb, res->msgs[0]);
if (ret != LDB_SUCCESS) {
return -1;
}
}
return serial;
}
/* Add DNS record to the database */
static WERROR dnsserver_db_do_add_rec(TALLOC_CTX *mem_ctx,
struct ldb_context *samdb,
struct ldb_dn *dn,
int num_rec,
struct dnsp_DnssrvRpcRecord *rec)
{
struct ldb_message *msg;
struct ldb_val v;
int ret;
enum ndr_err_code ndr_err;
int i;
msg = ldb_msg_new(mem_ctx);
W_ERROR_HAVE_NO_MEMORY(msg);
msg->dn = dn;
ret = ldb_msg_add_string(msg, "objectClass", "dnsNode");
if (ret != LDB_SUCCESS) {
return WERR_NOT_ENOUGH_MEMORY;
}
if (num_rec > 0 && rec) {
for (i=0; i<num_rec; i++) {
ndr_err = ndr_push_struct_blob(&v, mem_ctx, &rec[i],
(ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
return WERR_GEN_FAILURE;
}
ret = ldb_msg_add_value(msg, "dnsRecord", &v, NULL);
if (ret != LDB_SUCCESS) {
return WERR_NOT_ENOUGH_MEMORY;
}
}
}
ret = ldb_add(samdb, msg);
if (ret != LDB_SUCCESS) {
return WERR_INTERNAL_DB_ERROR;
}
return WERR_OK;
}
/* Add dnsNode record to the database with DNS record */
WERROR dnsserver_db_add_empty_node(TALLOC_CTX *mem_ctx,
struct ldb_context *samdb,
struct dnsserver_zone *z,
const char *name)
{
const char * const attrs[] = { "name", NULL };
struct ldb_result *res;
struct ldb_dn *dn;
char *encoded_name = ldb_binary_encode_string(mem_ctx, name);
int ret;
ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_BASE, attrs,
"(&(objectClass=dnsNode)(name=%s))",
encoded_name);
if (ret != LDB_SUCCESS) {
return WERR_INTERNAL_DB_ERROR;
}
if (res->count > 0) {
talloc_free(res);
return WERR_DNS_ERROR_RECORD_ALREADY_EXISTS;
}
dn = ldb_dn_copy(mem_ctx, z->zone_dn);
W_ERROR_HAVE_NO_MEMORY(dn);
if (!ldb_dn_add_child_fmt(dn, "DC=%s", name)) {
return WERR_NOT_ENOUGH_MEMORY;
}
return dnsserver_db_do_add_rec(mem_ctx, samdb, dn, 0, NULL);
}
/* Add a DNS record */
WERROR dnsserver_db_add_record(TALLOC_CTX *mem_ctx,
struct ldb_context *samdb,
struct dnsserver_zone *z,
const char *name,
struct DNS_RPC_RECORD *add_record)
{
const char * const attrs[] = { "dnsRecord", "dNSTombstoned", NULL };
struct ldb_result *res;
struct dnsp_DnssrvRpcRecord *rec = NULL;
struct ldb_message_element *el;
struct ldb_dn *dn;
enum ndr_err_code ndr_err;
int ret, i;
int serial;
WERROR werr;
bool was_tombstoned = false;
char *encoded_name = ldb_binary_encode_string(mem_ctx, name);
werr = dns_to_dnsp_convert(mem_ctx, add_record, &rec, true);
if (!W_ERROR_IS_OK(werr)) {
return werr;
}
/* Set the correct rank for the record. */
if (z->zoneinfo->dwZoneType == DNS_ZONE_TYPE_PRIMARY) {
if (strcmp(name, "@") != 0 && rec->wType == DNS_TYPE_NS) {
rec->rank = DNS_RANK_NS_GLUE;
} else {
rec->rank |= DNS_RANK_ZONE;
}
} else if (strcmp(z->name, ".") == 0) {
rec->rank |= DNS_RANK_ROOT_HINT;
}
serial = dnsserver_update_soa(mem_ctx, samdb, z);
if (serial < 0) {
return WERR_INTERNAL_DB_ERROR;
}
rec->dwSerial = serial;
rec->dwTimeStamp = 0;
ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs,
"(&(objectClass=dnsNode)(name=%s))",
encoded_name);
if (ret != LDB_SUCCESS) {
return WERR_INTERNAL_DB_ERROR;
}
if (res->count == 0) {
dn = dnsserver_name_to_dn(mem_ctx, z, name);
W_ERROR_HAVE_NO_MEMORY(dn);
return dnsserver_db_do_add_rec(mem_ctx, samdb, dn, 1, rec);
}
el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
if (el == NULL) {
ret = ldb_msg_add_empty(res->msgs[0], "dnsRecord", 0, &el);
if (ret != LDB_SUCCESS) {
return WERR_NOT_ENOUGH_MEMORY;
}
}
was_tombstoned = ldb_msg_find_attr_as_bool(res->msgs[0],
"dNSTombstoned", false);
if (was_tombstoned) {
el->num_values = 0;
}
for (i=0; i<el->num_values; i++) {
struct dnsp_DnssrvRpcRecord rec2;
ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2,
(ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
return WERR_GEN_FAILURE;
}
if (dns_record_match(rec, &rec2)) {
break;
}
}
if (i < el->num_values) {
return WERR_DNS_ERROR_RECORD_ALREADY_EXISTS;
}
if (i == el->num_values) {
/* adding a new value */
el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values+1);
W_ERROR_HAVE_NO_MEMORY(el->values);
el->num_values++;
}
ndr_err = ndr_push_struct_blob(&el->values[i], mem_ctx, rec,
(ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
return WERR_GEN_FAILURE;
}
el->flags = LDB_FLAG_MOD_REPLACE;
el = ldb_msg_find_element(res->msgs[0], "dNSTombstoned");
if (el != NULL) {
el->flags = LDB_FLAG_MOD_DELETE;
}
ret = ldb_modify(samdb, res->msgs[0]);
if (ret != LDB_SUCCESS) {
return WERR_INTERNAL_DB_ERROR;
}
return WERR_OK;
}
/* Update a DNS record */
WERROR dnsserver_db_update_record(TALLOC_CTX *mem_ctx,
struct ldb_context *samdb,
struct dnsserver_zone *z,
const char *name,
struct DNS_RPC_RECORD *add_record,
struct DNS_RPC_RECORD *del_record)
{
const char * const attrs[] = { "dnsRecord", NULL };
struct ldb_result *res;
struct dnsp_DnssrvRpcRecord *arec = NULL, *drec = NULL;
struct ldb_message_element *el;
enum ndr_err_code ndr_err;
int ret, i;
int serial;
WERROR werr;
char *encoded_name = ldb_binary_encode_string(mem_ctx, name);
werr = dns_to_dnsp_convert(mem_ctx, add_record, &arec, true);
if (!W_ERROR_IS_OK(werr)) {
return werr;
}
werr = dns_to_dnsp_convert(mem_ctx, del_record, &drec, true);
if (!W_ERROR_IS_OK(werr)) {
return werr;
}
arec->dwTimeStamp = 0;
ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs,
"(&(objectClass=dnsNode)(name=%s)(!(dNSTombstoned=TRUE)))",
encoded_name);
if (ret != LDB_SUCCESS) {
return WERR_INTERNAL_DB_ERROR;
}
if (res->count == 0) {
return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
}
el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
if (el == NULL || el->num_values == 0) {
return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
}
for (i=0; i<el->num_values; i++) {
struct dnsp_DnssrvRpcRecord rec2;
ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2,
(ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
return WERR_GEN_FAILURE;
}
if (dns_record_match(arec, &rec2)) {
break;
}
}
if (i < el->num_values) {
return WERR_DNS_ERROR_RECORD_ALREADY_EXISTS;
}
for (i=0; i<el->num_values; i++) {
struct dnsp_DnssrvRpcRecord rec2;
ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2,
(ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
return WERR_GEN_FAILURE;
}
if (dns_record_match(drec, &rec2)) {
break;
}
}
if (i == el->num_values) {
return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
}
/* If updating SOA record, use specified serial, otherwise increment */
if (arec->wType != DNS_TYPE_SOA) {
serial = dnsserver_update_soa(mem_ctx, samdb, z);
if (serial < 0) {
return WERR_INTERNAL_DB_ERROR;
}
arec->dwSerial = serial;
}
ndr_err = ndr_push_struct_blob(&el->values[i], mem_ctx, arec,
(ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
return WERR_GEN_FAILURE;
}
el->flags = LDB_FLAG_MOD_REPLACE;
ret = ldb_modify(samdb, res->msgs[0]);
if (ret != LDB_SUCCESS) {
return WERR_INTERNAL_DB_ERROR;
}
return WERR_OK;
}
/* Delete a DNS record */
WERROR dnsserver_db_delete_record(TALLOC_CTX *mem_ctx,
struct ldb_context *samdb,
struct dnsserver_zone *z,
const char *name,
struct DNS_RPC_RECORD *del_record)
{
const char * const attrs[] = { "dnsRecord", NULL };
struct ldb_result *res;
struct dnsp_DnssrvRpcRecord *rec = NULL;
struct ldb_message_element *el;
enum ndr_err_code ndr_err;
int ret, i;
int serial;
WERROR werr;
serial = dnsserver_update_soa(mem_ctx, samdb, z);
if (serial < 0) {
return WERR_INTERNAL_DB_ERROR;
}
werr = dns_to_dnsp_convert(mem_ctx, del_record, &rec, false);
if (!W_ERROR_IS_OK(werr)) {
return werr;
}
ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs,
"(&(objectClass=dnsNode)(name=%s))",
ldb_binary_encode_string(mem_ctx, name));
if (ret != LDB_SUCCESS) {
return WERR_INTERNAL_DB_ERROR;
}
if (res->count == 0) {
return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
}
if (res->count > 1) {
return WERR_DNS_ERROR_RCODE_SERVER_FAILURE;
}
el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
if (el == NULL || el->num_values == 0) {
return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
}
for (i=0; i<el->num_values; i++) {
struct dnsp_DnssrvRpcRecord rec2;
ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2,
(ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
return WERR_GEN_FAILURE;
}
if (dns_record_match(rec, &rec2)) {
break;
}
}
if (i == el->num_values) {
return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
}
if (i < el->num_values-1) {
memmove(&el->values[i], &el->values[i+1], sizeof(el->values[0])*((el->num_values-1)-i));
}
el->num_values--;
if (el->num_values == 0) {
ret = ldb_delete(samdb, res->msgs[0]->dn);
} else {
el->flags = LDB_FLAG_MOD_REPLACE;
ret = ldb_modify(samdb, res->msgs[0]);
}
if (ret != LDB_SUCCESS) {
return WERR_INTERNAL_DB_ERROR;
}
return WERR_OK;
}
static bool dnsserver_db_msg_add_dnsproperty(TALLOC_CTX *mem_ctx,
struct ldb_message *msg,
struct dnsp_DnsProperty *prop)
{
DATA_BLOB *prop_blob;
enum ndr_err_code ndr_err;
int ret;
prop_blob = talloc_zero(mem_ctx, DATA_BLOB);
if (prop_blob == NULL) return false;
ndr_err = ndr_push_struct_blob(prop_blob, mem_ctx, prop,
(ndr_push_flags_fn_t)ndr_push_dnsp_DnsProperty);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
return false;
}
ret = ldb_msg_add_steal_value(msg, "dNSProperty", prop_blob);
if (ret != LDB_SUCCESS) {
return false;
}
return true;
}
WERROR dnsserver_db_do_reset_dword(struct ldb_context *samdb,
struct dnsserver_zone *z,
struct DNS_RPC_NAME_AND_PARAM *n_p)
{
struct ldb_message_element *element = NULL;
struct dnsp_DnsProperty *prop = NULL;
enum ndr_err_code err;
TALLOC_CTX *tmp_ctx = NULL;
const char * const attrs[] = {"dNSProperty", NULL};
struct ldb_result *res = NULL;
int i, ret, prop_id;
if (strcasecmp(n_p->pszNodeName, "Aging") == 0) {
z->zoneinfo->fAging = n_p->dwParam;
prop_id = DSPROPERTY_ZONE_AGING_STATE;
} else if (strcasecmp(n_p->pszNodeName, "RefreshInterval") == 0) {
z->zoneinfo->dwRefreshInterval = n_p->dwParam;
prop_id = DSPROPERTY_ZONE_REFRESH_INTERVAL;
} else if (strcasecmp(n_p->pszNodeName, "NoRefreshInterval") == 0) {
z->zoneinfo->dwNoRefreshInterval = n_p->dwParam;
prop_id = DSPROPERTY_ZONE_NOREFRESH_INTERVAL;
} else if (strcasecmp(n_p->pszNodeName, "AllowUpdate") == 0) {
z->zoneinfo->fAllowUpdate = n_p->dwParam;
prop_id = DSPROPERTY_ZONE_ALLOW_UPDATE;
} else {
return WERR_UNKNOWN_PROPERTY;
}
tmp_ctx = talloc_new(NULL);
if (tmp_ctx == NULL) {
return WERR_NOT_ENOUGH_MEMORY;
}
ret = ldb_search(samdb, tmp_ctx, &res, z->zone_dn, LDB_SCOPE_BASE,
attrs, "(objectClass=dnsZone)");
if (ret != LDB_SUCCESS) {
DBG_ERR("dnsserver: no zone: %s\n",
ldb_dn_get_linearized(z->zone_dn));
TALLOC_FREE(tmp_ctx);
return WERR_INTERNAL_DB_ERROR;
}
if (res->count != 1) {
DBG_ERR("dnsserver: duplicate zone: %s\n",
ldb_dn_get_linearized(z->zone_dn));
TALLOC_FREE(tmp_ctx);
return WERR_GEN_FAILURE;
}
element = ldb_msg_find_element(res->msgs[0], "dNSProperty");
if (element == NULL) {
DBG_ERR("dnsserver: zone %s has no properties.\n",
ldb_dn_get_linearized(z->zone_dn));
TALLOC_FREE(tmp_ctx);
return WERR_INTERNAL_DB_ERROR;
}
for (i = 0; i < element->num_values; i++) {
prop = talloc_zero(element, struct dnsp_DnsProperty);
if (prop == NULL) {
TALLOC_FREE(tmp_ctx);
return WERR_NOT_ENOUGH_MEMORY;
}
err = ndr_pull_struct_blob(
&(element->values[i]),
tmp_ctx,
prop,
(ndr_pull_flags_fn_t)ndr_pull_dnsp_DnsProperty);
if (!NDR_ERR_CODE_IS_SUCCESS(err)){
DBG_ERR("dnsserver: couldn't PULL dns property id "
"%d in zone %s\n",
prop->id,
ldb_dn_get_linearized(z->zone_dn));
TALLOC_FREE(tmp_ctx);
return WERR_INTERNAL_DB_ERROR;
}
if (prop->id == prop_id) {
switch (prop_id) {
case DSPROPERTY_ZONE_AGING_STATE:
prop->data.aging_enabled = n_p->dwParam;
break;
case DSPROPERTY_ZONE_NOREFRESH_INTERVAL:
prop->data.norefresh_hours = n_p->dwParam;
break;
case DSPROPERTY_ZONE_REFRESH_INTERVAL:
prop->data.refresh_hours = n_p->dwParam;
break;
case DSPROPERTY_ZONE_ALLOW_UPDATE:
prop->data.allow_update_flag = n_p->dwParam;
break;
}
err = ndr_push_struct_blob(
&(element->values[i]),
tmp_ctx,
prop,
(ndr_push_flags_fn_t)ndr_push_dnsp_DnsProperty);
if (!NDR_ERR_CODE_IS_SUCCESS(err)){
DBG_ERR("dnsserver: couldn't PUSH dns prop id "
"%d in zone %s\n",
prop->id,
ldb_dn_get_linearized(z->zone_dn));
TALLOC_FREE(tmp_ctx);
return WERR_INTERNAL_DB_ERROR;
}
}
}
element->flags = LDB_FLAG_MOD_REPLACE;
ret = ldb_modify(samdb, res->msgs[0]);
if (ret != LDB_SUCCESS) {
TALLOC_FREE(tmp_ctx);
DBG_ERR("dnsserver: Failed to modify zone %s prop %s: %s\n",
z->name,
n_p->pszNodeName,
ldb_errstring(samdb));
return WERR_INTERNAL_DB_ERROR;
}
TALLOC_FREE(tmp_ctx);
return WERR_OK;
}
/* Create dnsZone record to database and set security descriptor */
static WERROR dnsserver_db_do_create_zone(TALLOC_CTX *tmp_ctx,
struct ldb_context *samdb,
struct ldb_dn *zone_dn,
struct dnsserver_zone *z)
{
const char * const attrs[] = { "objectSID", NULL };
struct ldb_message *msg;
struct ldb_result *res;
struct ldb_message_element *el;
const char sddl_template[] = "D:AI(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;CC;;;AU)(A;;RPLCLORC;;;WD)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CI;RPWPCRCCDCLCRCWOWDSDDTSW;;;ED)(A;CIID;RPWPCRCCDCLCRCWOWDSDDTSW;;;%s)(A;CIID;RPWPCRCCDCLCRCWOWDSDDTSW;;;ED)(OA;CIID;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)(A;CIID;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)(A;CIID;LC;;;RU)(A;CIID;RPWPCRCCLCLORCWOWDSDSW;;;BA)S:AI";
char *sddl;
struct dom_sid dnsadmins_sid;
const struct dom_sid *domain_sid;
struct security_descriptor *secdesc;
struct dnsp_DnsProperty *prop;
DATA_BLOB *sd_encoded;
enum ndr_err_code ndr_err;
int ret;
/* Get DnsAdmins SID */
ret = ldb_search(samdb, tmp_ctx, &res, ldb_get_default_basedn(samdb),
LDB_SCOPE_DEFAULT, attrs, "(sAMAccountName=DnsAdmins)");
if (ret != LDB_SUCCESS || res->count != 1) {
return WERR_INTERNAL_DB_ERROR;
}
el = ldb_msg_find_element(res->msgs[0], "objectSID");
if (el == NULL || el->num_values != 1) {
return WERR_INTERNAL_DB_ERROR;
}
ndr_err = ndr_pull_struct_blob(&el->values[0], tmp_ctx, &dnsadmins_sid,
(ndr_pull_flags_fn_t)ndr_pull_dom_sid);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
return WERR_INTERNAL_DB_ERROR;
}
/* create security descriptor with DnsAdmins GUID in sddl template */
sddl = talloc_asprintf(tmp_ctx, sddl_template,
dom_sid_string(tmp_ctx, &dnsadmins_sid));
if (sddl == NULL) {
return WERR_NOT_ENOUGH_MEMORY;
}
talloc_free(res);
domain_sid = samdb_domain_sid(samdb);
if (domain_sid == NULL) {
return WERR_INTERNAL_DB_ERROR;
}
secdesc = sddl_decode(tmp_ctx, sddl, domain_sid);
if (secdesc == NULL) {
return WERR_GEN_FAILURE;
}
msg = ldb_msg_new(tmp_ctx);
W_ERROR_HAVE_NO_MEMORY(msg);
msg->dn = zone_dn;
ret = ldb_msg_add_string(msg, "objectClass", "dnsZone");
if (ret != LDB_SUCCESS) {
return WERR_NOT_ENOUGH_MEMORY;
}
sd_encoded = talloc_zero(tmp_ctx, DATA_BLOB);
W_ERROR_HAVE_NO_MEMORY(sd_encoded);
ndr_err = ndr_push_struct_blob(sd_encoded, tmp_ctx, secdesc,
(ndr_push_flags_fn_t)ndr_push_security_descriptor);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
return WERR_GEN_FAILURE;
}
ret = ldb_msg_add_steal_value(msg, "nTSecurityDescriptor", sd_encoded);
if (ret != LDB_SUCCESS) {
return WERR_NOT_ENOUGH_MEMORY;
}
/* dns zone Properties */
prop = talloc_zero(tmp_ctx, struct dnsp_DnsProperty);
W_ERROR_HAVE_NO_MEMORY(prop);
prop->version = 1;
/* zone type */
prop->id = DSPROPERTY_ZONE_TYPE;
prop->data.zone_type = z->zoneinfo->dwZoneType;
if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) {
return WERR_NOT_ENOUGH_MEMORY;
}
/* allow update */
prop->id = DSPROPERTY_ZONE_ALLOW_UPDATE;
prop->data.allow_update_flag = z->zoneinfo->fAllowUpdate;
if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) {
return WERR_NOT_ENOUGH_MEMORY;
}
/* secure time */
prop->id = DSPROPERTY_ZONE_SECURE_TIME;
prop->data.zone_secure_time = 0;
if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) {
return WERR_NOT_ENOUGH_MEMORY;
}
/* norefresh interval */
prop->id = DSPROPERTY_ZONE_NOREFRESH_INTERVAL;
prop->data.norefresh_hours = 168;
if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) {
return WERR_NOT_ENOUGH_MEMORY;
}
/* refresh interval */
prop->id = DSPROPERTY_ZONE_REFRESH_INTERVAL;
prop->data.refresh_hours = 168;
if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) {
return WERR_NOT_ENOUGH_MEMORY;
}
/* aging state */
prop->id = DSPROPERTY_ZONE_AGING_STATE;
prop->data.aging_enabled = z->zoneinfo->fAging;
if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) {
return WERR_NOT_ENOUGH_MEMORY;
}
/* aging enabled time */
prop->id = DSPROPERTY_ZONE_AGING_ENABLED_TIME;
prop->data.next_scavenging_cycle_hours = 0;
if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) {
return WERR_NOT_ENOUGH_MEMORY;
}
talloc_free(prop);
ret = ldb_add(samdb, msg);
if (ret != LDB_SUCCESS) {
DEBUG(0, ("dnsserver: Failed to create zone (%s): %s\n",
z->name, ldb_errstring(samdb)));
return WERR_INTERNAL_DB_ERROR;
}
return WERR_OK;
}
/* Create new dnsZone record and @ record (SOA + NS) */
WERROR dnsserver_db_create_zone(struct ldb_context *samdb,
struct dnsserver_partition *partitions,
struct dnsserver_zone *zone,
struct loadparm_context *lp_ctx)
{
struct dnsserver_partition *p;
bool in_forest = false;
WERROR status;
struct ldb_dn *dn;
TALLOC_CTX *tmp_ctx;
struct dnsp_DnssrvRpcRecord *dns_rec;
struct dnsp_soa soa;
char *tmpstr, *server_fqdn, *soa_email;
/* We only support primary zones for now */
if (zone->zoneinfo->dwZoneType != DNS_ZONE_TYPE_PRIMARY) {
return WERR_CALL_NOT_IMPLEMENTED;
}
/* Get the correct partition */
if (zone->partition->dwDpFlags & DNS_DP_FOREST_DEFAULT) {
in_forest = true;
}
for (p = partitions; p; p = p->next) {
if (in_forest == p->is_forest) {
break;
}
}
if (p == NULL) {
return WERR_DNS_ERROR_DP_DOES_NOT_EXIST;
}
tmp_ctx = talloc_new(NULL);
W_ERROR_HAVE_NO_MEMORY(tmp_ctx);
dn = ldb_dn_copy(tmp_ctx, p->partition_dn);
W_ERROR_HAVE_NO_MEMORY_AND_FREE(dn, tmp_ctx);
if(!ldb_dn_add_child_fmt(dn, "DC=%s,CN=MicrosoftDNS", zone->name)) {
talloc_free(tmp_ctx);
return WERR_NOT_ENOUGH_MEMORY;
}
/* Add dnsZone record */
status = dnsserver_db_do_create_zone(tmp_ctx, samdb, dn, zone);
if (!W_ERROR_IS_OK(status)) {
talloc_free(tmp_ctx);
return status;
}
if (!ldb_dn_add_child_fmt(dn, "DC=@")) {
talloc_free(tmp_ctx);
return WERR_NOT_ENOUGH_MEMORY;
}
dns_rec = talloc_zero_array(tmp_ctx, struct dnsp_DnssrvRpcRecord, 2);
W_ERROR_HAVE_NO_MEMORY_AND_FREE(dns_rec, tmp_ctx);
tmpstr = talloc_asprintf(tmp_ctx, "%s.%s",
lpcfg_netbios_name(lp_ctx),
lpcfg_realm(lp_ctx));
W_ERROR_HAVE_NO_MEMORY_AND_FREE(tmpstr, tmp_ctx);
server_fqdn = strlower_talloc(tmp_ctx, tmpstr);
W_ERROR_HAVE_NO_MEMORY_AND_FREE(server_fqdn, tmp_ctx);
talloc_free(tmpstr);
tmpstr = talloc_asprintf(tmp_ctx, "hostmaster.%s",
lpcfg_realm(lp_ctx));
W_ERROR_HAVE_NO_MEMORY_AND_FREE(tmpstr, tmp_ctx);
soa_email = strlower_talloc(tmp_ctx, tmpstr);
W_ERROR_HAVE_NO_MEMORY_AND_FREE(soa_email, tmp_ctx);
talloc_free(tmpstr);
/* SOA Record - values same as defined in provision/sambadns.py */
soa.serial = 1;
soa.refresh = 900;
soa.retry = 600;
soa.expire = 86400;
soa.minimum = 3600;
soa.mname = server_fqdn;
soa.rname = soa_email;
dns_rec[0].wType = DNS_TYPE_SOA;
dns_rec[0].rank = DNS_RANK_ZONE;
dns_rec[0].dwSerial = soa.serial;
dns_rec[0].dwTtlSeconds = 3600;
dns_rec[0].dwTimeStamp = 0;
dns_rec[0].data.soa = soa;
/* NS Record */
dns_rec[1].wType = DNS_TYPE_NS;
dns_rec[1].rank = DNS_RANK_ZONE;
dns_rec[1].dwSerial = soa.serial;
dns_rec[1].dwTtlSeconds = 3600;
dns_rec[1].dwTimeStamp = 0;
dns_rec[1].data.ns = server_fqdn;
/* Add @ Record */
status = dnsserver_db_do_add_rec(tmp_ctx, samdb, dn, 2, dns_rec);
talloc_free(tmp_ctx);
return status;
}
/* Delete dnsZone record and all DNS records in the zone */
WERROR dnsserver_db_delete_zone(struct ldb_context *samdb,
struct dnsserver_zone *zone)
{
int ret;
ret = ldb_transaction_start(samdb);
if (ret != LDB_SUCCESS) {
return WERR_INTERNAL_DB_ERROR;
}
ret = dsdb_delete(samdb, zone->zone_dn, DSDB_TREE_DELETE);
if (ret != LDB_SUCCESS) {
ldb_transaction_cancel(samdb);
return WERR_INTERNAL_DB_ERROR;
}
ret = ldb_transaction_commit(samdb);
if (ret != LDB_SUCCESS) {
return WERR_INTERNAL_DB_ERROR;
}
return WERR_OK;
}