1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-25 23:21:54 +03:00

s4-drs: implement RODC attribute filtering override

When a RODC uses extended getncchanges operation
DRSUAPI_EXOP_REPL_SECRET it gets an override on the ability to
replicate the secret attributes.

Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
This commit is contained in:
Andrew Tridgell 2010-08-18 14:31:05 +10:00
parent dc7cf47371
commit c122939919
2 changed files with 79 additions and 39 deletions

View File

@ -3940,29 +3940,43 @@ int dsdb_validate_dsa_guid(struct ldb_context *ldb,
return LDB_SUCCESS;
}
const char *rodc_fas_list[] = {"ms-PKI-DPAPIMasterKeys",
"ms-PKI-AccountCredentials",
"ms-PKI-RoamingTimeStamp",
"ms-FVE-KeyPackage",
"ms-FVE-RecoveryGuid",
"ms-FVE-RecoveryInformation",
"ms-FVE-RecoveryPassword",
"ms-FVE-VolumeGuid",
"ms-TPM-OwnerInformation",
NULL};
static const char *secret_attributes[] = {
"currentValue",
"dBCSPwd",
"initialAuthIncoming",
"initialAuthOutgoing",
"lmPwdHistory",
"ntPwdHistory",
"priorValue",
"supplementalCredentials",
"trustAuthIncoming",
"trustAuthOutgoing",
"unicodePwd",
NULL
};
/*
check if the attribute belongs to the RODC filtered attribute set
Note that attributes that are in the filtered attribute set are the
ones that _are_ always sent to a RODC
*/
bool dsdb_attr_in_rodc_fas(uint32_t replica_flags, const struct dsdb_attribute *sa)
bool dsdb_attr_in_rodc_fas(const struct dsdb_attribute *sa)
{
int rodc_filtered_flags = SEARCH_FLAG_RODC_ATTRIBUTE | SEARCH_FLAG_CONFIDENTIAL;
bool drs_write_replica = ((replica_flags & DRSUAPI_DRS_WRIT_REP) == 0);
/* they never get secret attributes */
if (is_attr_in_list(secret_attributes, sa->lDAPDisplayName)) {
return false;
}
if (drs_write_replica && (sa->searchFlags & rodc_filtered_flags)) {
/* they do get non-secret critical attributes */
if (sa->schemaFlagsEx & SCHEMA_FLAG_ATTR_IS_CRITICAL) {
return true;
}
if (drs_write_replica && is_attr_in_list(rodc_fas_list, sa->cn)) {
/* they do get non-secret attributes marked as being in the FAS */
if (sa->searchFlags & SEARCH_FLAG_RODC_ATTRIBUTE) {
return true;
}
/* other attributes are denied */
return false;
}

View File

@ -95,7 +95,8 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem
DATA_BLOB *session_key,
uint64_t highest_usn,
uint32_t replica_flags,
struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector)
struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector,
enum drsuapi_DsExtendedOperation extended_op)
{
const struct ldb_val *md_value;
unsigned int i, n;
@ -177,9 +178,12 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem
obj->meta_data_ctr->meta_data = talloc_array(obj, struct drsuapi_DsReplicaMetaData, md.ctr.ctr1.count);
for (n=i=0; i<md.ctr.ctr1.count; i++) {
const struct dsdb_attribute *sa;
bool force_attribute = false;
/* if the attribute has not changed, and it is not the
instanceType then don't include it */
if (md.ctr.ctr1.array[i].local_usn < highest_usn &&
extended_op != DRSUAPI_EXOP_REPL_SECRET &&
md.ctr.ctr1.array[i].attid != DRSUAPI_ATTRIBUTE_instanceType) continue;
/* don't include the rDN */
@ -202,8 +206,16 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem
}
}
if (extended_op == DRSUAPI_EXOP_REPL_SECRET &&
!dsdb_attr_in_rodc_fas(sa)) {
force_attribute = true;
DEBUG(4,("Forcing attribute %s in %s\n",
sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
}
/* filter by uptodateness_vector */
if (md.ctr.ctr1.array[i].attid != DRSUAPI_ATTRIBUTE_instanceType &&
!force_attribute &&
udv_filter(uptodateness_vector,
&md.ctr.ctr1.array[i].originating_invocation_id,
md.ctr.ctr1.array[i].originating_usn)) {
@ -211,15 +223,20 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem
}
/*
* If the recipient is a RODC, then we should not add any
* RODC filtered attribute
* If the recipient is a RODC, then we should only give
* attributes from the RODC filtered attribute set
*
* TODO: This is not strictly correct, as it doesn't allow for administrators
* to setup some users to transfer passwords to specific RODCs. To support that
* we would instead remove this check and rely on extended ACL checking in the dsdb
* acl module.
*/
if (dsdb_attr_in_rodc_fas(replica_flags, sa)) {
if (!(replica_flags & DRSUAPI_DRS_WRIT_REP) &&
!force_attribute &&
!dsdb_attr_in_rodc_fas(sa)) {
DEBUG(4,("Skipping non-FAS attr %s in %s\n",
sa->lDAPDisplayName,
ldb_dn_get_linearized(msg->dn)));
continue;
}
@ -718,10 +735,13 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
struct ldb_dn *search_dn = NULL;
bool am_rodc;
enum security_user_level security_level;
struct ldb_context *sam_ctx;
DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
b_state = h->data;
sam_ctx = b_state->sam_ctx_system?b_state->sam_ctx_system:b_state->sam_ctx;
*r->out.level_out = 6;
/* TODO: linked attributes*/
r->out.ctr->ctr6.linked_attributes_count = 0;
@ -733,7 +753,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
r->out.ctr->ctr6.uptodateness_vector = NULL;
/* a RODC doesn't allow for any replication */
ret = samdb_rodc(b_state->sam_ctx, &am_rodc);
ret = samdb_rodc(sam_ctx, &am_rodc);
if (ret == LDB_SUCCESS && am_rodc) {
DEBUG(0,(__location__ ": DsGetNCChanges attempt on RODC\n"));
return WERR_DS_DRA_SOURCE_DISABLED;
@ -759,7 +779,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
return WERR_DS_DRA_INVALID_PARAMETER;
}
if (samdb_ntds_options(b_state->sam_ctx, &options) != LDB_SUCCESS) {
if (samdb_ntds_options(sam_ctx, &options) != LDB_SUCCESS) {
return WERR_DS_DRA_INTERNAL_ERROR;
}
@ -769,7 +789,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
}
werr = drs_security_level_check(dce_call, "DsGetNCChanges", SECURITY_RO_DOMAIN_CONTROLLER,
samdb_domain_sid(b_state->sam_ctx));
samdb_domain_sid(sam_ctx));
if (!W_ERROR_IS_OK(werr)) {
return werr;
}
@ -777,9 +797,10 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
/* for non-administrator replications, check that they have
given the correct source_dsa_invocation_id */
security_level = security_session_user_level(dce_call->conn->auth_state.session_info,
samdb_domain_sid(b_state->sam_ctx));
samdb_domain_sid(sam_ctx));
if (security_level == SECURITY_RO_DOMAIN_CONTROLLER &&
(req8->replica_flags & DRSUAPI_DRS_WRIT_REP)) {
(req8->replica_flags & DRSUAPI_DRS_WRIT_REP) &&
req8->extended_op != DRSUAPI_EXOP_REPL_SECRET) {
DEBUG(3,(__location__ ": Removing WRIT_REP flag for replication by RODC %s\n",
dom_sid_string(mem_ctx,
dce_call->conn->auth_state.session_info->security_token->sids[PRIMARY_USER_SID_INDEX])));
@ -800,15 +821,19 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
case DRSUAPI_EXOP_FSMO_RID_ALLOC:
werr = getncchanges_rid_alloc(b_state, mem_ctx, req8, &r->out.ctr->ctr6);
W_ERROR_NOT_OK_RETURN(werr);
search_dn = ldb_get_default_basedn(b_state->sam_ctx);
search_dn = ldb_get_default_basedn(sam_ctx);
break;
case DRSUAPI_EXOP_REPL_SECRET:
DEBUG(0,(__location__ ": DRSUAPI_EXOP_REPL_SECRET extended op\n"));
r->out.ctr->ctr6.extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
req8->highwatermark.highest_usn = 0;
break;
case DRSUAPI_EXOP_FSMO_REQ_ROLE:
case DRSUAPI_EXOP_FSMO_RID_REQ_ROLE:
case DRSUAPI_EXOP_FSMO_REQ_PDC:
case DRSUAPI_EXOP_FSMO_ABANDON_ROLE:
case DRSUAPI_EXOP_REPL_OBJ:
case DRSUAPI_EXOP_REPL_SECRET:
DEBUG(0,(__location__ ": Request for DsGetNCChanges unsupported extended op 0x%x\n",
(unsigned)req8->extended_op));
return WERR_DS_DRA_NOT_SUPPORTED;
@ -818,7 +843,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
/* see if a previous replication has been abandoned */
if (getnc_state) {
struct ldb_dn *new_dn = ldb_dn_new(getnc_state, b_state->sam_ctx, ncRoot->dn);
struct ldb_dn *new_dn = ldb_dn_new(getnc_state, sam_ctx, ncRoot->dn);
if (ldb_dn_compare(new_dn, getnc_state->ncRoot_dn) != 0) {
DEBUG(0,(__location__ ": DsGetNCChanges 2nd replication on different DN %s %s (last_dn %s)\n",
ldb_dn_get_linearized(new_dn),
@ -835,7 +860,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
return WERR_NOMEM;
}
b_state->getncchanges_state = getnc_state;
getnc_state->ncRoot_dn = ldb_dn_new(getnc_state, b_state->sam_ctx, ncRoot->dn);
getnc_state->ncRoot_dn = ldb_dn_new(getnc_state, sam_ctx, ncRoot->dn);
/* find out if we are to replicate Schema NC */
ret = ldb_dn_compare(getnc_state->ncRoot_dn,
@ -894,7 +919,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
DEBUG(1,(__location__ ": getncchanges on %s using filter %s\n",
ldb_dn_get_linearized(getnc_state->ncRoot_dn), search_filter));
ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, getnc_state, &getnc_state->site_res,
ret = drsuapi_search_with_extended_dn(sam_ctx, getnc_state, &getnc_state->site_res,
search_dn, scope, attrs,
search_filter);
if (ret != LDB_SUCCESS) {
@ -921,7 +946,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
}
/* Prefix mapping */
schema = dsdb_get_schema(b_state->sam_ctx, mem_ctx);
schema = dsdb_get_schema(sam_ctx, mem_ctx);
if (!schema) {
DEBUG(0,("No schema in sam_ctx\n"));
return WERR_DS_DRA_INTERNAL_ERROR;
@ -930,7 +955,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
r->out.ctr->ctr6.naming_context = talloc(mem_ctx, struct drsuapi_DsReplicaObjectIdentifier);
*r->out.ctr->ctr6.naming_context = *ncRoot;
if (dsdb_find_guid_by_dn(b_state->sam_ctx, getnc_state->ncRoot_dn,
if (dsdb_find_guid_by_dn(sam_ctx, getnc_state->ncRoot_dn,
&r->out.ctr->ctr6.naming_context->guid) != LDB_SUCCESS) {
DEBUG(0,(__location__ ": Failed to find GUID of ncRoot_dn %s\n",
ldb_dn_get_linearized(getnc_state->ncRoot_dn)));
@ -938,13 +963,13 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
}
/* find the SID if there is one */
dsdb_find_sid_by_dn(b_state->sam_ctx, getnc_state->ncRoot_dn, &r->out.ctr->ctr6.naming_context->sid);
dsdb_find_sid_by_dn(sam_ctx, getnc_state->ncRoot_dn, &r->out.ctr->ctr6.naming_context->sid);
dsdb_get_oid_mappings_drsuapi(schema, true, mem_ctx, &ctr);
r->out.ctr->ctr6.mapping_ctr = *ctr;
r->out.ctr->ctr6.source_dsa_guid = *(samdb_ntds_objectGUID(b_state->sam_ctx));
r->out.ctr->ctr6.source_dsa_invocation_id = *(samdb_ntds_invocation_id(b_state->sam_ctx));
r->out.ctr->ctr6.source_dsa_guid = *(samdb_ntds_objectGUID(sam_ctx));
r->out.ctr->ctr6.source_dsa_invocation_id = *(samdb_ntds_invocation_id(sam_ctx));
r->out.ctr->ctr6.old_highwatermark = req8->highwatermark;
r->out.ctr->ctr6.new_highwatermark = req8->highwatermark;
@ -975,15 +1000,16 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
obj = talloc_zero(mem_ctx, struct drsuapi_DsReplicaObjectListItemEx);
werr = get_nc_changes_build_object(obj, msg,
b_state->sam_ctx, getnc_state->ncRoot_dn,
sam_ctx, getnc_state->ncRoot_dn,
getnc_state->is_schema_nc,
schema, &session_key, getnc_state->min_usn,
req8->replica_flags, getnc_state->uptodateness_vector);
req8->replica_flags, getnc_state->uptodateness_vector,
req8->extended_op);
if (!W_ERROR_IS_OK(werr)) {
return werr;
}
werr = get_nc_changes_add_links(b_state->sam_ctx, getnc_state,
werr = get_nc_changes_add_links(sam_ctx, getnc_state,
getnc_state->ncRoot_dn,
schema, getnc_state->min_usn,
req8->replica_flags,
@ -1071,7 +1097,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
/* sort the whole array the first time */
if (!getnc_state->la_sorted) {
LDB_TYPESAFE_QSORT(getnc_state->la_list, getnc_state->la_count,
b_state->sam_ctx, linked_attribute_compare);
sam_ctx, linked_attribute_compare);
getnc_state->la_sorted = true;
}
@ -1095,7 +1121,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
r->out.ctr->ctr6.uptodateness_vector = talloc(mem_ctx, struct drsuapi_DsReplicaCursor2CtrEx);
r->out.ctr->ctr6.new_highwatermark.highest_usn = r->out.ctr->ctr6.new_highwatermark.tmp_highest_usn;
werr = get_nc_changes_udv(b_state->sam_ctx, getnc_state->ncRoot_dn,
werr = get_nc_changes_udv(sam_ctx, getnc_state->ncRoot_dn,
r->out.ctr->ctr6.uptodateness_vector);
if (!W_ERROR_IS_OK(werr)) {
return werr;