| |
@@ -134,7 +134,7 @@
|
| |
const char *policy_filename;
|
| |
};
|
| |
|
| |
- enum ace_eval_status {
|
| |
+ enum ace_eval_agp_status {
|
| |
AD_GPO_ACE_DENIED,
|
| |
AD_GPO_ACE_ALLOWED,
|
| |
AD_GPO_ACE_NEUTRAL
|
| |
@@ -698,40 +698,55 @@
|
| |
}
|
| |
|
| |
/*
|
| |
- * This function determines whether use of the extended right
|
| |
- * named "ApplyGroupPolicy" (AGP) is allowed, by comparing the specified
|
| |
- * user_sid and group_sids against the specified access control entry (ACE).
|
| |
+ * This function determines whether use of the extended right named
|
| |
+ * "ApplyGroupPolicy" (AGP) is allowed for the GPO, by comparing the
|
| |
+ * specified user_sid and group_sids against the passed access control
|
| |
+ * entry (ACE).
|
| |
* This function returns ALLOWED, DENIED, or NEUTRAL depending on whether
|
| |
* the ACE explicitly allows, explicitly denies, or does neither.
|
| |
*
|
| |
- * Note that the 'M' abbreviation used in the evaluation algorithm stands for
|
| |
- * "access_mask", which represents the set of access rights associated with an
|
| |
- * individual ACE. The access right of interest to the GPO code is
|
| |
+ * Notes:
|
| |
+ * (1) Abbreviation 'M' used in the evaluation algorithm stands for
|
| |
+ * "access_mask", which represents the set of access rights associated with
|
| |
+ * the passed ACE. The access right of interest to the GPO code is
|
| |
* RIGHT_DS_CONTROL_ACCESS, which serves as a container for all control access
|
| |
* rights. The specific control access right is identified by a GUID in the
|
| |
* ACE's ObjectType. In our case, this is the GUID corresponding to AGP.
|
| |
+ * (2) ACE that require an evaluation algorithm different from [MS-ADTS]
|
| |
+ * 5.1.3.3.4, e. g. RIGHT_DS_CONTROL_ACCESS (CR) is not present in M, are
|
| |
+ * ignored.
|
| |
*
|
| |
* The ACE evaluation algorithm is specified in [MS-ADTS] 5.1.3.3.4:
|
| |
- * - Deny access by default
|
| |
- * - If the "Inherit Only" (IO) flag is set in the ACE, skip the ACE.
|
| |
- * - If the SID in the ACE does not match any SID in the requester's
|
| |
- * security context, skip the ACE
|
| |
- * - If the ACE type is "Object Access Allowed", the access right
|
| |
- * RIGHT_DS_CONTROL_ACCESS (CR) is present in M, and the ObjectType
|
| |
- * field in the ACE is either not present OR contains a GUID value equal
|
| |
- * to AGP, then grant requested control access right. Stop access checking.
|
| |
- * - If the ACE type is "Object Access Denied", the access right
|
| |
- * RIGHT_DS_CONTROL_ACCESS (CR) is present in M, and the ObjectType
|
| |
- * field in the ACE is either not present OR contains a GUID value equal to
|
| |
- * AGP, then deny the requested control access right. Stop access checking.
|
| |
+ * Evaluate the DACL by examining each ACE in sequence, starting with the first
|
| |
+ * ACE. Perform the following sequence of actions for each ACE in the order as
|
| |
+ * shown:
|
| |
+ * 1. If the "Inherit Only" (IO) flag is set in the ACE, skip the ACE.
|
| |
+ * 2. If the SID in the ACE does not match any SID in the requester's
|
| |
+ * security context, skip the ACE.
|
| |
+ * 3. If the ACE type is "Object Access Allowed", the access right
|
| |
+ * RIGHT_DS_CONTROL_ACCESS (CR) is present in M, and the ObjectType
|
| |
+ * field in the ACE is not present, then grant the requested control
|
| |
+ * access right. Stop any further access checks.
|
| |
+ * 4. If the ACE type is "Object Access Allowed" the access right
|
| |
+ * RIGHT_DS_CONTROL_ACCESS (CR) is present in M, and the ObjectType
|
| |
+ * field in the ACE contains a GUID value equal to AGP, then grant
|
| |
+ * the requested control access right. Stop any further access checks.
|
| |
+ * 5. If the ACE type is "Object Access Denied", the access right
|
| |
+ * RIGHT_DS_CONTROL_ACCESS (CR) is present in M, and the ObjectType
|
| |
+ * field in the ACE is not present, then deny the requested control
|
| |
+ * access right. Stop any further access checks.
|
| |
+ * 6. If the ACE type is "Object Access Denied" the access right
|
| |
+ * RIGHT_DS_CONTROL_ACCESS (CR) is present in M, and the ObjectType
|
| |
+ * field in the ACE contains a GUID value equal to AGP, then deny
|
| |
+ * the requested control access right. Stop any further access checks.
|
| |
*/
|
| |
- static enum ace_eval_status ad_gpo_evaluate_ace(struct security_ace *ace,
|
| |
- struct sss_idmap_ctx *idmap_ctx,
|
| |
- const char *user_sid,
|
| |
- const char **group_sids,
|
| |
- int group_size)
|
| |
+ static enum ace_eval_agp_status
|
| |
+ ad_gpo_evaluate_agp_ace(struct security_ace *ace,
|
| |
+ struct sss_idmap_ctx *idmap_ctx,
|
| |
+ const char *user_sid,
|
| |
+ const char **group_sids,
|
| |
+ int group_size)
|
| |
{
|
| |
- bool agp_included = false;
|
| |
bool included = false;
|
| |
int ret = 0;
|
| |
struct security_ace_object object;
|
| |
@@ -752,36 +767,100 @@
|
| |
return AD_GPO_ACE_NEUTRAL;
|
| |
}
|
| |
|
| |
- object = ace->object.object;
|
| |
- GUID_from_string(AD_AGP_GUID, &ext_right_agp_guid);
|
| |
-
|
| |
- if (object.flags & SEC_ACE_OBJECT_TYPE_PRESENT) {
|
| |
- if (GUID_equal(&object.type.type, &ext_right_agp_guid)) {
|
| |
- agp_included = true;
|
| |
- }
|
| |
- } else {
|
| |
- agp_included = false;
|
| |
- }
|
| |
-
|
| |
if (ace->access_mask & SEC_ADS_CONTROL_ACCESS) {
|
| |
- if (agp_included) {
|
| |
- if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT) {
|
| |
- return AD_GPO_ACE_ALLOWED;
|
| |
- } else if (ace->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT) {
|
| |
- return AD_GPO_ACE_DENIED;
|
| |
+ object = ace->object.object;
|
| |
+ if (object.flags & SEC_ACE_OBJECT_TYPE_PRESENT) {
|
| |
+ GUID_from_string(AD_AGP_GUID, &ext_right_agp_guid);
|
| |
+ if (!GUID_equal(&object.type.type, &ext_right_agp_guid)) {
|
| |
+ return AD_GPO_ACE_NEUTRAL;
|
| |
}
|
| |
}
|
| |
+ if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT) {
|
| |
+ return AD_GPO_ACE_ALLOWED;
|
| |
+ } else if (ace->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT) {
|
| |
+ return AD_GPO_ACE_DENIED;
|
| |
+ }
|
| |
+ }
|
| |
+
|
| |
+ return AD_GPO_ACE_NEUTRAL;
|
| |
+ }
|
| |
+
|
| |
+ /*
|
| |
+ * This function evaluates, which standard access rights the passed access
|
| |
+ * control entry (ACE) allows or denies for the entire GPO.
|
| |
+ *
|
| |
+ * Notes:
|
| |
+ * (1) Abbreviation 'M' used in the evaluation algorithm stands for
|
| |
+ * "access_mask", which represents the set of access rights associated with
|
| |
+ * the passed ACE.
|
| |
+ * (2) Abbreviation 'G' used in the evaluation algorithm stands for
|
| |
+ * "granted rights", which represents the set of access rights, that
|
| |
+ * have already been granted by previously evaluated ACEs.
|
| |
+ * (3) Abbreviation 'D' used in the evaluation algorithm stands for
|
| |
+ * "denied rights", which represents the set of access rights, that
|
| |
+ * have already been explicitly denied by previously evaluated ACEs.
|
| |
+ *
|
| |
+ * The simple ACE evaluation algorithm is specified in [MS-ADTS] 5.1.3.3.2:
|
| |
+ * Evaluate the DACL by examining each ACE in sequence, starting with the first
|
| |
+ * ACE. Perform the following sequence of actions for each ACE in the order as
|
| |
+ * shown:
|
| |
+ * 1. If the "Inherit Only" (IO) flag is set in the ACE, skip the ACE.
|
| |
+ * 2. If the SID in the ACE does not match any SID in the requester's
|
| |
+ * security context, skip the ACE.
|
| |
+ * 3. If the ACE type is "Access Denied" and the access rights in M
|
| |
+ * are not in G, then add the rights in M to D.
|
| |
+ * 4. If the ACE type is "Access Allowed" and the access rights in M
|
| |
+ * are not in D, then add the rights in M to G.
|
| |
+ */
|
| |
+ static errno_t ad_gpo_simple_evaluate_ace(struct security_ace *ace,
|
| |
+ struct sss_idmap_ctx *idmap_ctx,
|
| |
+ const char *user_sid,
|
| |
+ const char **group_sids,
|
| |
+ int group_size,
|
| |
+ uint32_t *_gpo_access_granted_status,
|
| |
+ uint32_t *_gpo_access_denied_status)
|
| |
+ {
|
| |
+ bool included = false;
|
| |
+ uint32_t filtered_access_rights = 0;
|
| |
+ int ret = 0;
|
| |
+
|
| |
+ if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
|
| |
+ return EOK;
|
| |
+ }
|
| |
+
|
| |
+ ret = ad_gpo_ace_includes_client_sid(user_sid, group_sids, group_size,
|
| |
+ ace->trustee, idmap_ctx, &included);
|
| |
+
|
| |
+ if (ret != EOK || !included) {
|
| |
+ return ret;
|
| |
}
|
| |
|
| |
- return AD_GPO_ACE_DENIED;
|
| |
+ if (ace->type == SEC_ACE_TYPE_ACCESS_DENIED) {
|
| |
+ filtered_access_rights = ace->access_mask & ~*_gpo_access_granted_status;
|
| |
+ *_gpo_access_denied_status |= filtered_access_rights;
|
| |
+ } else if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED) {
|
| |
+ filtered_access_rights = ace->access_mask & ~*_gpo_access_denied_status;
|
| |
+ *_gpo_access_granted_status |= filtered_access_rights;
|
| |
+ }
|
| |
+
|
| |
+ return ret;
|
| |
}
|
| |
|
| |
+
|
| |
/*
|
| |
* This function extracts the GPO's DACL (discretionary access control list)
|
| |
* from the GPO's specified security descriptor, and determines whether
|
| |
* the GPO is applicable to the policy target, by comparing the specified
|
| |
* user_sid and group_sids against each access control entry (ACE) in the DACL.
|
| |
- * The boolean result is assigned to the _access_allowed output parameter.
|
| |
+ * The GPO is only applicable to the target, if the requester has been granted
|
| |
+ * read access (RIGHT_DS_READ_PROPERTY) to the properties of the GPO and
|
| |
+ * control access (RIGHT_DS_CONTROL_ACCESS) to apply the GPO (AGP).
|
| |
+ * The required read and control access rights for a particular trustee are
|
| |
+ * usually located in different ACEs, i.e. one ACE for control of read access
|
| |
+ * and one for control access.
|
| |
+ * If it comes to the end of the DACL, and the required access is still not
|
| |
+ * explicitly allowed or denied, SSSD denies access to the object as specified
|
| |
+ * in [MS-ADTS] 5.1.3.1.
|
| |
*/
|
| |
static errno_t ad_gpo_evaluate_dacl(struct security_acl *dacl,
|
| |
struct sss_idmap_ctx *idmap_ctx,
|
| |
@@ -791,14 +870,19 @@
|
| |
bool *_dacl_access_allowed)
|
| |
{
|
| |
uint32_t num_aces = 0;
|
| |
- enum ace_eval_status ace_status;
|
| |
- int i = 0;
|
| |
+ uint32_t access_granted_status = 0;
|
| |
+ uint32_t access_denied_status = 0;
|
| |
+ enum ace_eval_agp_status ace_status;
|
| |
struct security_ace *ace = NULL;
|
| |
+ int i = 0;
|
| |
+ int ret = 0;
|
| |
+ enum idmap_error_code err;
|
| |
+ char *trustee_dom_sid_str = NULL;
|
| |
|
| |
num_aces = dacl->num_aces;
|
| |
|
| |
/*
|
| |
- * [MS-ADTS] 5.1.3.3.4:
|
| |
+ * [MS-ADTS] 5.1.3.3.2. and 5.1.3.3.4:
|
| |
* If the DACL does not have any ACE, then deny the requester the
|
| |
* requested control access right.
|
| |
*/
|
| |
@@ -807,22 +891,88 @@
|
| |
return EOK;
|
| |
}
|
| |
|
| |
+ /*
|
| |
+ * [MS-GOPD] 2.4:
|
| |
+ * To process a policy that applies to a Group Policy client, the core
|
| |
+ * Group Policy engine must be able to read the policy data from the
|
| |
+ * directory service so that the policy settings can be applied to the
|
| |
+ * Group Policy client or the interactive user.
|
| |
+ */
|
| |
+ for (i = 0; i < dacl->num_aces; i++) {
|
| |
+ ace = &dacl->aces[i];
|
| |
+
|
| |
+ ret = ad_gpo_simple_evaluate_ace(ace, idmap_ctx, user_sid,
|
| |
+ group_sids, group_size,
|
| |
+ &access_granted_status,
|
| |
+ &access_denied_status);
|
| |
+
|
| |
+ if (ret != EOK) {
|
| |
+ err = sss_idmap_smb_sid_to_sid(idmap_ctx, &ace->trustee,
|
| |
+ &trustee_dom_sid_str);
|
| |
+ if (err != IDMAP_SUCCESS) {
|
| |
+ DEBUG(SSSDBG_OP_FAILURE,
|
| |
+ "sss_idmap_smb_sid_to_sid failed.\n");
|
| |
+ return EFAULT;
|
| |
+ }
|
| |
+
|
| |
+ DEBUG(SSSDBG_MINOR_FAILURE,
|
| |
+ "Could not determine if ACE is applicable; "
|
| |
+ " Trustee: %s\n", trustee_dom_sid_str);
|
| |
+ sss_idmap_free_sid(idmap_ctx, trustee_dom_sid_str);
|
| |
+ trustee_dom_sid_str = NULL;
|
| |
+ continue;
|
| |
+ }
|
| |
+ }
|
| |
+
|
| |
for (i = 0; i < dacl->num_aces; i ++) {
|
| |
ace = &dacl->aces[i];
|
| |
|
| |
- ace_status = ad_gpo_evaluate_ace(ace, idmap_ctx, user_sid,
|
| |
- group_sids, group_size);
|
| |
+ err = sss_idmap_smb_sid_to_sid(idmap_ctx, &ace->trustee,
|
| |
+ &trustee_dom_sid_str);
|
| |
+ if (err != IDMAP_SUCCESS) {
|
| |
+ DEBUG(SSSDBG_OP_FAILURE, "sss_idmap_smb_sid_to_sid failed.\n");
|
| |
+ return EFAULT;
|
| |
+ }
|
| |
+
|
| |
+ ace_status = ad_gpo_evaluate_agp_ace(ace, idmap_ctx, user_sid,
|
| |
+ group_sids, group_size);
|
| |
|
| |
switch (ace_status) {
|
| |
case AD_GPO_ACE_NEUTRAL:
|
| |
- continue;
|
| |
+ break;
|
| |
case AD_GPO_ACE_ALLOWED:
|
| |
- *_dacl_access_allowed = true;
|
| |
- return EOK;
|
| |
+ if (access_granted_status & SEC_ADS_READ_PROP) {
|
| |
+ *_dacl_access_allowed = true;
|
| |
+ sss_idmap_free_sid(idmap_ctx, trustee_dom_sid_str);
|
| |
+ return EOK;
|
| |
+ } else {
|
| |
+ DEBUG(SSSDBG_TRACE_FUNC,
|
| |
+ "GPO read properties access denied (security); "
|
| |
+ " Trustee: %s\n", trustee_dom_sid_str);
|
| |
+ break;
|
| |
+ }
|
| |
case AD_GPO_ACE_DENIED:
|
| |
- *_dacl_access_allowed = false;
|
| |
- return EOK;
|
| |
+ if (access_granted_status & SEC_ADS_READ_PROP) {
|
| |
+ DEBUG(SSSDBG_TRACE_FUNC,
|
| |
+ "GPO denied (security); "
|
| |
+ " Trustee: %s\n", trustee_dom_sid_str);
|
| |
+ sss_idmap_free_sid(idmap_ctx, trustee_dom_sid_str);
|
| |
+ *_dacl_access_allowed = false;
|
| |
+ return EOK;
|
| |
+ } else {
|
| |
+ DEBUG(SSSDBG_TRACE_FUNC,
|
| |
+ "GPO read properties access denied (security); "
|
| |
+ " Trustee: %s\n", trustee_dom_sid_str);
|
| |
+ break;
|
| |
+ }
|
| |
}
|
| |
+ sss_idmap_free_sid(idmap_ctx, trustee_dom_sid_str);
|
| |
+ trustee_dom_sid_str = NULL;
|
| |
+ }
|
| |
+
|
| |
+ if (access_granted_status & SEC_ADS_READ_PROP) {
|
| |
+ DEBUG(SSSDBG_TRACE_FUNC,
|
| |
+ "GPO apply group policy access denied (security)\n");
|
| |
}
|
| |
|
| |
*_dacl_access_allowed = false;
|
| |
@@ -887,12 +1037,12 @@
|
| |
access_allowed = false;
|
| |
candidate_gpo = candidate_gpos[i];
|
| |
|
| |
- DEBUG(SSSDBG_TRACE_ALL, "examining dacl candidate_gpo_guid:%s\n",
|
| |
- candidate_gpo->gpo_guid);
|
| |
+ DEBUG(SSSDBG_TRACE_FUNC, "examining dacl candidate_gpo_guid:%s\n",
|
| |
+ candidate_gpo->gpo_guid);
|
| |
|
| |
/* gpo_func_version must be set to version 2 */
|
| |
if (candidate_gpo->gpo_func_version != 2) {
|
| |
- DEBUG(SSSDBG_TRACE_ALL,
|
| |
+ DEBUG(SSSDBG_TRACE_FUNC,
|
| |
"GPO not applicable to target per security filtering: "
|
| |
"gPCFunctionalityVersion is not 2\n");
|
| |
continue;
|
| |
@@ -900,7 +1050,7 @@
|
| |
|
| |
sd = candidate_gpo->gpo_sd;
|
| |
if (sd == NULL) {
|
| |
- DEBUG(SSSDBG_TRACE_ALL, "Security descriptor is missing\n");
|
| |
+ DEBUG(SSSDBG_MINOR_FAILURE, "Security descriptor is missing\n");
|
| |
ret = EINVAL;
|
| |
goto done;
|
| |
}
|
| |
@@ -909,39 +1059,39 @@
|
| |
|
| |
/* gpo_flags value of 2 means that GPO's computer portion is disabled */
|
| |
if (candidate_gpo->gpo_flags == 2) {
|
| |
- DEBUG(SSSDBG_TRACE_ALL,
|
| |
+ DEBUG(SSSDBG_TRACE_FUNC,
|
| |
"GPO not applicable to target per security filtering: "
|
| |
"GPO's computer portion is disabled\n");
|
| |
continue;
|
| |
}
|
| |
|
| |
- /*
|
| |
- * [MS-ADTS] 5.1.3.3.4:
|
| |
- * If the security descriptor has no DACL or its "DACL Present" bit
|
| |
- * is not set, then grant requester the requested control access right.
|
| |
- */
|
| |
+ if (((sd->type & SEC_DESC_DACL_PRESENT)) && (dacl != NULL)) {
|
| |
+ ret = ad_gpo_evaluate_dacl(dacl, idmap_ctx, user_sid, group_sids,
|
| |
+ group_size, &access_allowed);
|
| |
+ if (ret != EOK) {
|
| |
+ DEBUG(SSSDBG_MINOR_FAILURE,
|
| |
+ "Could not determine if GPO is applicable\n");
|
| |
+ continue;
|
| |
+ }
|
| |
+ } else {
|
| |
+ /*
|
| |
+ * [MS-ADTS] 5.1.3.3.4:
|
| |
+ * If the security descriptor has no DACL or its "DACL Present" bit
|
| |
+ * is not set, then grant requester the requested control access right.
|
| |
+ */
|
| |
|
| |
- if ((!(sd->type & SEC_DESC_DACL_PRESENT)) || (dacl == NULL)) {
|
| |
DEBUG(SSSDBG_TRACE_ALL, "DACL is not present\n");
|
| |
access_allowed = true;
|
| |
- break;
|
| |
- }
|
| |
-
|
| |
- ret = ad_gpo_evaluate_dacl(dacl, idmap_ctx, user_sid, group_sids,
|
| |
- group_size, &access_allowed);
|
| |
- if (ret != EOK) {
|
| |
- DEBUG(SSSDBG_MINOR_FAILURE, "Could not determine if GPO is applicable\n");
|
| |
- continue;
|
| |
}
|
| |
|
| |
if (access_allowed) {
|
| |
- DEBUG(SSSDBG_TRACE_ALL,
|
| |
+ DEBUG(SSSDBG_TRACE_FUNC,
|
| |
"GPO applicable to target per security filtering\n");
|
| |
dacl_filtered_gpos[gpo_dn_idx] = talloc_steal(dacl_filtered_gpos,
|
| |
candidate_gpo);
|
| |
gpo_dn_idx++;
|
| |
} else {
|
| |
- DEBUG(SSSDBG_TRACE_ALL,
|
| |
+ DEBUG(SSSDBG_TRACE_FUNC,
|
| |
"GPO not applicable to target per security filtering: "
|
| |
"result of DACL evaluation\n");
|
| |
continue;
|
| |
Please find a summary and further information in the first commit, which was a merge commit of the fix branch:
Commit b2cdf58 [PATCH 0/6] Fix group policy security filtering and download.