diff options
| -rw-r--r-- | source4/rpc_server/dnsserver/dcerpc_dnsserver.c | 121 | ||||
| -rw-r--r-- | source4/rpc_server/dnsserver/dnsdb.c | 311 | ||||
| -rw-r--r-- | source4/rpc_server/dnsserver/dnsserver.h | 6 | 
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__ */  | 
