summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmitay Isaacs <amitay@gmail.com>2011-12-16 15:41:15 +1100
committerAmitay Isaacs <amitay@gmail.com>2011-12-23 16:18:25 +1100
commit10860d58d77e9769c70d07678baddc09f73d3c52 (patch)
tree8c4977dde495a8aa5ce4b331c8aa06db80bcbe89
parente398bdb76bf6a85c757d3152be77d02fd0febac4 (diff)
downloadsamba-10860d58d77e9769c70d07678baddc09f73d3c52.tar.gz
samba-10860d58d77e9769c70d07678baddc09f73d3c52.tar.bz2
samba-10860d58d77e9769c70d07678baddc09f73d3c52.zip
s4:rpc-dnsserver: Implement zone management RPC operations
- ZoneCreate operation to create zone. - DeleteZoneFromDs operation to delete zone When a zone is deleted, all the records in that zone are also deleted.
-rw-r--r--source4/rpc_server/dnsserver/dcerpc_dnsserver.c121
-rw-r--r--source4/rpc_server/dnsserver/dnsdb.c311
-rw-r--r--source4/rpc_server/dnsserver/dnsserver.h6
3 files changed, 436 insertions, 2 deletions
diff --git a/source4/rpc_server/dnsserver/dcerpc_dnsserver.c b/source4/rpc_server/dnsserver/dcerpc_dnsserver.c
index 5c1a20340a..c9de440621 100644
--- a/source4/rpc_server/dnsserver/dcerpc_dnsserver.c
+++ b/source4/rpc_server/dnsserver/dcerpc_dnsserver.c
@@ -40,6 +40,59 @@ struct dnsserver_state {
/* Utility functions */
+static void dnsserver_reload_zones(struct dnsserver_state *dsstate)
+{
+ struct dnsserver_partition *p;
+ struct dnsserver_zone *zones, *z, *znext, *zmatch;
+ struct dnsserver_zone *old_list, *new_list;
+
+ old_list = dsstate->zones;
+ new_list = NULL;
+
+ for (p = dsstate->partitions; p; p = p->next) {
+ zones = dnsserver_db_enumerate_zones(dsstate, dsstate->samdb, p);
+ if (zones == NULL) {
+ continue;
+ }
+ for (z = zones; z; ) {
+ znext = z->next;
+ zmatch = dnsserver_find_zone(old_list, z->name);
+ if (zmatch == NULL) {
+ /* Missing zone */
+ z->zoneinfo = dnsserver_init_zoneinfo(z, dsstate->serverinfo);
+ if (z->zoneinfo == NULL) {
+ continue;
+ }
+ DLIST_ADD_END(new_list, z, NULL);
+ p->zones_count++;
+ dsstate->zones_count++;
+ } else {
+ /* Existing zone */
+ talloc_free(z);
+ DLIST_REMOVE(old_list, zmatch);
+ DLIST_ADD_END(new_list, zmatch, NULL);
+ }
+ z = znext;
+ }
+ }
+
+ if (new_list == NULL) {
+ return;
+ }
+
+ /* Deleted zones */
+ for (z = old_list; z; ) {
+ znext = z->next;
+ z->partition->zones_count--;
+ dsstate->zones_count--;
+ talloc_free(z);
+ z = znext;
+ }
+
+ dsstate->zones = new_list;
+}
+
+
static struct dnsserver_state *dnsserver_connect(struct dcesrv_call_state *dce_call)
{
struct dnsserver_state *dsstate;
@@ -1040,7 +1093,55 @@ static WERROR dnsserver_operate_server(struct dnsserver_state *dsstate,
} else if (strcasecmp(operation, "WriteDirtyZones") == 0) {
valid_operation = true;
} else if (strcasecmp(operation, "ZoneCreate") == 0) {
- valid_operation = true;
+ struct dnsserver_zone *z, *z2;
+ WERROR status;
+
+ z = talloc_zero(mem_ctx, struct dnsserver_zone);
+ W_ERROR_HAVE_NO_MEMORY(z);
+ z->partition = talloc_zero(z, struct dnsserver_partition);
+ W_ERROR_HAVE_NO_MEMORY_AND_FREE(z->partition, z);
+ z->zoneinfo = talloc_zero(z, struct dnsserver_zoneinfo);
+ W_ERROR_HAVE_NO_MEMORY_AND_FREE(z->zoneinfo, z);
+
+ if (typeid == DNSSRV_TYPEID_ZONE_CREATE_W2K) {
+ z->name = talloc_strdup(z, r->ZoneCreateW2K->pszZoneName);
+ z->zoneinfo->dwZoneType = r->ZoneCreateW2K->dwZoneType;
+ z->zoneinfo->fAllowUpdate = r->ZoneCreateW2K->fAllowUpdate;
+ z->zoneinfo->fAging = r->ZoneCreateW2K->fAging;
+ z->zoneinfo->Flags = r->ZoneCreateW2K->dwFlags;
+ } else if (typeid == DNSSRV_TYPEID_ZONE_CREATE_DOTNET) {
+ z->name = talloc_strdup(z, r->ZoneCreateDotNet->pszZoneName);
+ z->zoneinfo->dwZoneType = r->ZoneCreateDotNet->dwZoneType;
+ z->zoneinfo->fAllowUpdate = r->ZoneCreateDotNet->fAllowUpdate;
+ z->zoneinfo->fAging = r->ZoneCreateDotNet->fAging;
+ z->zoneinfo->Flags = r->ZoneCreateDotNet->dwFlags;
+ z->partition->dwDpFlags = r->ZoneCreateDotNet->dwDpFlags;
+ } else if (typeid == DNSSRV_TYPEID_ZONE_CREATE) {
+ z->name = talloc_strdup(z, r->ZoneCreate->pszZoneName);
+ z->zoneinfo->dwZoneType = r->ZoneCreate->dwZoneType;
+ z->zoneinfo->fAllowUpdate = r->ZoneCreate->fAllowUpdate;
+ z->zoneinfo->fAging = r->ZoneCreate->fAging;
+ z->zoneinfo->Flags = r->ZoneCreate->dwFlags;
+ z->partition->dwDpFlags = r->ZoneCreate->dwDpFlags;
+ } else {
+ talloc_free(z);
+ return WERR_DNS_ERROR_INVALID_PROPERTY;
+ }
+
+ z2 = dnsserver_find_zone(dsstate->zones, z->name);
+ if (z2 != NULL) {
+ talloc_free(z);
+ return WERR_DNS_ERROR_ZONE_ALREADY_EXISTS;
+ }
+
+ status = dnsserver_db_create_zone(dsstate->samdb, dsstate->partitions, z,
+ dsstate->lp_ctx);
+ talloc_free(z);
+
+ if (W_ERROR_IS_OK(status)) {
+ dnsserver_reload_zones(dsstate);
+ }
+ return status;
} else if (strcasecmp(operation, "ClearStatistics") == 0) {
valid_operation = true;
} else if (strcasecmp(operation, "EnlistDirectoryPartition") == 0) {
@@ -1390,6 +1491,14 @@ static WERROR dnsserver_operate_zone(struct dnsserver_state *dsstate,
bool valid_operation = false;
if (strcasecmp(operation, "ResetDwordProperty") == 0) {
+ if (typeid != DNSSRV_TYPEID_NAME_AND_PARAM) {
+ return WERR_DNS_ERROR_INVALID_PROPERTY;
+ }
+
+ /* Ignore property resets */
+ if (strcasecmp(r->NameAndParam->pszNodeName, "AllowUpdate") == 0) {
+ return WERR_OK;
+ }
valid_operation = true;
} else if (strcasecmp(operation, "ZoneTypeReset") == 0) {
valid_operation = true;
@@ -1410,7 +1519,15 @@ static WERROR dnsserver_operate_zone(struct dnsserver_state *dsstate,
} else if (strcasecmp(operation, "WriteBackFile") == 0) {
valid_operation = true;
} else if (strcasecmp(operation, "DeleteZoneFromDs") == 0) {
- valid_operation = true;
+ WERROR status;
+ if (z == NULL) {
+ return WERR_DNS_ERROR_ZONE_DOES_NOT_EXIST;
+ }
+ status = dnsserver_db_delete_zone(dsstate->samdb, z);
+ if (W_ERROR_IS_OK(status)) {
+ dnsserver_reload_zones(dsstate);
+ }
+ return status;
} else if (strcasecmp(operation, "UpdateZoneFromDs") == 0) {
valid_operation = true;
} else if (strcasecmp(operation, "ZoneExport") == 0) {
diff --git a/source4/rpc_server/dnsserver/dnsdb.c b/source4/rpc_server/dnsserver/dnsdb.c
index 939365840b..8421dcded0 100644
--- a/source4/rpc_server/dnsserver/dnsdb.c
+++ b/source4/rpc_server/dnsserver/dnsdb.c
@@ -23,7 +23,11 @@
#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,
@@ -636,3 +640,310 @@ WERROR dnsserver_db_delete_record(TALLOC_CTX *mem_ctx,
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;
+}
+
+
+/* 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_NOMEM;
+ }
+ 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_GENERAL_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_NOMEM;
+ }
+
+ 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_GENERAL_FAILURE;
+ }
+
+ ret = ldb_msg_add_steal_value(msg, "nTSecurityDescriptor", sd_encoded);
+ if (ret != LDB_SUCCESS) {
+ return WERR_NOMEM;
+ }
+
+ /* 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_NOMEM;
+ }
+
+ /* 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_NOMEM;
+ }
+
+ /* 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_NOMEM;
+ }
+
+ /* 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_NOMEM;
+ }
+
+ /* 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_NOMEM;
+ }
+
+ /* 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_NOMEM;
+ }
+
+ /* 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_NOMEM;
+ }
+
+ 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;
+ NTTIME t;
+
+ /* 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_NOMEM;
+ }
+
+ /* 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_NOMEM;
+ }
+
+ 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);
+
+ unix_to_nt_time(&t, time(NULL));
+ t /= 10*1000*1000; /* convert to seconds (NT time is in 100ns units) */
+ t /= 3600; /* convert to hours */
+
+ /* 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 = (uint32_t)t;
+ 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].dwTimeStamp = (uint32_t)t;
+ 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;
+}
diff --git a/source4/rpc_server/dnsserver/dnsserver.h b/source4/rpc_server/dnsserver/dnsserver.h
index 63224c55c1..e3db0b2e1c 100644
--- a/source4/rpc_server/dnsserver/dnsserver.h
+++ b/source4/rpc_server/dnsserver/dnsserver.h
@@ -249,5 +249,11 @@ WERROR dnsserver_db_delete_record(TALLOC_CTX *mem_ctx,
struct dnsserver_zone *z,
const char *node_name,
struct DNS_RPC_RECORD *del_record);
+WERROR dnsserver_db_create_zone(struct ldb_context *samdb,
+ struct dnsserver_partition *partitions,
+ struct dnsserver_zone *z,
+ struct loadparm_context *lp_ctx);
+WERROR dnsserver_db_delete_zone(struct ldb_context *samdb,
+ struct dnsserver_zone *z);
#endif /* __DNSSERVER_H__ */