ad_gpo.c revision d3ca320a1ddea52fe86c052dd5521b8f98bb4f9f
/*
SSSD
Authors:
Yassir Elley <yelley@redhat.com>
Copyright (C) 2013 Red Hat
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/>.
*/
/*
* This file implements the following pair of *public* functions (see header):
* ad_gpo_access_send/recv: provides client-side GPO processing
*
* This file also implements the following pairs of *private* functions (which
* are used by the public functions):
* ad_gpo_process_som_send/recv: populate list of gp_som objects
* ad_gpo_process_gpo_send/recv: populate list of gp_gpo objects
* ad_gpo_process_cse_send/recv: retrieve policy file data
*/
#include <security/pam_modules.h>
#include "util/strtonum.h"
#include "util/child_common.h"
#include "providers/data_provider.h"
#include "providers/dp_backend.h"
#include "providers/ad/ad_access.h"
#include "providers/ad/ad_common.h"
#include "providers/ad/ad_domain_info.h"
#include "providers/ldap/sdap_access.h"
#include "providers/ldap/sdap_async.h"
#include "providers/ldap/sdap_idmap.h"
#include "util/util_sss_idmap.h"
#include <ndr.h>
#include <gen_ndr/security.h>
/* == gpo-ldap constants =================================================== */
#define AD_AT_DN "distinguishedName"
#define AD_AT_UAC "userAccountControl"
#define AD_AT_CONFIG_NC "configurationNamingContext"
#define AD_AT_GPLINK "gPLink"
#define AD_AT_GPOPTIONS "gpOptions"
#define AD_AT_NT_SEC_DESC "nTSecurityDescriptor"
#define AD_AT_CN "cn"
#define AD_AT_DISPLAY_NAME "displayName"
#define AD_AT_FILE_SYS_PATH "gPCFileSysPath"
#define AD_AT_VERSION_NUMBER "versionNumber"
#define AD_AT_MACHINE_EXT_NAMES "gPCMachineExtensionNames"
#define AD_AT_USER_EXT_NAMES "gPCUserExtensionNames"
#define AD_AT_FUNC_VERSION "gPCFunctionalityVersion"
#define AD_AT_FLAGS "flags"
#define UAC_WORKSTATION_TRUST_ACCOUNT 0x00001000
#define AD_AGP_GUID "edacfd8f-ffb3-11d1-b41d-00a0c968f939"
#define AD_AUTHENTICATED_USERS_SID "S-1-5-11"
/* == gpo-smb constants ==================================================== */
#define SMB_STANDARD_URI "smb://"
#define GPO_VERSION_USER(x) (x >> 16)
#define GPO_VERSION_MACHINE(x) (x & 0xffff)
#define GP_EXT_GUID_SECURITY "{827D319E-6EAC-11D2-A4EA-00C04F79F83A}"
#define GP_EXT_GUID_SECURITY_SUFFIX "/Microsoft/Windows NT/SecEdit/GptTmpl.inf"
#ifndef SSSD_LIBEXEC_PATH
#error "SSSD_LIBEXEC_PATH not defined"
#else
#endif
/* fd used by the gpo_child process for logging */
int gpo_child_debug_fd = -1;
/* == common data structures and declarations ============================= */
struct gp_som {
const char *som_dn;
struct gp_gplink **gplink_list;
int num_gplinks;
};
struct gp_gplink {
const char *gpo_dn;
bool enforced;
};
struct gp_gpo {
struct security_descriptor *gpo_sd;
const char *gpo_dn;
const char *gpo_guid;
const char *gpo_display_name;
const char *gpo_file_sys_path;
const char **gpo_cse_guids;
int num_gpo_cse_guids;
int gpo_func_version;
int gpo_flags;
};
enum ace_eval_status {
};
struct tevent_context *ev,
struct sdap_id_conn_ctx *conn,
struct ldb_context *ldb_ctx,
struct sdap_id_op *sdap_op,
struct sdap_options *opts,
int timeout,
const char *target_dn,
const char *domain_name);
struct tevent_context *ev,
struct sdap_id_op *sdap_op,
struct sdap_options *opts,
char *server_hostname,
int timeout,
struct gp_gpo ***candidate_gpos,
int *num_candidate_gpos);
struct tevent_context *ev,
char *smb_uri);
int *_allowed_size,
char ***_allowed_sids,
int *_denied_size,
char ***_denied_sids);
/* == ad_gpo_access_send/recv helpers =======================================*/
static bool
{
int i;
return true;
}
return false;
}
return false;
}
for (i = 0; i < 6; i++) {
return false;
}
}
return false;
}
return false;
}
}
return true;
}
/*
* This function retrieves the SIDs corresponding to the input user and returns
* the user_sid, group_sids, and group_size in their respective output params.
*
* Note: since authentication must complete successfully before the
* has been authenticated. As such, this function always adds the
* AD_AUTHENTICATED_USERS_SID to the group_sids.
*/
static errno_t
const char *user,
struct sss_domain_info *domain,
const char **_user_sid,
const char ***_group_sids,
int *_group_size)
{
struct ldb_result *res;
int ret = 0;
int i = 0;
int num_group_sids = 0;
const char **group_sids = NULL;
goto done;
}
/* first result from sysdb_initgroups is user_sid; rest are group_sids */
"sysdb_initgroups failed: [%d](%s)\n",
return ret;
}
"sysdb_initgroups returned empty result\n");
return ret;
}
/* include space for AD_AUTHENTICATED_USERS_SID and NULL */
if (group_sids == NULL) {
goto done;
}
for (i = 0; i < num_group_sids; i++) {
continue;
}
if (group_sids[i] == NULL) {
goto done;
}
}
group_sids[i] = NULL;
return EOK;
done:
return ret;
}
/*
* This function determines whether the input ace_dom_sid matches any of the
* client's SIDs. The boolean result is assigned to the _included output param.
*/
static errno_t
ad_gpo_ace_includes_client_sid(const char *user_sid,
const char **group_sids,
int group_size,
struct dom_sid ace_dom_sid,
struct sss_idmap_ctx *idmap_ctx,
bool *_included)
{
int i = 0;
struct dom_sid *user_dom_sid;
struct dom_sid *group_dom_sid;
enum idmap_error_code err;
bool included = false;
if (err != IDMAP_SUCCESS) {
return EFAULT;
}
if (included) {
*_included = true;
return EOK;
}
for (i = 0; i < group_size; i++) {
if (err != IDMAP_SUCCESS) {
return EFAULT;
}
if (included) {
*_included = true;
return EOK;
}
}
*_included = false;
return EOK;
}
/*
* 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 returns ALLOWED, DENIED, or NEUTRAL depending on whether
* the ACE explictly 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
* 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.
*
* 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.
*/
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;
struct GUID ext_right_agp_guid;
return AD_GPO_ACE_NEUTRAL;
}
return AD_GPO_ACE_DENIED;
}
if (!included) {
return AD_GPO_ACE_NEUTRAL;
}
agp_included = true;
}
} else {
agp_included = false;
}
if (agp_included) {
return AD_GPO_ACE_ALLOWED;
return AD_GPO_ACE_DENIED;
}
}
}
return AD_GPO_ACE_DENIED;
}
/*
* 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.
*/
struct sss_idmap_ctx *idmap_ctx,
const char *user_sid,
const char **group_sids,
int group_size,
bool *_dacl_access_allowed)
{
enum ace_eval_status ace_status;
int i = 0;
/*
* [MS-ADTS] 5.1.3.3.4:
* If the DACL does not have any ACE, then deny the requester the
* requested control access right.
*/
if (num_aces == 0) {
*_dacl_access_allowed = false;
return EOK;
}
switch (ace_status) {
case AD_GPO_ACE_NEUTRAL:
continue;
case AD_GPO_ACE_ALLOWED:
*_dacl_access_allowed = true;
return EOK;
case AD_GPO_ACE_DENIED:
*_dacl_access_allowed = false;
return EOK;
}
}
*_dacl_access_allowed = false;
return EOK;
}
/*
* This function takes candidate_gpos as input, filters out any gpo that is
* not applicable to the policy target and assigns the result to the
* _dacl_filtered_gpos output parameter. The filtering algorithm is
* defined in [MS-GPOL] 3.2.5.1.6
*/
static errno_t
const char *user,
struct sss_domain_info *domain,
struct sss_idmap_ctx *idmap_ctx,
struct gp_gpo **candidate_gpos,
int num_candidate_gpos,
struct gp_gpo ***_dacl_filtered_gpos,
int *_num_dacl_filtered_gpos)
{
int i = 0;
int ret = 0;
const char **group_sids = NULL;
int group_size = 0;
int gpo_dn_idx = 0;
bool access_allowed = false;
goto done;
}
&group_sids, &group_size);
ret = ERR_NO_SIDS;
goto done;
}
struct gp_gpo *,
num_candidate_gpos + 1);
if (dacl_filtered_gpos == NULL) {
goto done;
}
for (i = 0; i < num_candidate_gpos; i++) {
access_allowed = false;
candidate_gpo = candidate_gpos[i];
/* gpo_func_version must be set to version 2 */
"GPO not applicable to target per security filtering\n");
continue;
}
/* gpo_flags value of 2 means that GPO's computer portion is disabled */
"GPO not applicable to target per security filtering\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.
*/
access_allowed = true;
break;
}
if (access_allowed) {
"GPO applicable to target per security filtering\n");
gpo_dn_idx++;
} else {
"GPO not applicable to target per security filtering\n");
continue;
}
}
done:
return ret;
}
/*
* This function determines whether the input cse_guid matches any of the input
* gpo_cse_guids. The boolean result is assigned to the _included output param.
*/
static bool
ad_gpo_includes_cse_guid(const char *cse_guid,
const char **gpo_cse_guids,
int num_gpo_cse_guids)
{
int i = 0;
const char *gpo_cse_guid = NULL;
for (i = 0; i < num_gpo_cse_guids; i++) {
gpo_cse_guid = gpo_cse_guids[i];
return true;
}
}
return false;
}
/*
* This function takes an input dacl_filtered_gpos list, filters out any gpo
* that does not contain the input cse_guid, and assigns the result to the
* _cse_filtered_gpos output parameter.
*/
static errno_t
const char *cse_guid,
struct gp_gpo **dacl_filtered_gpos,
struct gp_gpo ***_cse_filtered_gpos,
int *_num_cse_filtered_gpos)
{
int i = 0;
int ret = 0;
int gpo_dn_idx = 0;
bool included;
goto done;
}
struct gp_gpo *,
num_dacl_filtered_gpos + 1);
if (cse_filtered_gpos == NULL) {
goto done;
}
for (i = 0; i < num_dacl_filtered_gpos; i++) {
if (included) {
"GPO applicable to target per cse_guid filtering\n");
dacl_filtered_gpos[i] = NULL;
gpo_dn_idx++;
} else {
"GPO not applicable to target per cse_guid filtering\n");
continue;
}
}
done:
return ret;
}
/*
* This cse-specific function (GP_EXT_GUID_SECURITY) populates the output
* parameter (found) based on whether the input user_sid or any of the input
* group_sids appear in the input list of privilege_sids.
*/
static bool
check_rights(char **privilege_sids,
int privilege_size,
const char *user_sid,
const char **group_sids,
int group_size)
{
int i, j;
for (i = 0; i < privilege_size; i++) {
return true;
}
for (j = 0; j < group_size; j++) {
return true;
}
}
}
return false;
}
/*
* This cse-specific function (GP_EXT_GUID_SECURITY) performs HBAC policy
* application and determines whether logon access is granted or denied for
* the {user,domain} tuple specified in the inputs. This function returns EOK
* to indicate that access is granted. Any other return value indicates that
* access is denied.
*
* The access control algorithm first determines whether the "principal_sids"
* (i.e. user_sid or group_sids) appear in allowed_sids and denied_sids.
*
* For access to be granted, both the "allowed_sids_condition" *and* the
* "denied_sids_condition" must be met (in all other cases, access is denied).
* 1) The "allowed_sids_condition" is satisfied if any of the principal_sids
* appears in allowed_sids OR if the allowed_sids list is empty
* 2) The "denied_sids_condition" is satisfied if none of the principal_sids
* appear in denied_sids
*
* Note that a deployment that is unaware of GPO-based access-control policy
* settings is unaffected by them (b/c the absence of allowed_sids grants access).
*
* Note that if a principal_sid appears in both allowed_sids and denied_sids,
* the "allowed_sids_condition" is met, but the "denied_sids_condition" is not.
* In other words, Deny takes precedence over Allow.
*/
static errno_t
const char *user,
struct sss_domain_info *domain,
char **allowed_sids,
int allowed_size,
char **denied_sids,
int denied_size)
{
const char *user_sid;
const char **group_sids;
int group_size = 0;
bool access_granted = false;
bool access_denied = false;
int ret;
int j;
for (j= 0; j < allowed_size; j++) {
allowed_sids[j]);
}
for (j= 0; j < denied_size; j++) {
denied_sids[j]);
}
&group_sids, &group_size);
ret = ERR_NO_SIDS;
goto done;
}
for (j= 0; j < group_size; j++) {
group_sids[j]);
}
/* If AllowLogonLocally is not defined, all users are allowed */
if (allowed_size == 0) {
access_granted = true;
} else {
}
if (access_granted && !access_denied) {
return EOK;
} else {
return EACCES;
}
done:
if (ret) {
}
return ret;
}
#define GPO_CHILD_LOG_FILE "gpo_child"
static errno_t gpo_child_init(void)
{
int ret;
return ret;
}
if (gpo_child_debug_fd == -1) {
return ret;
}
}
return EOK;
}
/* == ad_gpo_access_send/recv implementation ================================*/
struct ad_gpo_access_state {
struct tevent_context *ev;
struct ldb_context *ldb_ctx;
struct sdap_id_conn_ctx *conn;
struct sdap_id_op *sdap_op;
char *server_hostname;
struct sdap_options *opts;
int timeout;
struct sss_domain_info *domain;
const char *user;
const char *ad_hostname;
const char *target_dn;
struct gp_gpo **dacl_filtered_gpos;
struct gp_gpo **cse_filtered_gpos;
int cse_gpo_index;
};
struct tevent_req *
struct tevent_context *ev,
struct sss_domain_info *domain,
struct ad_access_ctx *ctx,
const char *user)
{
struct tevent_req *req;
struct tevent_req *subreq;
struct ad_gpo_access_state *state;
char *server_uri;
/* setup logging for gpo child */
return NULL;
}
state->num_dacl_filtered_gpos = 0;
state->num_cse_filtered_gpos = 0;
goto immediately;
}
/* extract server_hostname from server_uri */
if (ret != LDAP_SUCCESS) {
"Failed to parse ldap URI (%s)!\n", server_uri);
goto immediately;
}
"The LDAP URI (%s) did not contain a host name\n", server_uri);
goto immediately;
}
if (!state->server_hostname) {
goto immediately;
}
"sdap_id_op_connect_send failed: [%d](%s)\n",
goto immediately;
}
}
return req;
}
static void
{
struct tevent_req *req;
struct ad_gpo_access_state *state;
char* filter;
char *sam_account_name;
char *domain_dn;
int dp_error;
/* TBD: handle (dp_error == DP_ERR_OFFLINE) case */
"Failed to connect to AD server: [%d](%s)\n",
return;
}
if (sam_account_name == NULL) {
return;
}
/* Convert the domain name into domain DN */
"Cannot convert domain name [%s] to base DN [%d]: %s\n",
return;
}
/* SDAP_OC_USER objectclass covers both users and computers */
"(&(objectclass=%s)(%s=%s))",
return;
}
false);
return;
}
}
static void
{
struct tevent_req *req;
struct ad_gpo_access_state *state;
int ret;
int dp_error;
struct sysdb_attrs **reply;
&reply_count, &reply);
/* TBD: handle (dp_error == DP_ERR_OFFLINE) case */
"Unable to get policy target's DN: [%d](%s)\n",
goto done;
}
/* make sure there is only one non-NULL reply returned */
if (reply_count < 1) {
goto done;
} else if (reply_count > 1) {
ret = ERR_INTERNAL;
goto done;
ret = ERR_INTERNAL;
goto done;
}
/* reply[0] holds requested attributes of single reply */
"sysdb_attrs_get_string failed: [%d](%s)\n",
goto done;
}
goto done;
}
"sysdb_attrs_get_uint32_t failed: [%d](%s)\n",
goto done;
}
/* we only support computer policy targets, not users */
if (!(uac & UAC_WORKSTATION_TRUST_ACCOUNT)) {
goto done;
}
goto done;
}
done:
}
}
static void
{
struct tevent_req *req;
struct ad_gpo_access_state *state;
int ret;
"Unable to get som list: [%d](%s)\n",
goto done;
}
som_list);
goto done;
}
done:
}
}
/*
* This function retrieves a list of candidate_gpos and potentially reduces it
* to a list of dacl_filtered_gpos, based on each GPO's DACL.
*
* This function then takes the list of dacl_filtered_gpos and potentially
* reduces it to a list of cse_filtered_gpos, based on whether each GPO's list
* of cse_guids includes the "SecuritySettings" CSE GUID (used for HBAC).
*
* This function then sends each cse_filtered_gpo to the CSE processing engine
* for policy application, which currently consists of HBAC functionality.
*/
static void
{
struct tevent_req *req;
struct ad_gpo_access_state *state;
int ret;
int dp_error;
int num_candidate_gpos = 0;
int i = 0;
/* TBD: handle (dp_error == DP_ERR_OFFLINE) case */
"Unable to get GPO list: [%d](%s)\n",
goto done;
}
"Unable to filter GPO list by DACKL: [%d](%s)\n",
goto done;
}
/* since no applicable gpos were found, there is nothing to enforce */
"no applicable gpos found after dacl filtering\n");
goto done;
}
for (i = 0; i < state->num_dacl_filtered_gpos; i++) {
}
"Unable to filter GPO list by CSE_GUID: [%d](%s)\n",
goto done;
}
/* no gpos contain "SecuritySettings" cse_guid, nothing to enforce */
"no applicable gpos found after cse_guid filtering\n");
goto done;
}
for (i = 0; i < state->num_cse_filtered_gpos; i++) {
}
done:
}
}
static errno_t
{
struct tevent_req *subreq;
struct ad_gpo_access_state *state;
char *smb_uri;
int i = 0;
state->cse_gpo_index++;
struct gp_gpo *cse_filtered_gpo =
/* cse_filtered_gpo is NULL only after all GPOs have been processed */
for (i = 0; i < cse_filtered_gpo->num_gpo_cse_guids; i++) {
"cse_filtered_gpos[%d]->gpo_cse_guids[%d]->gpo_guid is %s\n",
}
return EAGAIN;
}
/*
* This cse-specific function (GP_EXT_GUID_SECURITY) retrieves a list of
* allowed_sids and denied_sids, and uses them to determine whether logon
* access is granted or denied for the state's {user, domain} tuple.
*
* If it is determined that the current cse_filtered_gpo grants access, then
* we process the next cse_filtered_gpo in the list. At any time, if access is
* denied, we return immediately with an error.
*/
static void
{
struct tevent_req *req;
struct ad_gpo_access_state *state;
int ret;
char **allowed_sids;
int allowed_size;
char **denied_sids;
int denied_size;
&denied_size, &denied_sids);
/* TBD: handle ret error */
goto done;
}
/* TBD: allowed/denied_sids/size, should be retrieved from cache */
"GPO access check failed: [%d](%s)\n",
goto done;
}
done:
}
}
{
return EOK;
}
/* == ad_gpo_process_som_send/recv helpers ================================= */
/*
* This function returns the parent of an LDAP DN
*/
static errno_t
struct ldb_context *ldb_ctx,
const char *dn,
const char **_parent_dn)
{
struct ldb_dn *parent_ldb_dn;
const char *p;
int ret;
goto done;
}
done:
return ret;
}
/*
* This function populates the _som_list output parameter by parsing the input
* DN into a list of gp_som objects. This function essentially repeatedly
* appends the input DN's parent to the SOM List (if the parent starts with
* "OU=" or "DC="), until the first "DC=" component is reached.
* Example: if input DN is "CN=MyComputer,CN=Computers,OU=Sales,DC=FOO,DC=COM",
* then SOM List has 2 SOM entries: {[OU=Sales,DC=FOO,DC=COM], [DC=FOO, DC=COM]}
*/
static errno_t
struct ldb_context *ldb_ctx,
const char *target_dn,
int *_num_soms,
{
int ret;
int rdn_count = 0;
int som_idx = 0;
struct ldb_dn *ldb_target_dn;
goto done;
}
if (ldb_target_dn == NULL) {
goto done;
}
if (rdn_count == -1) {
goto done;
}
if (rdn_count == 0) {
goto done;
}
/* assume the worst-case, in which every parent is a SOM */
/* include space for Site SOM and NULL: rdn_count + 1 + 1 */
goto done;
}
/* first, populate the OU and Domain SOMs */
goto done;
}
goto done;
}
som_idx++;
}
break;
}
}
done:
return ret;
}
/*
* This function populates the _gplink_list output parameter by parsing the
* input raw_gplink_value into an array of gp_gplink objects, each consisting of
* a GPO DN and bool enforced field.
*
* The raw_gplink_value is single string consisting of multiple gplink strings.
* The raw_gplink_value is in the following format:
* "[GPO_DN_1;GPLinkOptions_1]...[GPO_DN_n;GPLinkOptions_n]"
*
* Each gplink string consists of a GPO DN and a GPLinkOptions field (which
* indicates whether its associated GPO DN is ignored, unenforced, or enforced).
* If a GPO DN is flagged as ignored, it is discarded and will not be added to
* the _gplink_list. If the allow_enforced_only input is true, AND a GPO DN is
* flagged as unenforced, it will also be discarded.
*
* Example: if raw_gplink_value="[OU=Sales,DC=FOO,DC=COM;0][DC=FOO,DC=COM;2]"
* and allow_enforced_only=FALSE, then the output would consist of following:
* _gplink_list[0]: {GPO DN: "OU=Sales,DC=FOO,DC=COM", enforced: FALSE}
* _gplink_list[1]: {GPO DN: "DC=FOO,DC=COM", enforced: TRUE}
*/
static errno_t
const char *som_dn,
char *raw_gplink_value,
struct gp_gplink ***_gplink_list,
bool allow_enforced_only)
{
char *ptr;
char *first;
char *last;
char *dn;
char *gplink_options;
const char delim = ']';
struct gp_gplink **gplink_list;
int i;
int ret;
int gplink_count = 0;
int num_enabled = 0;
if (raw_gplink_value == NULL ||
*raw_gplink_value == '\0' ||
_gplink_list == NULL) {
return EINVAL;
}
goto done;
}
ptr++;
gplink_count++;
}
if (gplink_count == 0) {
goto done;
}
if (gplink_list == NULL) {
goto done;
}
num_enabled = 0;
for (i = 0; i < gplink_count; i++) {
goto done;
}
*last = '\0';
last++;
}
if (gplink_options == NULL) {
goto done;
}
*gplink_options = '\0';
if (errno != 0) {
goto done;
}
/* ignore flag is set */
continue;
}
if (allow_enforced_only && (gplink_number == 0)) {
/* unenforced flag is set; only enforced gpos allowed */
continue;
}
goto done;
}
goto done;
}
if (gplink_number == 0) {
num_enabled++;
} else if (gplink_number == 2) {
num_enabled++;
} else {
goto done;
}
}
done:
return ret;
}
/* == ad_gpo_process_som_send/recv implementation ========================== */
struct ad_gpo_process_som_state {
struct tevent_context *ev;
struct sdap_id_op *sdap_op;
struct sdap_options *opts;
int timeout;
bool allow_enforced_only;
char *site_name;
char *site_dn;
int som_index;
int num_soms;
};
/*
* This function uses the input target_dn and input domain_name to populate
* a list of gp_som objects. Each object in this list represents a SOM
* associated with the target (such as OU, Domain, and Site).
*
* The inputs are used to determine the DNs of each SOM associated with the
* target. In turn, the SOM object DNs are used to retrieve certain LDAP
* attributes of each SOM object, that are parsed into an array of gp_gplink
* objects, essentially representing the GPOs that have been linked to each
* SOM object. Note that it is perfectly valid for there to be *no* GPOs
* linked to a SOM object.
*/
struct tevent_req *
struct tevent_context *ev,
struct sdap_id_conn_ctx *conn,
struct ldb_context *ldb_ctx,
struct sdap_id_op *sdap_op,
struct sdap_options *opts,
int timeout,
const char *target_dn,
const char *domain_name)
{
struct tevent_req *req;
struct tevent_req *subreq;
struct ad_gpo_process_som_state *state;
return NULL;
}
state->allow_enforced_only = 0;
"Unable to retrieve SOM List : [%d](%s)\n",
goto immediately;
}
goto immediately;
}
goto immediately;
}
}
return req;
}
static void
{
struct tevent_req *req;
struct ad_gpo_process_som_state *state;
int ret;
char *site;
/* gpo code only cares about the site name */
return;
}
return;
}
/*
* note: the configNC attribute is being retrieved here from the rootDSE
* entry. In future, since we already make an LDAP query for the rootDSE
* entry when LDAP connection is made, this attribute should really be
* retrieved at that point (see https://fedorahosted.org/sssd/ticket/2276)
*/
"", LDAP_SCOPE_BASE,
false);
return;
}
}
static void
{
struct tevent_req *req;
struct ad_gpo_process_som_state *state;
int ret;
int dp_error;
int i = 0;
struct sysdb_attrs **reply;
const char *configNC;
&reply_count, &reply);
/* TBD: handle (dp_error == DP_ERR_OFFLINE) case */
goto done;
}
/* make sure there is only one non-NULL reply returned */
if (reply_count < 1) {
goto done;
} else if (reply_count > 1) {
ret = ERR_INTERNAL;
goto done;
ret = ERR_INTERNAL;
goto done;
}
/* reply[0] holds requested attributes of single reply */
"sysdb_attrs_get_string failed: [%d](%s)\n",
goto done;
}
goto done;
}
/* note that space was allocated for site_dn when allocating som_list */
goto done;
}
goto done;
}
i = 0;
i++;
}
done:
}
}
static errno_t
{
struct tevent_req *subreq;
struct ad_gpo_process_som_state *state;
/* gp_som is NULL only after all SOMs have been processed */
false);
return ENOMEM;
}
return EAGAIN;
}
static void
{
struct tevent_req *req;
struct ad_gpo_process_som_state *state;
int ret;
int dp_error;
struct sysdb_attrs **results;
&num_results, &results);
/* TBD: handle (dp_error == DP_ERR_OFFLINE) case */
"Unable to get SOM attributes: [%d](%s)\n",
goto done;
}
goto done;
} else if (num_results > 1) {
ret = ERR_INTERNAL;
goto done;
}
/* Get the gplink value, if available */
"sysdb_attrs_get_el() failed: [%d](%s)\n",
goto done;
}
goto done;
}
goto done;
}
"gpoptions attr not found or has no value; defaults to 0\n");
allow_enforced_only = 0;
} else {
if (errno != 0) {
goto done;
}
}
(char *)raw_gplink_value,
"ad_gpo_populate_gplink_list() failed\n");
goto done;
}
if (allow_enforced_only) {
}
done:
}
}
int
{
struct ad_gpo_process_som_state *state =
return EOK;
}
/* == ad_gpo_process_gpo_send/recv helpers ================================= */
/*
* This function examines the gp_gplink objects in each gp_som object specified
* in the input som_list, and populates the _candidate_gpos output parameter's
* gpo_dn fields with prioritized list of GPO DNs. Prioritization ensures that:
* - GPOs linked to an OU will be applied after GPOs linked to a Domain,
* which will be applied after GPOs linked to a Site.
* - multiple GPOs linked to a single SOM are applied in their link order
* (i.e. 1st GPO linked to SOM is applied after 2nd GPO linked to SOM, etc).
* - enforced GPOs are applied after unenforced GPOs.
*
* As such, the _candidate_gpos output's dn fields looks like (in link order):
* [unenforced {Site, Domain, OU}; enforced {Site, Domain, OU}]
*
* Note that in the case of conflicting policy settings, GPOs appearing later
* in the list will trump GPOs appearing earlier in the list.
*/
static errno_t
struct gp_gpo ***_candidate_gpos,
int *_num_candidate_gpos)
{
int num_candidate_gpos = 0;
const char **enforced_gpo_dns = NULL;
const char **unenforced_gpo_dns = NULL;
int gpo_dn_idx = 0;
int num_enforced = 0;
int enforced_idx = 0;
int num_unenforced = 0;
int unenforced_idx = 0;
int i = 0;
int j = 0;
int ret;
goto done;
}
while (som_list[i]) {
j = 0;
goto done;
}
num_enforced++;
} else {
}
j++;
}
i++;
}
if (num_candidate_gpos == 0) {
*_candidate_gpos = NULL;
*_num_candidate_gpos = 0;
goto done;
}
if (enforced_gpo_dns == NULL) {
goto done;
}
if (unenforced_gpo_dns == NULL) {
goto done;
}
i = 0;
while (som_list[i]) {
j = 0;
goto done;
}
goto done;
}
enforced_idx++;
} else {
goto done;
}
}
j++;
}
i++;
}
struct gp_gpo *,
num_candidate_gpos + 1);
if (candidate_gpos == NULL) {
goto done;
}
gpo_dn_idx = 0;
for (i = num_unenforced - 1; i >= 0; i--) {
goto done;
}
goto done;
}
"candidate_gpos[%d]->gpo_dn: %s\n",
gpo_dn_idx++;
}
for (i = 0; i < num_enforced; i++) {
goto done;
}
goto done;
}
"candidate_gpos[%d]->gpo_dn: %s\n",
gpo_dn_idx++;
}
done:
return ret;
}
/*
* This function converts the input_path to an smb uri, which is used to
* populate the _converted_path output parameter. The output is constructed by
* concatenating the following elements:
* - SMB_STANDARD_URI ("smb://")
* - server_hostname (which replaces domain_name in input path)
* - smb_path (which starts with the slash immediately after the domain name
* Additionally, each forward slash ('\') is replaced with a back slash ('/')
*
* server_hostname = "adserver.foo.com", then _converted_path would be
* "smb://adserver.foo.com/SysVol/foo.com/..."
*
* Note that the input_path must have at least three forward slash separators.
* For example, input_path = "\\foo.com" is not a valid input_path, because
* it has only two forward slash separators.
*/
static errno_t
char *server_hostname,
char *input_path,
const char **_converted_path)
{
char *ptr;
const char delim = '\\';
int ret;
int num_seps = 0;
if (input_path == NULL ||
*input_path == '\0' ||
_converted_path == NULL) {
goto done;
}
ptr = input_path;
num_seps++;
if (num_seps == 3) {
/* keep track of path from third slash onwards (after domain name) */
}
*ptr = '/';
ptr++;
}
if (num_seps == 0) {
goto done;
}
goto done;
}
smb_path);
done:
return ret;
}
/*
* This function populates the _cse_guid_list output parameter by parsing the
* input raw_machine_ext_names_value into an array of cse_guid strings.
*
* The raw_machine_ext_names_value is a single string in the following format:
* "[{cse_guid_1}{tool_guid1}]...[{cse_guid_n}{tool_guid_n}]"
*/
static errno_t
char *raw_machine_ext_names_value,
const char ***_gpo_cse_guids,
int *_num_gpo_cse_guids)
{
char *ptr;
char *first;
char *last;
char *cse_guid;
char *tool_guid;
const char delim = ']';
const char **gpo_cse_guids;
int i;
int ret;
int num_gpo_cse_guids = 0;
if (raw_machine_ext_names_value == NULL ||
*raw_machine_ext_names_value == '\0' ||
_gpo_cse_guids == NULL) {
return EINVAL;
}
goto done;
}
ptr++;
}
if (num_gpo_cse_guids == 0) {
goto done;
}
if (gpo_cse_guids == NULL) {
goto done;
}
for (i = 0; i < num_gpo_cse_guids; i++) {
break;
}
*last = '\0';
last++;
first ++;
break;
}
*tool_guid = '\0';
}
gpo_cse_guids[i] = NULL;
for (i = 0; i < num_gpo_cse_guids; i++) {
"gpo_cse_guids[%d] is %s\n", i, gpo_cse_guids[i]);
}
done:
return ret;
}
enum ndr_err_code
struct security_descriptor *r);
/*
* This function parses the input data blob and assigns the resulting
* security_descriptor object to the _gpo_sd output parameter.
*/
struct security_descriptor **_gpo_sd)
{
struct security_descriptor sd;
enum ndr_err_code ndr_err;
return EINVAL;
}
&sd);
if (ndr_err != NDR_ERR_SUCCESS) {
return EINVAL;
}
return EOK;
}
/* == ad_gpo_process_gpo_send/recv implementation ========================== */
struct ad_gpo_process_gpo_state {
struct tevent_context *ev;
struct sdap_id_op *sdap_op;
struct sdap_options *opts;
char *server_hostname;
int timeout;
struct gp_gpo **candidate_gpos;
int num_candidate_gpos;
int gpo_index;
};
/*
* This function uses the input som_list to populate a prioritized list of
* gp_gpo objects, prioritized based on SOM type, link order, and whether the
* GPO is "enforced". This list represents the initial set of candidate GPOs
* that might be applicable to the target. This list can not be expanded, but
* it might be reduced based on subsequent filtering steps. The GPO object DNs
* are used to retrieve certain LDAP attributes of each GPO object, that are
* parsed into the various fields of the gp_gpo object.
*/
struct tevent_req *
struct tevent_context *ev,
struct sdap_id_op *sdap_op,
struct sdap_options *opts,
char *server_hostname,
int timeout,
{
struct tevent_req *req;
struct ad_gpo_process_gpo_state *state;
return NULL;
}
state->num_candidate_gpos = 0;
"Unable to retrieve GPO List: [%d](%s)\n",
goto immediately;
}
goto immediately;
}
}
return req;
}
static errno_t
{
AD_AT_FLAGS, NULL};
struct tevent_req *subreq;
struct ad_gpo_process_gpo_state *state;
/* gp_gpo is NULL only after all GPOs have been processed */
return ENOMEM;
}
return EAGAIN;
}
static void
{
struct tevent_req *req;
struct ad_gpo_process_gpo_state *state;
int ret;
int dp_error;
struct sysdb_attrs **results;
const char *gpo_display_name = NULL;
const char *raw_file_sys_path = NULL;
char *file_sys_path = NULL;
/* TBD: handle (dp_error == DP_ERR_OFFLINE) case */
"Unable to get GPO attributes: [%d](%s)\n",
goto done;
}
goto done;
}
else if (num_results > 1) {
ret = ERR_INTERNAL;
goto done;
}
/* retrieve AD_AT_CN */
"sysdb_attrs_get_string failed: [%d](%s)\n",
goto done;
}
goto done;
}
/* retrieve AD_AT_DISPLAY_NAME */
"sysdb_attrs_get_string failed: [%d](%s)\n",
goto done;
}
goto done;
}
/* retrieve AD_AT_FILE_SYS_PATH */
"sysdb_attrs_get_string failed: [%d](%s)\n",
goto done;
}
&smb_uri);
smb_uri);
goto done;
}
/* retrieve AD_AT_VERSION_NUMBER */
"sysdb_attrs_get_uint32_t failed: [%d](%s)\n",
goto done;
}
/* retrieve AD_AT_MACHINE_EXT_NAMES */
goto done;
}
"machine_ext_names not found or has no value\n");
goto done;
}
(char *)raw_machine_ext_names,
"ad_gpo_parse_machine_ext_names() failed\n");
goto done;
}
/* retrieve AD_AT_FUNC_VERSION */
"sysdb_attrs_get_int32_t failed: [%d](%s)\n",
goto done;
}
/* retrieve AD_AT_FLAGS */
"sysdb_attrs_get_int32_t failed: [%d](%s)\n",
goto done;
}
/* retrieve AD_AT_NT_SEC_DESC */
goto done;
}
"nt_sec_desc attribute not found or has no value\n");
goto done;
}
goto done;
}
done:
}
}
int
struct gp_gpo ***candidate_gpos,
int *num_candidate_gpos)
{
struct ad_gpo_process_gpo_state *state =
return EOK;
}
/* == ad_gpo_process_cse_send/recv helpers ================================= */
static errno_t
char *smb_uri,
{
int smb_uri_length;
return ENOMEM;
}
return ENOMEM;
}
rp = 0;
/* smb_uri */
return EOK;
}
static errno_t
char ***_allowed_sids,
int *_allowed_size,
char ***_denied_sids,
int *_denied_size)
{
size_t p = 0;
int allowed_size = 0;
int denied_size = 0;
int i = 0;
int sid_len = 0;
char **allowed_sids;
char **denied_sids;
goto done;
}
/* operation result code */
/* allowed_size */
if (allowed_sids == NULL) {
goto done;
}
for (i = 0; i < allowed_size; i++) {
goto done;
}
(const char *)buf + p,
sid_len);
if (allowed_sids[i] == NULL) {
goto done;
}
p += sid_len;
}
/* denied_size */
if (denied_sids == NULL) {
goto done;
}
for (i = 0; i < denied_size; i++) {
goto done;
}
(const char *)buf + p,
sid_len);
if (denied_sids[i] == NULL) {
goto done;
}
p += sid_len;
}
done:
return ret;
}
/* == ad_gpo_process_cse_send/recv implementation ========================== */
struct ad_gpo_process_cse_state {
struct tevent_context *ev;
};
struct io {
int read_from_child_fd;
int write_to_child_fd;
};
static errno_t
gpo_child_io_destructor(void *ptr)
{
int ret;
}
}
}
}
return EOK;
}
/*
* This cse-specific function (GP_EXT_GUID_SECURITY) retrieves the data
* referenced by the input smb_uri, and uses the parsed results to populate the
* state's list of allowed_sids and denied_sids.
*/
struct tevent_req *
struct tevent_context *ev,
char *smb_uri)
{
struct tevent_req *req;
struct tevent_req *subreq;
struct ad_gpo_process_cse_state *state;
return NULL;
}
goto fail;
}
/* prepare the data to pass to child */
goto fail;
}
goto fail;
}
goto fail;
}
return req;
fail:
return req;
}
{
struct tevent_req *req;
struct ad_gpo_process_cse_state *state;
int ret;
return;
}
return;
}
}
{
struct tevent_req *req;
struct ad_gpo_process_cse_state *state;
int ret;
return;
}
return;
}
int *_allowed_size,
char ***_allowed_sids,
int *_denied_size,
char ***_denied_sids)
{
int ret;
char **allowed_sids;
int allowed_size;
char **denied_sids;
int denied_size;
struct ad_gpo_process_cse_state *state;
&denied_size);
return ret;
}
return EOK;
}
static errno_t
{
int pipefd_to_child[2];
int pipefd_from_child[2];
int ret;
struct ad_gpo_process_cse_state *state;
if (ret == -1) {
return err;
}
if (ret == -1) {
return err;
}
if (pid == 0) { /* child */
return err;
} else if (pid > 0) { /* parent */
close(pipefd_to_child[0]);
"Could not set up child signal handler\n");
return ret;
}
} else { /* error */
return err;
}
return EOK;
}