Hi,
The attached patch implements the LDAP component of the GPO-based access control project. For more details on the project, see https://fedorahosted.org/sssd/wiki/DesignDocs/ActiveDirectoryGPOIntegration. The project is not yet complete, since the SMB component still needs to be implemented. However, this seems like a useful milestone to allow others to review the code. This is my first patch submission to the list, so any feedback is appreciated.
In order to exercise the code in your AD environment, you will need to link a GPO to the site, domain, or OUs, associated with the policy target (i.e. domain member computer). Since we are not yet retrieving any policy files (over SMB), there is no access control taking place yet, meaning that any policy settings can be used in the GPOs. The only way to determine what is going on is by examining the logs.
In summary, the code currently does the following: 1. Determines the DN of the policy target (e.g. "cn=f20-laptop,OU=West,OU=Sales,DC=foo,DC=com") 2. Calculates the list of SOM DNs (Site, Domain and OUs) associated with the policy target DN 3. Retrieves various LDAP attributes from each SOM DN in order to populate a list of GPLinks (i.e. GPO DN plus GPO Options) 4. Creates a prioritized list of GPO DNs based on SOM Priority, Link Order, and GPO Options 5. Retrieves various LDAP attributes from each GPO DN in order to populate a list of candidate GPOs 6. Converts the candidate list to a filtered list, by removing any GPO that has a DACL which denies the policy target permission to apply the GPO 7. It is this filtered list that will be sent to the Short-Lived GPO Child process, which will make the necessary blocking SMB calls
Regards, Yassir.
On (12/03/14 18:20), Yassir Elley wrote:
Hi,
The attached patch implements the LDAP component of the GPO-based access control project. For more details on the project, see https://fedorahosted.org/sssd/wiki/DesignDocs/ActiveDirectoryGPOIntegration. The project is not yet complete, since the SMB component still needs to be implemented. However, this seems like a useful milestone to allow others to review the code. This is my first patch submission to the list, so any feedback is appreciated.
In order to exercise the code in your AD environment, you will need to link a GPO to the site, domain, or OUs, associated with the policy target (i.e. domain member computer). Since we are not yet retrieving any policy files (over SMB), there is no access control taking place yet, meaning that any policy settings can be used in the GPOs. The only way to determine what is going on is by examining the logs.
In summary, the code currently does the following:
- Determines the DN of the policy target (e.g. "cn=f20-laptop,OU=West,OU=Sales,DC=foo,DC=com")
- Calculates the list of SOM DNs (Site, Domain and OUs) associated with the policy target DN
- Retrieves various LDAP attributes from each SOM DN in order to populate a list of GPLinks (i.e. GPO DN plus GPO Options)
- Creates a prioritized list of GPO DNs based on SOM Priority, Link Order, and GPO Options
- Retrieves various LDAP attributes from each GPO DN in order to populate a list of candidate GPOs
- Converts the candidate list to a filtered list, by removing any GPO that has a DACL which denies the policy target permission to apply the GPO
- It is this filtered list that will be sent to the Short-Lived GPO Child process, which will make the necessary blocking SMB calls
Regards, Yassir.
We changed DEBUG messages recently and your patch cannot be compiled on current master. Probably, it happened when you rebased patches.
I am attaching which fixes these problems. And some (probably not all) code style issues: trailing whitespaces, tabs instead of spaces ...
Feel free to squash this patch to yours.
LS
----- Original Message -----
On (12/03/14 18:20), Yassir Elley wrote:
Hi,
The attached patch implements the LDAP component of the GPO-based access control project. For more details on the project, see https://fedorahosted.org/sssd/wiki/DesignDocs/ActiveDirectoryGPOIntegration. The project is not yet complete, since the SMB component still needs to be implemented. However, this seems like a useful milestone to allow others to review the code. This is my first patch submission to the list, so any feedback is appreciated.
In order to exercise the code in your AD environment, you will need to link a GPO to the site, domain, or OUs, associated with the policy target (i.e. domain member computer). Since we are not yet retrieving any policy files (over SMB), there is no access control taking place yet, meaning that any policy settings can be used in the GPOs. The only way to determine what is going on is by examining the logs.
In summary, the code currently does the following:
- Determines the DN of the policy target (e.g.
"cn=f20-laptop,OU=West,OU=Sales,DC=foo,DC=com") 2. Calculates the list of SOM DNs (Site, Domain and OUs) associated with the policy target DN 3. Retrieves various LDAP attributes from each SOM DN in order to populate a list of GPLinks (i.e. GPO DN plus GPO Options) 4. Creates a prioritized list of GPO DNs based on SOM Priority, Link Order, and GPO Options 5. Retrieves various LDAP attributes from each GPO DN in order to populate a list of candidate GPOs 6. Converts the candidate list to a filtered list, by removing any GPO that has a DACL which denies the policy target permission to apply the GPO 7. It is this filtered list that will be sent to the Short-Lived GPO Child process, which will make the necessary blocking SMB calls
Regards, Yassir.
We changed DEBUG messages recently and your patch cannot be compiled on current master. Probably, it happened when you rebased patches.
I am attaching which fixes these problems. And some (probably not all) code style issues: trailing whitespaces, tabs instead of spaces ...
Feel free to squash this patch to yours.
LS
sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
Hmmm... I did notice the recent DEBUG message changes and I went through and fixed each one so it compiled. I must have made a mistake somewhere when merging from my local gpo branch to my local master branch. Thanks for the patch!
Regards, Yassir.
----- Original Message -----
----- Original Message -----
On (12/03/14 18:20), Yassir Elley wrote:
Hi,
The attached patch implements the LDAP component of the GPO-based access control project. For more details on the project, see https://fedorahosted.org/sssd/wiki/DesignDocs/ActiveDirectoryGPOIntegration. The project is not yet complete, since the SMB component still needs to be implemented. However, this seems like a useful milestone to allow others to review the code. This is my first patch submission to the list, so any feedback is appreciated.
In order to exercise the code in your AD environment, you will need to link a GPO to the site, domain, or OUs, associated with the policy target (i.e. domain member computer). Since we are not yet retrieving any policy files (over SMB), there is no access control taking place yet, meaning that any policy settings can be used in the GPOs. The only way to determine what is going on is by examining the logs.
In summary, the code currently does the following:
- Determines the DN of the policy target (e.g.
"cn=f20-laptop,OU=West,OU=Sales,DC=foo,DC=com") 2. Calculates the list of SOM DNs (Site, Domain and OUs) associated with the policy target DN 3. Retrieves various LDAP attributes from each SOM DN in order to populate a list of GPLinks (i.e. GPO DN plus GPO Options) 4. Creates a prioritized list of GPO DNs based on SOM Priority, Link Order, and GPO Options 5. Retrieves various LDAP attributes from each GPO DN in order to populate a list of candidate GPOs 6. Converts the candidate list to a filtered list, by removing any GPO that has a DACL which denies the policy target permission to apply the GPO 7. It is this filtered list that will be sent to the Short-Lived GPO Child process, which will make the necessary blocking SMB calls
Regards, Yassir.
We changed DEBUG messages recently and your patch cannot be compiled on current master. Probably, it happened when you rebased patches.
I am attaching which fixes these problems. And some (probably not all) code style issues: trailing whitespaces, tabs instead of spaces ...
Feel free to squash this patch to yours.
LS
sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
Hmmm... I did notice the recent DEBUG message changes and I went through and fixed each one so it compiled. I must have made a mistake somewhere when merging from my local gpo branch to my local master branch. Thanks for the patch!
Regards, Yassir. _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
I have attached an updated patch to address Lukas's initial comments.
Regards, Yassir.
----- Original Message -----
----- Original Message -----
----- Original Message -----
On (12/03/14 18:20), Yassir Elley wrote:
Hi,
The attached patch implements the LDAP component of the GPO-based access control project. For more details on the project, see https://fedorahosted.org/sssd/wiki/DesignDocs/ActiveDirectoryGPOIntegration. The project is not yet complete, since the SMB component still needs to be implemented. However, this seems like a useful milestone to allow others to review the code. This is my first patch submission to the list, so any feedback is appreciated.
In order to exercise the code in your AD environment, you will need to link a GPO to the site, domain, or OUs, associated with the policy target (i.e. domain member computer). Since we are not yet retrieving any policy files (over SMB), there is no access control taking place yet, meaning that any policy settings can be used in the GPOs. The only way to determine what is going on is by examining the logs.
In summary, the code currently does the following:
- Determines the DN of the policy target (e.g.
"cn=f20-laptop,OU=West,OU=Sales,DC=foo,DC=com") 2. Calculates the list of SOM DNs (Site, Domain and OUs) associated with the policy target DN 3. Retrieves various LDAP attributes from each SOM DN in order to populate a list of GPLinks (i.e. GPO DN plus GPO Options) 4. Creates a prioritized list of GPO DNs based on SOM Priority, Link Order, and GPO Options 5. Retrieves various LDAP attributes from each GPO DN in order to populate a list of candidate GPOs 6. Converts the candidate list to a filtered list, by removing any GPO that has a DACL which denies the policy target permission to apply the GPO 7. It is this filtered list that will be sent to the Short-Lived GPO Child process, which will make the necessary blocking SMB calls
Regards, Yassir.
We changed DEBUG messages recently and your patch cannot be compiled on current master. Probably, it happened when you rebased patches.
I am attaching which fixes these problems. And some (probably not all) code style issues: trailing whitespaces, tabs instead of spaces ...
Feel free to squash this patch to yours.
LS
sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
Hmmm... I did notice the recent DEBUG message changes and I went through and fixed each one so it compiled. I must have made a mistake somewhere when merging from my local gpo branch to my local master branch. Thanks for the patch!
Regards, Yassir. _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
I have attached an updated patch to address Lukas's initial comments.
Regards, Yassir. _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
I have re-organized the code (and added more comments) and attached an updated patch for code review. I hope this will make it easier to review.
Regards, Yassir.
On Thu, Apr 03, 2014 at 12:30:17PM -0400, Yassir Elley wrote:
----- Original Message -----
----- Original Message -----
----- Original Message -----
On (12/03/14 18:20), Yassir Elley wrote:
Hi,
The attached patch implements the LDAP component of the GPO-based access control project. For more details on the project, see https://fedorahosted.org/sssd/wiki/DesignDocs/ActiveDirectoryGPOIntegration. The project is not yet complete, since the SMB component still needs to be implemented. However, this seems like a useful milestone to allow others to review the code. This is my first patch submission to the list, so any feedback is appreciated.
In order to exercise the code in your AD environment, you will need to link a GPO to the site, domain, or OUs, associated with the policy target (i.e. domain member computer). Since we are not yet retrieving any policy files (over SMB), there is no access control taking place yet, meaning that any policy settings can be used in the GPOs. The only way to determine what is going on is by examining the logs.
In summary, the code currently does the following:
- Determines the DN of the policy target (e.g.
"cn=f20-laptop,OU=West,OU=Sales,DC=foo,DC=com") 2. Calculates the list of SOM DNs (Site, Domain and OUs) associated with the policy target DN 3. Retrieves various LDAP attributes from each SOM DN in order to populate a list of GPLinks (i.e. GPO DN plus GPO Options) 4. Creates a prioritized list of GPO DNs based on SOM Priority, Link Order, and GPO Options 5. Retrieves various LDAP attributes from each GPO DN in order to populate a list of candidate GPOs 6. Converts the candidate list to a filtered list, by removing any GPO that has a DACL which denies the policy target permission to apply the GPO 7. It is this filtered list that will be sent to the Short-Lived GPO Child process, which will make the necessary blocking SMB calls
Regards, Yassir.
We changed DEBUG messages recently and your patch cannot be compiled on current master. Probably, it happened when you rebased patches.
I am attaching which fixes these problems. And some (probably not all) code style issues: trailing whitespaces, tabs instead of spaces ...
Feel free to squash this patch to yours.
LS
sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
Hmmm... I did notice the recent DEBUG message changes and I went through and fixed each one so it compiled. I must have made a mistake somewhere when merging from my local gpo branch to my local master branch. Thanks for the patch!
Regards, Yassir. _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
I have attached an updated patch to address Lukas's initial comments.
Regards, Yassir. _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
I have re-organized the code (and added more comments) and attached an updated patch for code review. I hope this will make it easier to review.
Regards, Yassir.
Hi,
I've read the patches and did some basic tests. You can find my review comments in-line below. I had to add some NULL-checks to make my tests work, see details below as well.
In general I think you should add an option to disable the GPO checks to be able to resolve regressions in existing setups.
I don't know if you already have some unit tests in your tree, but there are several functions in your patch where unit tests would be nice.
If to comments are addressed we should push the patch to master to get more testing, coverity checks etc.
bye, Sumit
From a508e36e67aa881665d0387ff418162e5f1825cd Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Mon, 20 Jan 2014 11:17:06 -0500 Subject: [PATCH] Implemented LDAP component of GPO-based access control
Makefile.am | 3 + src/providers/ad/ad_access.c | 125 ++- src/providers/ad/ad_domain_info.c | 34 +- src/providers/ad/ad_domain_info.h | 1 + src/providers/ad/ad_gpo.c | 2199 +++++++++++++++++++++++++++++++++++++ src/providers/ad/ad_gpo.h | 55 + src/providers/ad/ad_id.c | 3 +- src/providers/ad/ad_subdomains.c | 3 +- src/providers/ldap/sdap_async.c | 176 +++ src/providers/ldap/sdap_async.h | 14 + src/util/sss_ldap.h | 9 + 11 files changed, 2571 insertions(+), 51 deletions(-) create mode 100644 src/providers/ad/ad_gpo.c create mode 100644 src/providers/ad/ad_gpo.h
diff --git a/Makefile.am b/Makefile.am index f319624..2935d15 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1608,6 +1608,7 @@ ad_access_filter_tests_LDADD = \ libsss_ldap_common.la \ libsss_idmap.la \ libsss_krb5_common.la \
- libsss_ad.la \ libsss_test_common.la
I see
CCLD ad_access_filter_tests
*** Warning: Linking the executable ad_access_filter_tests against the loadable module *** libsss_ad.so is not portable!
when running make check, I think you have to replace libsss_ad.la by the list of source files here
...
- state->cindex++;
- if (state->clist[state->cindex] == NULL) {
DEBUG(SSSDBG_OP_FAILURE,
"Error retrieving access check result: %s\n",
sss_strerror(ret));
tevent_req_error(req, ret);
return;
/* If possible, retry with LDAP */
state->cindex++;
if (state->clist[state->cindex] == NULL) {
DEBUG(SSSDBG_OP_FAILURE,
"Error retrieving access check result: %s\n",
sss_strerror(ret));
tevent_req_error(req, ret);
return;
}
ret = ad_sdap_access_step(req, state->clist[state->cindex]);
if (ret != EOK) {
tevent_req_error(req, ret);
return;
}
/* Another check in progress */
You have to call return here because ad_sdap_access_step() will create a new request which will return to ad_sdap_access_done().
}
- ret = ad_access_step(req, state->clist[state->cindex]);
- if (ret != EOK) {
tevent_req_error(req, ret);
- subreq = ad_gpo_access_send(state,
state->be_ctx->ev,
state->domain,
state->ctx,
state->pd->user);
- if (!subreq) {
}tevent_req_error(req, ENOMEM); return;
- /* Another check in progress */
- tevent_req_set_callback(subreq, ad_gpo_access_done, req);
}
...
static void -ad_access_check_done(struct tevent_req *req) +ad_access_done(struct tevent_req *req) { errno_t ret; struct be_req *breq = tevent_req_callback_data(req, struct be_req); struct pam_data *pd = talloc_get_type(be_req_get_data(breq), struct pam_data);
Please do not remove this empty line, it separates declarations from the code. There is no formal requirement in the coding-style but I think it helps reading the code nonetheless.
ret = ad_access_recv(req); talloc_zfree(req); switch (ret) {
diff --git a/src/providers/ad/ad_domain_info.c b/src/providers/ad/ad_domain_info.c index 9594484..d18a9c1 100644 --- a/src/providers/ad/ad_domain_info.c +++ b/src/providers/ad/ad_domain_info.c @@ -44,7 +44,9 @@ static errno_t netlogon_get_domain_info(TALLOC_CTX *mem_ctx, struct sysdb_attrs *reply,
char **_flat_name, char **_forest)
char **_flat_name,
char **_site,
char **_forest)
{ errno_t ret; struct ldb_message_element *el; @@ -53,6 +55,7 @@ netlogon_get_domain_info(TALLOC_CTX *mem_ctx, enum ndr_err_code ndr_err; struct netlogon_samlogon_response response; const char *flat_name;
const char *site; const char *forest;
ret = sysdb_attrs_get_el(reply, AD_AT_NETLOGON, &el);
@@ -129,6 +132,25 @@ netlogon_get_domain_info(TALLOC_CTX *mem_ctx, goto done; }
- /* get site name */
- if (response.data.nt5_ex.client_site != NULL
&& response.data.nt5_ex.client_site[0] != '\0') {
site = response.data.nt5_ex.client_site;
- } else if (response.data.nt5_ex.next_closest_site != NULL
&& response.data.nt5_ex.next_closest_site[0] != '\0') {
site = response.data.nt5_ex.next_closest_site;
I think you don't want to use the next_closest_site here. In general for a domain member always belongs to a site, if it cannot be determined it is an error. The next_closest_site might have completely different policy settings. We use next_closest_site in the DNS lookup code because e.g. for request to a trusted forest there will only be a next_closest_site.
- } else {
ret = ENOENT;
goto done;
- }
- *_site = talloc_strdup(mem_ctx, site);
- if (*_site == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
ret = ENOMEM;
goto done;
- }
- ret = EOK;
done: talloc_free(ndr_pull);
...
diff --git a/src/providers/ad/ad_gpo.c b/src/providers/ad/ad_gpo.c new file mode 100644 index 0000000..5a44b24 --- /dev/null +++ b/src/providers/ad/ad_gpo.c @@ -0,0 +1,2199 @@ +/*
- SSSD
- ad_gpo.c
- Authors:
- Yassir Elley yelley@redhat.com
- Copyright (C) 2013 Red Hat
- This program is free software; you can redistribute it and/or modify
- 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
- */
+#include <security/pam_modules.h> +#include "util/util.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/ad/ad_gpo.h" +#include "providers/ldap/sdap_access.h" +#include "providers/ldap/sdap_async.h" +#include "providers/ldap/sdap.h" +#include <ndr.h> +#include <gen_ndr/security.h>
+#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" +#define SID_MAX_LEN 1024
+/* == common data structures and declarations ============================= */
+struct gp_som {
- char *som_dn;
- struct gp_gplink **gplink_list;
- int num_gplinks;
+};
+struct gp_gplink {
- char *gpo_dn;
- bool enforced;
+};
+struct gp_gpo {
- struct security_descriptor *gpo_sd;
- char *gpo_dn;
- char *gpo_guid;
- char *gpo_display_name;
- char *gpo_file_sys_path;
- uint32_t gpo_container_version;
- char **gpo_cse_guids;
- int num_gpo_cse_guids;
- int gpo_func_version;
- int gpo_flags;
+};
+enum ace_eval_status {
- AD_GPO_ACE_DENIED,
- AD_GPO_ACE_ALLOWED,
- AD_GPO_ACE_NEUTRAL
+};
+struct tevent_req *ad_gpo_process_som_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct sdap_id_conn_ctx *conn,
struct sdap_id_op *sdap_op,
struct sdap_options *opts,
int timeout,
char *target_dn,
char *domain_name);
+int ad_gpo_process_som_recv(struct tevent_req *req,
TALLOC_CTX *mem_ctx,
struct gp_som ***som_list);
+struct tevent_req *ad_gpo_process_gpo_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct sdap_id_op *sdap_op,
struct sdap_options *opts,
int timeout,
struct gp_som **som_list);
+int ad_gpo_process_gpo_recv(struct tevent_req *req,
TALLOC_CTX *mem_ctx,
struct gp_gpo ***candidate_gpos,
int *num_candidate_gpos);
+/* == ad_gpo_access_send/recv helpers =======================================*/
+/*
- 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
- gpo access checks are called, we can safely assume that the user/computer
- has been authenticated. As such, this function always adds the
- AD_AUTHENTICATED_USERS_SID to the group_sids.
- */
+static errno_t +ad_gpo_get_sids(TALLOC_CTX *mem_ctx,
char *user,
struct sss_domain_info *domain,
const char **_user_sid,
const char ***_group_sids,
int *_group_size)
+{
- TALLOC_CTX *tmp_ctx = NULL;
- struct ldb_result *res;
- int ret = 0;
- int i = 0;
- int num_group_sids = 0;
- const char *user_sid = NULL;
- const char *group_sid = NULL;
- const char **group_sids = NULL;
- tmp_ctx = talloc_new(NULL);
- if (tmp_ctx == NULL) {
ret = ENOMEM;
goto done;
- }
- /* first result from sysdb_initgroups is user_sid; rest are group_sids */
- ret = sysdb_initgroups(tmp_ctx, domain, user, &res);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_initgroups failed: [%d](%s)\n",
ret, strerror(ret));
return ret;
- }
- user_sid = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_SID_STR, NULL);
- num_group_sids = (res->count) - 1;
- /* include space for AD_AUTHENTICATED_USERS_SID and NULL */
- group_sids = talloc_array(tmp_ctx, const char *, num_group_sids + 1 + 1);
- if (group_sids == NULL) {
ret = ENOMEM;
goto done;
- }
- for (i = 0; i < num_group_sids; i++) {
group_sid = ldb_msg_find_attr_as_string(res->msgs[i+1],
SYSDB_SID_STR, NULL);
if (group_sid == NULL) {
continue;
}
group_sids[i] = talloc_strdup(group_sids, group_sid);
if (group_sids[i] == NULL) {
ret = ENOMEM;
goto done;
}
- }
- group_sids[i++] = AD_AUTHENTICATED_USERS_SID;
I think this is risky. In general nobody should get the idea to free the list entries manually. But if someone does he would fail here. I think it would be better if list members always have the same storage class so please use talloc_strdup() here as well.
- group_sids[i] = NULL;
- *_group_size = num_group_sids + 1;
- *_group_sids = talloc_steal(mem_ctx, group_sids);
- *_user_sid = talloc_strdup(mem_ctx, user_sid);
- return EOK;
- done:
- talloc_free(tmp_ctx);
- return ret;
+}
+bool string_to_sid(struct dom_sid *sidout, const char *sidstr); +int dom_sid_string_buf(const struct dom_sid *sid, char *buf, int buflen); +bool dom_sid_equal(const struct dom_sid *sid1, const struct dom_sid *sid2);
...
+static void +ad_gpo_target_dn_retrieval_done(struct tevent_req *subreq) +{
- struct tevent_req *req;
- struct ad_gpo_access_state *state;
- int ret;
- int dp_error;
- size_t reply_count;
- struct sysdb_attrs **reply;
- const char *target_dn = NULL;
- req = tevent_req_callback_data(subreq, struct tevent_req);
- state = tevent_req_data(req, struct ad_gpo_access_state);
- ret = sdap_get_generic_recv(subreq, state,
&reply_count, &reply);
- talloc_zfree(subreq);
- if (ret != EOK) {
ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
/* TBD: handle (dp_error == DP_ERR_OFFLINE) case */
DEBUG(SSSDBG_OP_FAILURE,
"Unable to get policy target's DN: [%d](%s)\n",
ret, strerror(ret));
ret = ENOENT;
goto done;
- }
- /* make sure there is only one non-NULL reply returned */
- if (reply_count < 1) {
DEBUG(SSSDBG_OP_FAILURE, "No DN retrieved for policy target.\n");
ret = ENOENT;
goto done;
- } else if (reply_count > 1) {
DEBUG(SSSDBG_OP_FAILURE, "Multiple replies for policy target\n");
ret = ERR_INTERNAL;
goto done;
- } else if (reply == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "reply_count is 1, but reply is NULL\n");
ret = ERR_INTERNAL;
goto done;
- }
- /* reply[0] holds requested attributes of single reply */
- ret = sysdb_attrs_get_string(reply[0], AD_AT_DN, &target_dn);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_string failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- state->target_dn = talloc_strdup(state, target_dn);
- if (state->target_dn == NULL) {
ret = ENOMEM;
goto done;
- }
- uint32_t uac;
Please declare variables at the beginning of the function.
- ret = sysdb_attrs_get_uint32_t(reply[0], AD_AT_UAC, &uac);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_uint32_t failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- /* we only support computer policy targets, not users */
- if (!(uac & UAC_WORKSTATION_TRUST_ACCOUNT)) {
ret = EINVAL;
goto done;
- }
- state->target_dn = talloc_strdup(state, target_dn);
- if (state->target_dn == NULL) {
ret = ENOMEM;
goto done;
- }
- subreq = ad_gpo_process_som_send(state,
state->ev,
state->conn,
state->sdap_op,
state->opts,
state->timeout,
state->target_dn,
state->domain->name);
- if (subreq == NULL) {
ret = ENOMEM;
goto done;
- }
- tevent_req_set_callback(subreq, ad_gpo_process_som_done, req);
- done:
- if (ret != EOK) {
tevent_req_error(req, ret);
- }
+}
+static void +ad_gpo_process_som_done(struct tevent_req *subreq) +{
- struct tevent_req *req;
- struct ad_gpo_access_state *state;
- int ret;
- struct gp_som **som_list;
- req = tevent_req_callback_data(subreq, struct tevent_req);
- state = tevent_req_data(req, struct ad_gpo_access_state);
- ret = ad_gpo_process_som_recv(subreq, state, &som_list);
- talloc_zfree(subreq);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Unable to get som list: [%d](%s)\n",
ret, strerror(ret));
ret = ENOENT;
goto done;
- }
- subreq = ad_gpo_process_gpo_send(state,
state->ev,
state->sdap_op,
state->opts,
state->timeout,
som_list);
- if (subreq == NULL) {
ret = ENOMEM;
goto done;
- }
- tevent_req_set_callback(subreq, ad_gpo_process_gpo_done, req);
Currently it is obvious that ret==EOK if we reached this point. But if some code is added in future it might be not so obvious anymore and I would suggest to explicitly set ret=EOK before entering the done: block or call return here. I would expect that a compiler will optimize out the line if it is clear that ret can only be EOK when reaching this point.
- done:
- if (ret != EOK) {
tevent_req_error(req, ret);
- }
+}
+/*
- 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.
- */
+static void +ad_gpo_process_gpo_done(struct tevent_req *subreq) +{
- struct tevent_req *req;
- struct ad_gpo_access_state *state;
- int ret;
- int dp_error;
- struct gp_gpo **candidate_gpos = NULL;
- int num_candidate_gpos = 0;
- int i = 0;
- req = tevent_req_callback_data(subreq, struct tevent_req);
- state = tevent_req_data(req, struct ad_gpo_access_state);
- ret = ad_gpo_process_gpo_recv(subreq, state, &candidate_gpos,
&num_candidate_gpos);
- talloc_zfree(subreq);
- ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
- if (ret != EOK) {
/* TBD: handle (dp_error == DP_ERR_OFFLINE) case */
DEBUG(SSSDBG_OP_FAILURE,
"Unable to get GPO list: [%d](%s)\n",
ret, strerror(ret));
ret = ENOENT;
goto done;
- }
- ret = ad_gpo_filter_gpos_by_dacl(state, state->user, state->domain,
candidate_gpos, num_candidate_gpos,
&state->dacl_filtered_gpos,
&state->num_dacl_filtered_gpos);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Unable to filter GPO list: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- if (state->dacl_filtered_gpos[0] == NULL) {
DEBUG(SSSDBG_TRACE_FUNC, "dacl_filtered_gpos is empty\n");
- }
- for (i = 0; i < state->num_dacl_filtered_gpos; i++) {
DEBUG(SSSDBG_TRACE_FUNC, "dacl_filtered_gpos[%d]->gpo_guid is %s\n", i,
state->dacl_filtered_gpos[i]->gpo_guid);
- }
- /* TBD: initiate SMB retrieval */
- DEBUG(SSSDBG_TRACE_FUNC, "time for SMB retrieval\n");
see above
- done:
- if (ret == EOK) {
tevent_req_done(req);
- } else if (ret != EAGAIN) {
tevent_req_error(req, ret);
- }
+}
+errno_t +ad_gpo_access_recv(struct tevent_req *req) +{
- TEVENT_REQ_RETURN_ON_ERROR(req);
- return EOK;
+}
+/* == ad_gpo_process_som_send/recv helpers ================================= */
+/*
- This function returns the parent of an LDAP DN
- */
+static char *ad_gpo_parent_dn(const char *dn) +{
- char *p;
- if (dn == NULL) {
return NULL;
- }
- p = strchr(dn, ',');
- if (p == NULL) {
return NULL;
- }
I would suggest the use calls from OpenLDAP or ldb (there is ldb_dn_get_parent() maybe to already does the right thing) to properly split the DN in components and created the parent DN. Your way would fail e.g. if the RDN contains an escaped comma ",".
- return p+1;
+}
+/*
- 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 +ad_gpo_populate_som_list(TALLOC_CTX *mem_ctx,
char *target_dn,
int *_num_soms,
struct gp_som ***_som_list)
+{
- TALLOC_CTX *tmp_ctx = NULL;
- int ret;
- int rdn_count = 0;
- int som_idx = 0;
- struct gp_som **som_list;
- char *parent_dn = NULL;
- char *tmp_dn = NULL;
- tmp_ctx = talloc_new(NULL);
- if (tmp_ctx == NULL) {
ret = ENOMEM;
goto done;
- }
- tmp_dn = target_dn;
- while ((parent_dn = ad_gpo_parent_dn(tmp_dn))) {
rdn_count++;
tmp_dn = parent_dn;
- }
There is ldb_dn_get_comp_num() which should return the number of components in a save way.
- if (rdn_count == 0) {
*_som_list = NULL;
ret = EOK;
goto done;
- }
- /* assume the worst-case, in which every parent is a SOM */
- /* include space for Site SOM and NULL: rdn_count + 1 + 1 */
- som_list = talloc_array(tmp_ctx, struct gp_som *, rdn_count + 1 + 1);
- if (som_list == NULL) {
ret = ENOMEM;
goto done;
- }
- /* first, populate the OU and Domain SOMs */
- tmp_dn = target_dn;
- while ((parent_dn = ad_gpo_parent_dn(tmp_dn))) {
if ((strncasecmp(parent_dn, "OU=", strlen("OU=")) == 0) ||
(strncasecmp(parent_dn, "DC=", strlen("DC=")) == 0)) {
som_list[som_idx] = talloc_zero(som_list, struct gp_som);
if (som_list[som_idx] == NULL) {
ret = ENOMEM;
goto done;
}
som_list[som_idx]->som_dn = talloc_strdup(som_list[som_idx],
parent_dn);
if (som_list[som_idx]->som_dn == NULL) {
ret = ENOMEM;
goto done;
}
som_idx++;
}
if (strncasecmp(parent_dn, "DC=", strlen("DC=")) == 0) {
break;
}
tmp_dn = parent_dn;
- }
- som_list[som_idx] = NULL;
- *_num_soms = som_idx;
- *_som_list = talloc_steal(mem_ctx, som_list);
- ret = EOK;
- done:
- talloc_free(tmp_ctx);
- 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 +ad_gpo_populate_gplink_list(TALLOC_CTX *mem_ctx,
char *som_dn,
char *raw_gplink_value,
struct gp_gplink ***_gplink_list,
bool allow_enforced_only)
+{
- TALLOC_CTX *tmp_ctx = NULL;
- 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_number;
- int gplink_count = 0;
- int num_enabled = 0;
- if (raw_gplink_value == NULL ||
*raw_gplink_value == '\0' ||
_gplink_list == NULL) {
return EINVAL;
- }
- DEBUG(SSSDBG_TRACE_ALL, "som_dn: %s\n", som_dn);
- tmp_ctx = talloc_new(NULL);
- if (tmp_ctx == NULL) {
ret = ENOMEM;
goto done;
- }
- ptr = raw_gplink_value;
- while ((ptr = strchr(ptr, delim))) {
if (ptr == NULL) break;
ptr++;
gplink_count++;
- }
- if (gplink_count == 0) {
ret = EINVAL;
goto done;
- }
- gplink_list = talloc_array(tmp_ctx, struct gp_gplink *, gplink_count + 1);
- if (gplink_list == NULL) {
ret = ENOMEM;
goto done;
- }
- num_enabled = 0;
- ptr = raw_gplink_value;
- for (i = 0; i < gplink_count; i++) {
first = ptr + 1;
last = strchr(first, delim);
if (last == NULL) {
break;
}
*last = '\0';
last++;
dn = first;
if ( strncasecmp(dn, "LDAP://", 7)== 0 ) {
dn = dn + 7;
}
are you sure this can only be LDAP:// ? I wonder if it can be LDAPS:// is the original request is using LDAPS? But since AD does not activate LDAPS by default it might be safe.
gplink_options = strchr(first, ';');
if (gplink_options == NULL) {
break;
}
*gplink_options = '\0';
gplink_options++;
gplink_number = atoi(gplink_options);
In general we prefer strto* for better error checking. I assume that gplink_options is expected to be a 32bit value. If this is true you might want to use strtouint32(). In this case it might be better to declare gplink_options as uint32t instead of int?
DEBUG(SSSDBG_TRACE_ALL,
"gplink_list[%d]: [%s; %d]\n", num_enabled, dn, gplink_number);
if ((gplink_number == 1) || (gplink_number ==3)) {
/* ignore flag is set */
DEBUG(SSSDBG_TRACE_ALL, "ignored gpo skipped\n");
ptr = last;
continue;
}
if (allow_enforced_only && (gplink_number == 0)) {
/* unenforced flag is set; only enforced gpos allowed */
DEBUG(SSSDBG_TRACE_ALL, "unenforced gpo skipped\n");
ptr = last;
continue;
}
gplink_list[num_enabled] = talloc_zero(gplink_list, struct gp_gplink);
if (gplink_list[num_enabled] == NULL) {
ret = ENOMEM;
goto done;
}
gplink_list[num_enabled]->gpo_dn =
talloc_strdup(gplink_list[num_enabled], dn);
if (gplink_list[num_enabled]->gpo_dn == NULL) {
ret = ENOMEM;
goto done;
}
if (gplink_number == 0) {
gplink_list[num_enabled]->enforced = 0;
num_enabled++;
} else if (gplink_number == 2) {
gplink_list[num_enabled]->enforced = 1;
num_enabled++;
} else {
ret = EINVAL;
goto done;
}
ptr = last;
- }
- gplink_list[num_enabled] = NULL;
- *_gplink_list = talloc_steal(mem_ctx, gplink_list);
- ret = EOK;
- done:
- talloc_free(tmp_ctx);
- return ret;
+}
...
+static void +ad_gpo_get_som_attrs_done(struct tevent_req *subreq) +{
- struct tevent_req *req;
- struct ad_gpo_process_som_state *state;
- int ret;
- int dp_error;
- size_t num_results;
- struct sysdb_attrs **results;
- struct ldb_message_element *el = NULL;
- uint8_t *raw_gplink_value;
- uint8_t *raw_gpoptions_value;
- int allow_enforced_only = 0;
- struct gp_som *gp_som;
- req = tevent_req_callback_data(subreq, struct tevent_req);
- state = tevent_req_data(req, struct ad_gpo_process_som_state);
- ret = sdap_get_generic_recv(subreq, state,
&num_results, &results);
- talloc_zfree(subreq);
- if (ret != EOK) {
ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
/* TBD: handle (dp_error == DP_ERR_OFFLINE) case */
DEBUG(SSSDBG_OP_FAILURE,
"Unable to get SOM attributes: [%d](%s)\n",
ret, strerror(ret));
ret = ENOENT;
goto done;
- }
- if ((num_results < 1) || (results == NULL)) {
DEBUG(SSSDBG_OP_FAILURE, "no attrs found for SOM; try next SOM.\n");
ret = ad_gpo_get_som_attrs_step(req);
goto done;
- } else if (num_results > 1) {
DEBUG(SSSDBG_OP_FAILURE, "Received multiple replies\n");
ret = ERR_INTERNAL;
goto done;
- }
- /* Get the gplink value, if available */
- ret = sysdb_attrs_get_el(results[0], AD_AT_GPLINK, &el);
- if (ret != EOK && ret != ENOENT) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_el() failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- if ((ret == ENOENT) || (el->num_values == 0)) {
DEBUG(SSSDBG_OP_FAILURE, "no attrs found for SOM; try next SOM\n");
ret = ad_gpo_get_som_attrs_step(req);
goto done;
- }
- raw_gplink_value = el[0].values[0].data;
- ret = sysdb_attrs_get_el(results[0], AD_AT_GPOPTIONS, &el);
- if (ret != EOK && ret != ENOENT) {
DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_el() failed\n");
goto done;
- }
- if ((ret == ENOENT) || (el->num_values == 0)) {
DEBUG(SSSDBG_TRACE_ALL,
"gpoptions attr not found or has no value; defaults to 0\n");
allow_enforced_only = 0;
- } else {
raw_gpoptions_value = el[0].values[0].data;
allow_enforced_only = atoi((char *)raw_gpoptions_value);
see above about atoi();
- }
- gp_som = state->som_list[state->som_index];
- ret = ad_gpo_populate_gplink_list(gp_som,
gp_som->som_dn,
(char *)raw_gplink_value,
&gp_som->gplink_list,
state->allow_enforced_only);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"ad_gpo_populate_gplink_list() failed\n");
goto done;
- }
- if (allow_enforced_only) {
state->allow_enforced_only = 1;
- }
- ret = ad_gpo_get_som_attrs_step(req);
- done:
- if (ret == EOK) {
tevent_req_done(req);
- } else if (ret != EAGAIN) {
tevent_req_error(req, ret);
- }
+}
+int +ad_gpo_process_som_recv(struct tevent_req *req,
TALLOC_CTX *mem_ctx,
struct gp_som ***som_list)
+{
- struct ad_gpo_process_som_state *state =
tevent_req_data(req, struct ad_gpo_process_som_state);
- TEVENT_REQ_RETURN_ON_ERROR(req);
- *som_list = talloc_steal(mem_ctx, state->som_list);
- 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 +ad_gpo_populate_candidate_gpos(TALLOC_CTX *mem_ctx,
struct gp_som **som_list,
struct gp_gpo ***_candidate_gpos,
int *_num_candidate_gpos)
+{
- TALLOC_CTX *tmp_ctx = NULL;
- struct gp_som *gp_som = NULL;
- struct gp_gplink *gp_gplink = NULL;
- struct gp_gpo **candidate_gpos = NULL;
- int num_candidate_gpos = 0;
- char **enforced_gpo_dns = NULL;
- 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;
- tmp_ctx = talloc_new(NULL);
- if (tmp_ctx == NULL) {
ret = ENOMEM;
goto done;
- }
- while (som_list[i]) {
gp_som = som_list[i];
j = 0;
while (gp_som->gplink_list[j]) {
I had to change this to
while (gp_som && gp_som->gplink_list && gp_som->gplink_list[j]) {
to make my basic tests work. Maybe it would be good to add such a NULL check to the som_list while loop as well?
gp_gplink = gp_som->gplink_list[j];
if (gp_gplink == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "unexpected null gp_gplink\n");
ret = EINVAL;
goto done;
}
if (gp_gplink->enforced) {
num_enforced++;
} else {
num_unenforced++;
}
j++;
}
i++;
- }
- num_candidate_gpos = num_enforced + num_unenforced;
- if (num_candidate_gpos == 0) {
*_candidate_gpos = NULL;
*_num_candidate_gpos = 0;
ret = EOK;
goto done;
- }
- enforced_gpo_dns = talloc_array(tmp_ctx, char *, num_enforced + 1);
- if (enforced_gpo_dns == NULL) {
ret = ENOMEM;
goto done;
- }
- unenforced_gpo_dns = talloc_array(tmp_ctx, char *, num_unenforced + 1);
- if (unenforced_gpo_dns == NULL) {
ret = ENOMEM;
goto done;
- }
- i = 0;
- while (som_list[i]) {
gp_som = som_list[i];
j = 0;
while (gp_som->gplink_list[j]) {
see above
gp_gplink = gp_som->gplink_list[j];
if (gp_gplink == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "unexpected null gp_gplink\n");
ret = EINVAL;
goto done;
}
if (gp_gplink->enforced) {
enforced_gpo_dns[enforced_idx] =
talloc_strdup(enforced_gpo_dns, gp_gplink->gpo_dn);
if (enforced_gpo_dns[enforced_idx] == NULL) {
ret = ENOMEM;
goto done;
}
enforced_idx++;
} else {
unenforced_gpo_dns[unenforced_idx] =
talloc_strdup(unenforced_gpo_dns, gp_gplink->gpo_dn);
if (unenforced_gpo_dns[unenforced_idx] == NULL) {
ret = ENOMEM;
goto done;
}
unenforced_idx++;
}
j++;
}
i++;
- }
- enforced_gpo_dns[num_enforced] = NULL;
- unenforced_gpo_dns[num_unenforced] = NULL;
- candidate_gpos = talloc_array(tmp_ctx,
struct gp_gpo *,
num_candidate_gpos + 1);
- if (candidate_gpos == NULL) {
ret = ENOMEM;
goto done;
- }
- gpo_dn_idx = 0;
- for (i = num_unenforced - 1; i >= 0; i--) {
candidate_gpos[gpo_dn_idx] = talloc_zero(candidate_gpos, struct gp_gpo);
if (candidate_gpos[gpo_dn_idx] == NULL) {
ret = ENOMEM;
goto done;
}
candidate_gpos[gpo_dn_idx]->gpo_dn =
talloc_strdup(candidate_gpos[gpo_dn_idx], unenforced_gpo_dns[i]);
Is there a reason for adding the unenforced_gpo_dns in reverse order compared to enforced_gpo_dns?
if (candidate_gpos[gpo_dn_idx]->gpo_dn == NULL) {
ret = ENOMEM;
goto done;
}
DEBUG(SSSDBG_TRACE_FUNC,
"candidate_gpos[%d]->gpo_dn: %s\n",
gpo_dn_idx, candidate_gpos[gpo_dn_idx]->gpo_dn);
gpo_dn_idx++;
- }
- for (i = 0; i < num_enforced; i++) {
candidate_gpos[gpo_dn_idx] = talloc_zero(candidate_gpos, struct gp_gpo);
if (candidate_gpos[gpo_dn_idx] == NULL) {
ret = ENOMEM;
goto done;
}
candidate_gpos[gpo_dn_idx]->gpo_dn =
talloc_strdup(candidate_gpos[gpo_dn_idx], enforced_gpo_dns[i]);
if (candidate_gpos[gpo_dn_idx]->gpo_dn == NULL) {
ret = ENOMEM;
goto done;
}
DEBUG(SSSDBG_TRACE_FUNC,
"candidate_gpos[%d]->gpo_dn: %s\n",
gpo_dn_idx, candidate_gpos[gpo_dn_idx]->gpo_dn);
gpo_dn_idx++;
- }
- candidate_gpos[gpo_dn_idx] = NULL;
- *_candidate_gpos = talloc_steal(mem_ctx, candidate_gpos);
- *_num_candidate_gpos = num_candidate_gpos;
- ret = EOK;
- done:
- talloc_free(tmp_ctx);
- return ret;
+}
...
+/*
- 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 * +ad_gpo_process_gpo_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct sdap_id_op *sdap_op,
struct sdap_options *opts,
int timeout,
struct gp_som **som_list)
+{
- struct tevent_req *req;
- struct ad_gpo_process_gpo_state *state;
- errno_t ret;
- req = tevent_req_create(mem_ctx, &state, struct ad_gpo_process_gpo_state);
- if (req == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
return NULL;
- }
- state->ev = ev;
- state->sdap_op = sdap_op;
- state->opts = opts;
- state->timeout = timeout;
- state->gpo_index = -1;
- state->candidate_gpos = NULL;
- state->num_candidate_gpos = 0;
- ret = ad_gpo_populate_candidate_gpos(state,
som_list,
&state->candidate_gpos,
&state->num_candidate_gpos);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Unable to retrieve GPO List: [%d](%s)\n",
ret, strerror(ret));
ret = ENOENT;
goto immediately;
- }
- if (state->candidate_gpos == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "no gpos found\n");
goto immediately;
I think you should set ret here, otherwise it would be EOK which might mot be what you want.
- }
- ret = ad_gpo_get_gpo_attrs_step(req);
+immediately:
- if (ret == EOK) {
tevent_req_done(req);
tevent_req_post(req, ev);
- } else if (ret != EAGAIN) {
tevent_req_error(req, ret);
tevent_req_post(req, ev);
- }
- return req;
+}
+static errno_t +ad_gpo_get_gpo_attrs_step(struct tevent_req *req) +{
- const char *attrs[] = {AD_AT_NT_SEC_DESC, AD_AT_CN, AD_AT_DISPLAY_NAME,
AD_AT_FILE_SYS_PATH, AD_AT_VERSION_NUMBER,
AD_AT_MACHINE_EXT_NAMES, AD_AT_FUNC_VERSION,
AD_AT_FLAGS, NULL};
- struct tevent_req *subreq;
- struct ad_gpo_process_gpo_state *state;
- state = tevent_req_data(req, struct ad_gpo_process_gpo_state);
- state->gpo_index++;
- struct gp_gpo *gp_gpo = state->candidate_gpos[state->gpo_index];
- /* gp_gpo is NULL only after all GPOs have been processed */
- if (gp_gpo == NULL) return EOK;
- char *gpo_dn = gp_gpo->gpo_dn;
- subreq = sdap_sd_search_send(state, state->ev,
state->opts, sdap_id_op_handle(state->sdap_op),
gpo_dn, SECINFO_DACL, attrs, state->timeout);
- if (subreq == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send failed.\n");
return ENOMEM;
- }
- tevent_req_set_callback(subreq, ad_gpo_get_gpo_attrs_done, req);
- return EAGAIN;
+}
+static void +ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq) +{
- struct tevent_req *req;
- struct ad_gpo_process_gpo_state *state;
- int ret;
- int dp_error;
- size_t num_results;
- struct sysdb_attrs **results;
- struct ldb_message_element *el = NULL;
- const char *gpo_guid = NULL;
- char *smb_uri = NULL;
- req = tevent_req_callback_data(subreq, struct tevent_req);
- state = tevent_req_data(req, struct ad_gpo_process_gpo_state);
- ret = sdap_sd_search_recv(subreq, state, &num_results, &results);
- talloc_zfree(subreq);
- if (ret != EOK) {
ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
/* TBD: handle (dp_error == DP_ERR_OFFLINE) case */
DEBUG(SSSDBG_OP_FAILURE,
"Unable to get GPO attributes: [%d](%s)\n",
ret, strerror(ret));
ret = ENOENT;
goto done;
- }
- if ((num_results < 1) || (results == NULL)) {
DEBUG(SSSDBG_OP_FAILURE, "no attrs found for GPO; try next GPO.\n");
ret = ad_gpo_get_gpo_attrs_step(req);
goto done;
- }
- else if (num_results > 1) {
DEBUG(SSSDBG_OP_FAILURE, "Received multiple replies\n");
ret = ERR_INTERNAL;
goto done;
- }
- struct gp_gpo *gp_gpo = state->candidate_gpos[state->gpo_index];
- /* retrieve AD_AT_CN */
- ret = sysdb_attrs_get_string(results[0], AD_AT_CN, &gpo_guid);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_string failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- gp_gpo->gpo_guid = talloc_strdup(gp_gpo, gpo_guid);
- if (gp_gpo->gpo_guid == NULL) {
ret = ENOMEM;
goto done;
- }
- DEBUG(SSSDBG_TRACE_ALL, "populating attrs for gpo_guid: %s\n",
gp_gpo->gpo_guid);
- /* retrieve AD_AT_DISPLAY_NAME */
- const char *gpo_display_name = NULL;
Please move the declaration to the beginning of the function.
- ret = sysdb_attrs_get_string(results[0], AD_AT_DISPLAY_NAME,
&gpo_display_name);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_string failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- gp_gpo->gpo_display_name = talloc_strdup(gp_gpo, gpo_display_name);
- if (gp_gpo->gpo_display_name == NULL) {
ret = ENOMEM;
goto done;
- }
- DEBUG(SSSDBG_TRACE_ALL, "gpo_display_name: %s\n",
gp_gpo->gpo_display_name);
- /* retrieve AD_AT_FILE_SYS_PATH */
- const char *raw_file_sys_path = NULL;
see above
- ret = sysdb_attrs_get_string(results[0],
AD_AT_FILE_SYS_PATH,
&raw_file_sys_path);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_string failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- char *file_sys_path = talloc_strdup(gp_gpo, raw_file_sys_path);
see above
- ad_gpo_convert_to_smb_uri(state, file_sys_path, &smb_uri);
- gp_gpo->gpo_file_sys_path = talloc_asprintf(gp_gpo, "%s/Machine",
smb_uri);
- if (gp_gpo->gpo_file_sys_path == NULL) {
ret = ENOMEM;
goto done;
- }
- DEBUG(SSSDBG_TRACE_FUNC, "gpo_file_sys_path: %s\n",
gp_gpo->gpo_file_sys_path);
- /* retrieve AD_AT_VERSION_NUMBER */
- ret = sysdb_attrs_get_uint32_t(results[0], AD_AT_VERSION_NUMBER,
&gp_gpo->gpo_container_version);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_uint32_t failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- DEBUG(SSSDBG_TRACE_ALL, "gpo_container_version: %d\n",
gp_gpo->gpo_container_version);
- /* retrieve AD_AT_MACHINE_EXT_NAMES */
- ret = sysdb_attrs_get_el(results[0], AD_AT_MACHINE_EXT_NAMES, &el);
- if (ret != EOK && ret != ENOENT) {
DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_el() failed\n");
goto done;
- }
- if ((ret == ENOENT) || (el->num_values == 0)) {
DEBUG(SSSDBG_OP_FAILURE,
"machine_ext_names not found or has no value\n");
ret = ENOENT;
goto done;
- }
- uint8_t *raw_machine_ext_names = el[0].values[0].data;
see above
- ret = ad_gpo_parse_machine_ext_names(gp_gpo,
(char *)raw_machine_ext_names,
&gp_gpo->gpo_cse_guids,
&gp_gpo->num_gpo_cse_guids);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"ad_gpo_parse_machine_ext_names() failed\n");
goto done;
- }
- /* retrieve AD_AT_FUNC_VERSION */
- ret = sysdb_attrs_get_int32_t(results[0], AD_AT_FUNC_VERSION,
&gp_gpo->gpo_func_version);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_int32_t failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- DEBUG(SSSDBG_TRACE_ALL, "gpo_func_version: %d\n",
gp_gpo->gpo_func_version);
- /* retrieve AD_AT_FLAGS */
- ret = sysdb_attrs_get_int32_t(results[0], AD_AT_FLAGS,
&gp_gpo->gpo_flags);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_int32_t failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- DEBUG(SSSDBG_TRACE_ALL, "gpo_flags: %d\n", gp_gpo->gpo_flags);
- /* retrieve AD_AT_NT_SEC_DESC */
- ret = sysdb_attrs_get_el(results[0], AD_AT_NT_SEC_DESC, &el);
- if (ret != EOK && ret != ENOENT) {
DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_el() failed\n");
goto done;
- }
- if ((ret == ENOENT) || (el->num_values == 0)) {
DEBUG(SSSDBG_OP_FAILURE,
"nt_sec_desc attribute not found or has no value\n");
ret = ENOENT;
goto done;
- }
- ret = ad_gpo_parse_sd(gp_gpo, el[0].values[0].data, el[0].values[0].length,
&gp_gpo->gpo_sd);
- ret = ad_gpo_get_gpo_attrs_step(req);
- done:
- if (ret == EOK) {
tevent_req_done(req);
- } else if (ret != EAGAIN) {
tevent_req_error(req, ret);
- }
+}
+int +ad_gpo_process_gpo_recv(struct tevent_req *req,
TALLOC_CTX *mem_ctx,
struct gp_gpo ***candidate_gpos,
int *num_candidate_gpos)
+{
- struct ad_gpo_process_gpo_state *state =
tevent_req_data(req, struct ad_gpo_process_gpo_state);
- TEVENT_REQ_RETURN_ON_ERROR(req);
- *candidate_gpos = talloc_steal(mem_ctx, state->candidate_gpos);
- *num_candidate_gpos = state->num_candidate_gpos;
- return EOK;
+} diff --git a/src/providers/ad/ad_gpo.h b/src/providers/ad/ad_gpo.h new file mode 100644 index 0000000..99baf3c --- /dev/null +++ b/src/providers/ad/ad_gpo.h @@ -0,0 +1,55 @@ +/*
- SSSD
- Authors:
Stephen Gallagher <sgallagh@redhat.com>
- Copyright (C) 2012 Red Hat
I always knew that Stephen is ahead of time :-)
- This program is free software; you can redistribute it and/or modify
- 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/.
+*/
+#ifndef AD_GPO_H_ +#define AD_GPO_H_
+/*
- This pair of functions provides client-side GPO processing.
- While a GPO can target both user and computer objects, this
- implementation only supports targetting of computer objects.
- A GPO overview is at https://fedorahosted.org/sssd/wiki/GpoOverview
- In summary, client-side processing involves:
- determining the target's DN
- extracting the SOM object DNs (i.e. OUs and Domain) from target's DN
- including the target's Site as another SOM object
- determining which GPOs apply to the target's SOMs
- prioritizing GPOs based on SOM, link order, and whether GPO is "enforced"
- retrieving the corresponding GPO objects
- sending the GPO DNs to the CSE processing engine for policy application
- policy application currently consists of HBAC-like functionality
- */
+struct tevent_req * +ad_gpo_access_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct sss_domain_info *domain,
struct ad_access_ctx *ctx,
char *user);
+errno_t ad_gpo_access_recv(struct tevent_req *req);
+struct security_descriptor;
+#endif /* AD_GPO_H_ */ diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c index 3425b35..6af073d 100644 --- a/src/providers/ad/ad_id.c +++ b/src/providers/ad/ad_id.c @@ -555,10 +555,11 @@ ad_enumeration_master_done(struct tevent_req *subreq) struct ad_enumeration_state); char *flat_name; char *master_sid;
char *site; char *forest;
ret = ad_master_domain_recv(subreq, state,
&flat_name, &master_sid, &forest);
&flat_name, &master_sid, &site, &forest);
I just realize that I could have made this comment earlier but maybe it would be better to allow NULL arguments in ad_master_domain_recv() for values which are not needed by the caller instead of creating useless variables?
talloc_zfree(subreq); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "Cannot retrieve master domain info\n");
diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c index b191f73..cda6012 100644 --- a/src/providers/ad/ad_subdomains.c +++ b/src/providers/ad/ad_subdomains.c @@ -92,6 +92,7 @@ struct ad_subdomains_req_ctx {
char *master_sid; char *flat_name;
- char *site; char *forest;
};
@@ -572,7 +573,7 @@ static void ad_subdomains_master_dom_done(struct tevent_req *req)
ret = ad_master_domain_recv(req, ctx, &ctx->flat_name, &ctx->master_sid,
&ctx->forest);
talloc_zfree(req); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "Cannot retrieve master domain info\n");&ctx->site, &ctx->forest);
diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c index a3da4ac..5058f8b 100644 --- a/src/providers/ldap/sdap_async.c +++ b/src/providers/ldap/sdap_async.c @@ -19,6 +19,7 @@ along with this program. If not, see http://www.gnu.org/licenses/. */
#include <ctype.h> #include "util/util.h" #include "util/strtonum.h" @@ -1829,6 +1830,181 @@ sdap_x_deref_search_recv(struct tevent_req *req, return EOK; }
+/* ==Security Descriptor (ACL) search=================================== */ +struct sdap_sd_search_state {
- LDAPControl **ctrls;
- struct sdap_options *opts;
- size_t reply_count;
- struct sysdb_attrs **reply;
- struct sdap_reply sreply;
+};
+static int sdap_sd_search_create_control(struct sdap_handle *sh,
int val,
LDAPControl **ctrl);
+static int sdap_sd_search_ctrls_destructor(void *ptr); +static errno_t sdap_sd_search_parse_entry(struct sdap_handle *sh,
struct sdap_msg *msg,
void *pvt);
+static void sdap_sd_search_done(struct tevent_req *subreq);
+struct tevent_req * +sdap_sd_search_send(TALLOC_CTX *memctx, struct tevent_context *ev,
struct sdap_options *opts, struct sdap_handle *sh,
const char *base_dn, int sd_flags,
const char **attrs, int timeout)
+{
- struct tevent_req *req = NULL;
- struct tevent_req *subreq = NULL;
- struct sdap_sd_search_state *state;
- int ret;
- req = tevent_req_create(memctx, &state, struct sdap_sd_search_state);
- if (!req) return NULL;
- state->ctrls = talloc_zero_array(state, LDAPControl *, 2);
- state->opts = opts;
- if (state->ctrls == NULL) {
talloc_zfree(req);
return NULL;
Please use the convention used in other *_send() calls as well. Only return NULL if tevent_req_create() failed. For all errors call tevent_req_error() and tevent_req_post() and return req to the caller.
- }
- talloc_set_destructor((TALLOC_CTX *) state->ctrls,
sdap_sd_search_ctrls_destructor);
- ret = sdap_sd_search_create_control(sh, sd_flags, &state->ctrls[0]);
- if (ret != EOK) {
talloc_zfree(req);
DEBUG(SSSDBG_CRIT_FAILURE, "Could not create SD control\n");
return NULL;
see above
- }
- DEBUG(SSSDBG_TRACE_FUNC, "Searching entry [%s] using SD\n", base_dn);
- subreq = sdap_get_generic_ext_send(state, ev, opts, sh, base_dn,
LDAP_SCOPE_BASE, "(objectclass=*)", attrs,
false, state->ctrls, NULL, 0, timeout,
true, sdap_sd_search_parse_entry,
state);
- if (!subreq) {
talloc_zfree(req);
return NULL;
see above
- }
- tevent_req_set_callback(subreq, sdap_sd_search_done, req);
- return req;
+}
----- Original Message -----
On Thu, Apr 03, 2014 at 12:30:17PM -0400, Yassir Elley wrote:
----- Original Message -----
----- Original Message -----
----- Original Message -----
On (12/03/14 18:20), Yassir Elley wrote:
Hi,
The attached patch implements the LDAP component of the GPO-based access control project. For more details on the project, see https://fedorahosted.org/sssd/wiki/DesignDocs/ActiveDirectoryGPOIntegration. The project is not yet complete, since the SMB component still needs to be implemented. However, this seems like a useful milestone to allow others to review the code. This is my first patch submission to the list, so any feedback is appreciated.
In order to exercise the code in your AD environment, you will need to link a GPO to the site, domain, or OUs, associated with the policy target (i.e. domain member computer). Since we are not yet retrieving any policy files (over SMB), there is no access control taking place yet, meaning that any policy settings can be used in the GPOs. The only way to determine what is going on is by examining the logs.
In summary, the code currently does the following:
- Determines the DN of the policy target (e.g.
"cn=f20-laptop,OU=West,OU=Sales,DC=foo,DC=com") 2. Calculates the list of SOM DNs (Site, Domain and OUs) associated with the policy target DN 3. Retrieves various LDAP attributes from each SOM DN in order to populate a list of GPLinks (i.e. GPO DN plus GPO Options) 4. Creates a prioritized list of GPO DNs based on SOM Priority, Link Order, and GPO Options 5. Retrieves various LDAP attributes from each GPO DN in order to populate a list of candidate GPOs 6. Converts the candidate list to a filtered list, by removing any GPO that has a DACL which denies the policy target permission to apply the GPO 7. It is this filtered list that will be sent to the Short-Lived GPO Child process, which will make the necessary blocking SMB calls
Regards, Yassir.
We changed DEBUG messages recently and your patch cannot be compiled on current master. Probably, it happened when you rebased patches.
I am attaching which fixes these problems. And some (probably not all) code style issues: trailing whitespaces, tabs instead of spaces ...
Feel free to squash this patch to yours.
LS
sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
Hmmm... I did notice the recent DEBUG message changes and I went through and fixed each one so it compiled. I must have made a mistake somewhere when merging from my local gpo branch to my local master branch. Thanks for the patch!
Regards, Yassir. _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
I have attached an updated patch to address Lukas's initial comments.
Regards, Yassir. _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
I have re-organized the code (and added more comments) and attached an updated patch for code review. I hope this will make it easier to review.
Regards, Yassir.
Hi,
I've read the patches and did some basic tests. You can find my review comments in-line below. I had to add some NULL-checks to make my tests work, see details below as well.
Thanks for the thorough review. Much appreciated!
In general I think you should add an option to disable the GPO checks to be able to resolve regressions in existing setups.
I'm not sure I understand this comment. What do you mean by "existing setups"? If you mean GPO-unaware setups, then the GPO code should not break any existing regressions. If it did, we would want to know about it, right?
I don't know if you already have some unit tests in your tree, but there are several functions in your patch where unit tests would be nice.
If I create some unit tests, should I include them in the next patch? Or should the next patch to the list be limited to addressing your review comments? I just don't know the process.
If to comments are addressed we should push the patch to master to get more testing, coverity checks etc.
Sounds good. Below, I have included a few responses/questions related to your comments. If I have not responded to a comment, that means I agree with it and will make the necessary change. Is that OK? Or is it preferrable to give positive acknowledgment of each comment?
Thanks, Yassir.
bye, Sumit
From a508e36e67aa881665d0387ff418162e5f1825cd Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Mon, 20 Jan 2014 11:17:06 -0500 Subject: [PATCH] Implemented LDAP component of GPO-based access control
Makefile.am | 3 + src/providers/ad/ad_access.c | 125 ++- src/providers/ad/ad_domain_info.c | 34 +- src/providers/ad/ad_domain_info.h | 1 + src/providers/ad/ad_gpo.c | 2199 +++++++++++++++++++++++++++++++++++++ src/providers/ad/ad_gpo.h | 55 + src/providers/ad/ad_id.c | 3 +- src/providers/ad/ad_subdomains.c | 3 +- src/providers/ldap/sdap_async.c | 176 +++ src/providers/ldap/sdap_async.h | 14 + src/util/sss_ldap.h | 9 + 11 files changed, 2571 insertions(+), 51 deletions(-) create mode 100644 src/providers/ad/ad_gpo.c create mode 100644 src/providers/ad/ad_gpo.h
diff --git a/Makefile.am b/Makefile.am index f319624..2935d15 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1608,6 +1608,7 @@ ad_access_filter_tests_LDADD = \ libsss_ldap_common.la \ libsss_idmap.la \ libsss_krb5_common.la \
- libsss_ad.la \ libsss_test_common.la
I see
CCLD ad_access_filter_tests
*** Warning: Linking the executable ad_access_filter_tests against the loadable module *** libsss_ad.so is not portable!
when running make check, I think you have to replace libsss_ad.la by the list of source files here
I didn't understand this comment. Are you suggesting inserting ${libsss_ad_la_SOURCES} in the LDADD line? I don't think the linker deals with source files.
Also, since we have full control of libsss_ad.so, and since the unit tests will always be built together with the current version of libsss_ad.so, why does it matter that linking against libsss_ad.so is not portable. I thought that this warning would only be relevant if we were linking against a library that we had no control over.
...
- state->cindex++;
- if (state->clist[state->cindex] == NULL) {
DEBUG(SSSDBG_OP_FAILURE,
"Error retrieving access check result: %s\n",
sss_strerror(ret));
tevent_req_error(req, ret);
return;
/* If possible, retry with LDAP */
state->cindex++;
if (state->clist[state->cindex] == NULL) {
DEBUG(SSSDBG_OP_FAILURE,
"Error retrieving access check result: %s\n",
sss_strerror(ret));
tevent_req_error(req, ret);
return;
}
ret = ad_sdap_access_step(req, state->clist[state->cindex]);
if (ret != EOK) {
tevent_req_error(req, ret);
return;
}
/* Another check in progress */
You have to call return here because ad_sdap_access_step() will create a new request which will return to ad_sdap_access_done().
}
- ret = ad_access_step(req, state->clist[state->cindex]);
- if (ret != EOK) {
tevent_req_error(req, ret);
- subreq = ad_gpo_access_send(state,
state->be_ctx->ev,
state->domain,
state->ctx,
state->pd->user);
- if (!subreq) {
}tevent_req_error(req, ENOMEM); return;
- /* Another check in progress */
- tevent_req_set_callback(subreq, ad_gpo_access_done, req);
}
...
static void -ad_access_check_done(struct tevent_req *req) +ad_access_done(struct tevent_req *req) { errno_t ret; struct be_req *breq = tevent_req_callback_data(req, struct be_req); struct pam_data *pd = talloc_get_type(be_req_get_data(breq), struct pam_data);
Please do not remove this empty line, it separates declarations from the code. There is no formal requirement in the coding-style but I think it helps reading the code nonetheless.
Agreed.
I noticed that some other functions in this file also do not separate declarations from the code with an empty line. Should I fix those as well in this patch (even though those functions are not my code)? Or is that a separate patch? In general, is there a policy of limiting the next patch to only addressing code review comments (and not inserting additional fixes like the ones here).
ret = ad_access_recv(req); talloc_zfree(req); switch (ret) {
diff --git a/src/providers/ad/ad_domain_info.c b/src/providers/ad/ad_domain_info.c index 9594484..d18a9c1 100644 --- a/src/providers/ad/ad_domain_info.c +++ b/src/providers/ad/ad_domain_info.c @@ -44,7 +44,9 @@ static errno_t netlogon_get_domain_info(TALLOC_CTX *mem_ctx, struct sysdb_attrs *reply,
char **_flat_name, char **_forest)
char **_flat_name,
char **_site,
char **_forest)
{ errno_t ret; struct ldb_message_element *el; @@ -53,6 +55,7 @@ netlogon_get_domain_info(TALLOC_CTX *mem_ctx, enum ndr_err_code ndr_err; struct netlogon_samlogon_response response; const char *flat_name;
const char *site; const char *forest;
ret = sysdb_attrs_get_el(reply, AD_AT_NETLOGON, &el);
@@ -129,6 +132,25 @@ netlogon_get_domain_info(TALLOC_CTX *mem_ctx, goto done; }
- /* get site name */
- if (response.data.nt5_ex.client_site != NULL
&& response.data.nt5_ex.client_site[0] != '\0') {
site = response.data.nt5_ex.client_site;
- } else if (response.data.nt5_ex.next_closest_site != NULL
&& response.data.nt5_ex.next_closest_site[0] != '\0') {
site = response.data.nt5_ex.next_closest_site;
I think you don't want to use the next_closest_site here. In general for a domain member always belongs to a site, if it cannot be determined it is an error. The next_closest_site might have completely different policy settings. We use next_closest_site in the DNS lookup code because e.g. for request to a trusted forest there will only be a next_closest_site.
- } else {
ret = ENOENT;
goto done;
- }
- *_site = talloc_strdup(mem_ctx, site);
- if (*_site == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
ret = ENOMEM;
goto done;
- }
- ret = EOK;
done: talloc_free(ndr_pull);
...
diff --git a/src/providers/ad/ad_gpo.c b/src/providers/ad/ad_gpo.c new file mode 100644 index 0000000..5a44b24 --- /dev/null +++ b/src/providers/ad/ad_gpo.c @@ -0,0 +1,2199 @@ +/*
- SSSD
- ad_gpo.c
- Authors:
- Yassir Elley yelley@redhat.com
- Copyright (C) 2013 Red Hat
- This program is free software; you can redistribute it and/or modify
- 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
- */
+#include <security/pam_modules.h> +#include "util/util.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/ad/ad_gpo.h" +#include "providers/ldap/sdap_access.h" +#include "providers/ldap/sdap_async.h" +#include "providers/ldap/sdap.h" +#include <ndr.h> +#include <gen_ndr/security.h>
+#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" +#define SID_MAX_LEN 1024
+/* == common data structures and declarations ============================= */
+struct gp_som {
- char *som_dn;
- struct gp_gplink **gplink_list;
- int num_gplinks;
+};
+struct gp_gplink {
- char *gpo_dn;
- bool enforced;
+};
+struct gp_gpo {
- struct security_descriptor *gpo_sd;
- char *gpo_dn;
- char *gpo_guid;
- char *gpo_display_name;
- char *gpo_file_sys_path;
- uint32_t gpo_container_version;
- char **gpo_cse_guids;
- int num_gpo_cse_guids;
- int gpo_func_version;
- int gpo_flags;
+};
+enum ace_eval_status {
- AD_GPO_ACE_DENIED,
- AD_GPO_ACE_ALLOWED,
- AD_GPO_ACE_NEUTRAL
+};
+struct tevent_req *ad_gpo_process_som_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct sdap_id_conn_ctx *conn,
struct sdap_id_op *sdap_op,
struct sdap_options *opts,
int timeout,
char *target_dn,
char *domain_name);
+int ad_gpo_process_som_recv(struct tevent_req *req,
TALLOC_CTX *mem_ctx,
struct gp_som ***som_list);
+struct tevent_req *ad_gpo_process_gpo_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct sdap_id_op *sdap_op,
struct sdap_options *opts,
int timeout,
struct gp_som **som_list);
+int ad_gpo_process_gpo_recv(struct tevent_req *req,
TALLOC_CTX *mem_ctx,
struct gp_gpo ***candidate_gpos,
int *num_candidate_gpos);
+/* == ad_gpo_access_send/recv helpers =======================================*/
+/*
- 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
- gpo access checks are called, we can safely assume that the
user/computer
- has been authenticated. As such, this function always adds the
- AD_AUTHENTICATED_USERS_SID to the group_sids.
- */
+static errno_t +ad_gpo_get_sids(TALLOC_CTX *mem_ctx,
char *user,
struct sss_domain_info *domain,
const char **_user_sid,
const char ***_group_sids,
int *_group_size)
+{
- TALLOC_CTX *tmp_ctx = NULL;
- struct ldb_result *res;
- int ret = 0;
- int i = 0;
- int num_group_sids = 0;
- const char *user_sid = NULL;
- const char *group_sid = NULL;
- const char **group_sids = NULL;
- tmp_ctx = talloc_new(NULL);
- if (tmp_ctx == NULL) {
ret = ENOMEM;
goto done;
- }
- /* first result from sysdb_initgroups is user_sid; rest are group_sids
*/
- ret = sysdb_initgroups(tmp_ctx, domain, user, &res);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_initgroups failed: [%d](%s)\n",
ret, strerror(ret));
return ret;
- }
- user_sid = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_SID_STR,
NULL);
- num_group_sids = (res->count) - 1;
- /* include space for AD_AUTHENTICATED_USERS_SID and NULL */
- group_sids = talloc_array(tmp_ctx, const char *, num_group_sids + 1 +
1);
- if (group_sids == NULL) {
ret = ENOMEM;
goto done;
- }
- for (i = 0; i < num_group_sids; i++) {
group_sid = ldb_msg_find_attr_as_string(res->msgs[i+1],
SYSDB_SID_STR, NULL);
if (group_sid == NULL) {
continue;
}
group_sids[i] = talloc_strdup(group_sids, group_sid);
if (group_sids[i] == NULL) {
ret = ENOMEM;
goto done;
}
- }
- group_sids[i++] = AD_AUTHENTICATED_USERS_SID;
I think this is risky. In general nobody should get the idea to free the list entries manually. But if someone does he would fail here. I think it would be better if list members always have the same storage class so please use talloc_strdup() here as well.
- group_sids[i] = NULL;
- *_group_size = num_group_sids + 1;
- *_group_sids = talloc_steal(mem_ctx, group_sids);
- *_user_sid = talloc_strdup(mem_ctx, user_sid);
- return EOK;
- done:
- talloc_free(tmp_ctx);
- return ret;
+}
+bool string_to_sid(struct dom_sid *sidout, const char *sidstr); +int dom_sid_string_buf(const struct dom_sid *sid, char *buf, int buflen); +bool dom_sid_equal(const struct dom_sid *sid1, const struct dom_sid *sid2);
...
+static void +ad_gpo_target_dn_retrieval_done(struct tevent_req *subreq) +{
- struct tevent_req *req;
- struct ad_gpo_access_state *state;
- int ret;
- int dp_error;
- size_t reply_count;
- struct sysdb_attrs **reply;
- const char *target_dn = NULL;
- req = tevent_req_callback_data(subreq, struct tevent_req);
- state = tevent_req_data(req, struct ad_gpo_access_state);
- ret = sdap_get_generic_recv(subreq, state,
&reply_count, &reply);
- talloc_zfree(subreq);
- if (ret != EOK) {
ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
/* TBD: handle (dp_error == DP_ERR_OFFLINE) case */
DEBUG(SSSDBG_OP_FAILURE,
"Unable to get policy target's DN: [%d](%s)\n",
ret, strerror(ret));
ret = ENOENT;
goto done;
- }
- /* make sure there is only one non-NULL reply returned */
- if (reply_count < 1) {
DEBUG(SSSDBG_OP_FAILURE, "No DN retrieved for policy target.\n");
ret = ENOENT;
goto done;
- } else if (reply_count > 1) {
DEBUG(SSSDBG_OP_FAILURE, "Multiple replies for policy target\n");
ret = ERR_INTERNAL;
goto done;
- } else if (reply == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "reply_count is 1, but reply is NULL\n");
ret = ERR_INTERNAL;
goto done;
- }
- /* reply[0] holds requested attributes of single reply */
- ret = sysdb_attrs_get_string(reply[0], AD_AT_DN, &target_dn);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_string failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- state->target_dn = talloc_strdup(state, target_dn);
- if (state->target_dn == NULL) {
ret = ENOMEM;
goto done;
- }
- uint32_t uac;
Please declare variables at the beginning of the function.
- ret = sysdb_attrs_get_uint32_t(reply[0], AD_AT_UAC, &uac);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_uint32_t failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- /* we only support computer policy targets, not users */
- if (!(uac & UAC_WORKSTATION_TRUST_ACCOUNT)) {
ret = EINVAL;
goto done;
- }
- state->target_dn = talloc_strdup(state, target_dn);
- if (state->target_dn == NULL) {
ret = ENOMEM;
goto done;
- }
- subreq = ad_gpo_process_som_send(state,
state->ev,
state->conn,
state->sdap_op,
state->opts,
state->timeout,
state->target_dn,
state->domain->name);
- if (subreq == NULL) {
ret = ENOMEM;
goto done;
- }
- tevent_req_set_callback(subreq, ad_gpo_process_som_done, req);
- done:
- if (ret != EOK) {
tevent_req_error(req, ret);
- }
+}
+static void +ad_gpo_process_som_done(struct tevent_req *subreq) +{
- struct tevent_req *req;
- struct ad_gpo_access_state *state;
- int ret;
- struct gp_som **som_list;
- req = tevent_req_callback_data(subreq, struct tevent_req);
- state = tevent_req_data(req, struct ad_gpo_access_state);
- ret = ad_gpo_process_som_recv(subreq, state, &som_list);
- talloc_zfree(subreq);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Unable to get som list: [%d](%s)\n",
ret, strerror(ret));
ret = ENOENT;
goto done;
- }
- subreq = ad_gpo_process_gpo_send(state,
state->ev,
state->sdap_op,
state->opts,
state->timeout,
som_list);
- if (subreq == NULL) {
ret = ENOMEM;
goto done;
- }
- tevent_req_set_callback(subreq, ad_gpo_process_gpo_done, req);
Currently it is obvious that ret==EOK if we reached this point. But if some code is added in future it might be not so obvious anymore and I would suggest to explicitly set ret=EOK before entering the done: block or call return here. I would expect that a compiler will optimize out the line if it is clear that ret can only be EOK when reaching this point.
- done:
- if (ret != EOK) {
tevent_req_error(req, ret);
- }
+}
+/*
- 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.
- */
+static void +ad_gpo_process_gpo_done(struct tevent_req *subreq) +{
- struct tevent_req *req;
- struct ad_gpo_access_state *state;
- int ret;
- int dp_error;
- struct gp_gpo **candidate_gpos = NULL;
- int num_candidate_gpos = 0;
- int i = 0;
- req = tevent_req_callback_data(subreq, struct tevent_req);
- state = tevent_req_data(req, struct ad_gpo_access_state);
- ret = ad_gpo_process_gpo_recv(subreq, state, &candidate_gpos,
&num_candidate_gpos);
- talloc_zfree(subreq);
- ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
- if (ret != EOK) {
/* TBD: handle (dp_error == DP_ERR_OFFLINE) case */
DEBUG(SSSDBG_OP_FAILURE,
"Unable to get GPO list: [%d](%s)\n",
ret, strerror(ret));
ret = ENOENT;
goto done;
- }
- ret = ad_gpo_filter_gpos_by_dacl(state, state->user, state->domain,
candidate_gpos, num_candidate_gpos,
&state->dacl_filtered_gpos,
&state->num_dacl_filtered_gpos);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Unable to filter GPO list: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- if (state->dacl_filtered_gpos[0] == NULL) {
DEBUG(SSSDBG_TRACE_FUNC, "dacl_filtered_gpos is empty\n");
- }
- for (i = 0; i < state->num_dacl_filtered_gpos; i++) {
DEBUG(SSSDBG_TRACE_FUNC, "dacl_filtered_gpos[%d]->gpo_guid is
%s\n", i,
state->dacl_filtered_gpos[i]->gpo_guid);
- }
- /* TBD: initiate SMB retrieval */
- DEBUG(SSSDBG_TRACE_FUNC, "time for SMB retrieval\n");
see above
- done:
- if (ret == EOK) {
tevent_req_done(req);
- } else if (ret != EAGAIN) {
tevent_req_error(req, ret);
- }
+}
+errno_t +ad_gpo_access_recv(struct tevent_req *req) +{
- TEVENT_REQ_RETURN_ON_ERROR(req);
- return EOK;
+}
+/* == ad_gpo_process_som_send/recv helpers ================================= */
+/*
- This function returns the parent of an LDAP DN
- */
+static char *ad_gpo_parent_dn(const char *dn) +{
- char *p;
- if (dn == NULL) {
return NULL;
- }
- p = strchr(dn, ',');
- if (p == NULL) {
return NULL;
- }
I would suggest the use calls from OpenLDAP or ldb (there is ldb_dn_get_parent() maybe to already does the right thing) to properly split the DN in components and created the parent DN. Your way would fail e.g. if the RDN contains an escaped comma ",".
- return p+1;
+}
+/*
- 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 +ad_gpo_populate_som_list(TALLOC_CTX *mem_ctx,
char *target_dn,
int *_num_soms,
struct gp_som ***_som_list)
+{
- TALLOC_CTX *tmp_ctx = NULL;
- int ret;
- int rdn_count = 0;
- int som_idx = 0;
- struct gp_som **som_list;
- char *parent_dn = NULL;
- char *tmp_dn = NULL;
- tmp_ctx = talloc_new(NULL);
- if (tmp_ctx == NULL) {
ret = ENOMEM;
goto done;
- }
- tmp_dn = target_dn;
- while ((parent_dn = ad_gpo_parent_dn(tmp_dn))) {
rdn_count++;
tmp_dn = parent_dn;
- }
There is ldb_dn_get_comp_num() which should return the number of components in a save way.
- if (rdn_count == 0) {
*_som_list = NULL;
ret = EOK;
goto done;
- }
- /* assume the worst-case, in which every parent is a SOM */
- /* include space for Site SOM and NULL: rdn_count + 1 + 1 */
- som_list = talloc_array(tmp_ctx, struct gp_som *, rdn_count + 1 + 1);
- if (som_list == NULL) {
ret = ENOMEM;
goto done;
- }
- /* first, populate the OU and Domain SOMs */
- tmp_dn = target_dn;
- while ((parent_dn = ad_gpo_parent_dn(tmp_dn))) {
if ((strncasecmp(parent_dn, "OU=", strlen("OU=")) == 0) ||
(strncasecmp(parent_dn, "DC=", strlen("DC=")) == 0)) {
som_list[som_idx] = talloc_zero(som_list, struct gp_som);
if (som_list[som_idx] == NULL) {
ret = ENOMEM;
goto done;
}
som_list[som_idx]->som_dn = talloc_strdup(som_list[som_idx],
parent_dn);
if (som_list[som_idx]->som_dn == NULL) {
ret = ENOMEM;
goto done;
}
som_idx++;
}
if (strncasecmp(parent_dn, "DC=", strlen("DC=")) == 0) {
break;
}
tmp_dn = parent_dn;
- }
- som_list[som_idx] = NULL;
- *_num_soms = som_idx;
- *_som_list = talloc_steal(mem_ctx, som_list);
- ret = EOK;
- done:
- talloc_free(tmp_ctx);
- 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 +ad_gpo_populate_gplink_list(TALLOC_CTX *mem_ctx,
char *som_dn,
char *raw_gplink_value,
struct gp_gplink ***_gplink_list,
bool allow_enforced_only)
+{
- TALLOC_CTX *tmp_ctx = NULL;
- 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_number;
- int gplink_count = 0;
- int num_enabled = 0;
- if (raw_gplink_value == NULL ||
*raw_gplink_value == '\0' ||
_gplink_list == NULL) {
return EINVAL;
- }
- DEBUG(SSSDBG_TRACE_ALL, "som_dn: %s\n", som_dn);
- tmp_ctx = talloc_new(NULL);
- if (tmp_ctx == NULL) {
ret = ENOMEM;
goto done;
- }
- ptr = raw_gplink_value;
- while ((ptr = strchr(ptr, delim))) {
if (ptr == NULL) break;
ptr++;
gplink_count++;
- }
- if (gplink_count == 0) {
ret = EINVAL;
goto done;
- }
- gplink_list = talloc_array(tmp_ctx, struct gp_gplink *, gplink_count +
1);
- if (gplink_list == NULL) {
ret = ENOMEM;
goto done;
- }
- num_enabled = 0;
- ptr = raw_gplink_value;
- for (i = 0; i < gplink_count; i++) {
first = ptr + 1;
last = strchr(first, delim);
if (last == NULL) {
break;
}
*last = '\0';
last++;
dn = first;
if ( strncasecmp(dn, "LDAP://", 7)== 0 ) {
dn = dn + 7;
}
are you sure this can only be LDAP:// ? I wonder if it can be LDAPS:// is the original request is using LDAPS? But since AD does not activate LDAPS by default it might be safe.
The [MS-GPOL] spec indicates that the "LDAP://" prefix should be stripped from each GPO DN before being used in an LDAP search.
gplink_options = strchr(first, ';');
if (gplink_options == NULL) {
break;
}
*gplink_options = '\0';
gplink_options++;
gplink_number = atoi(gplink_options);
In general we prefer strto* for better error checking. I assume that gplink_options is expected to be a 32bit value. If this is true you might want to use strtouint32(). In this case it might be better to declare gplink_options as uint32t instead of int?
DEBUG(SSSDBG_TRACE_ALL,
"gplink_list[%d]: [%s; %d]\n", num_enabled, dn,
gplink_number);
if ((gplink_number == 1) || (gplink_number ==3)) {
/* ignore flag is set */
DEBUG(SSSDBG_TRACE_ALL, "ignored gpo skipped\n");
ptr = last;
continue;
}
if (allow_enforced_only && (gplink_number == 0)) {
/* unenforced flag is set; only enforced gpos allowed */
DEBUG(SSSDBG_TRACE_ALL, "unenforced gpo skipped\n");
ptr = last;
continue;
}
gplink_list[num_enabled] = talloc_zero(gplink_list, struct
gp_gplink);
if (gplink_list[num_enabled] == NULL) {
ret = ENOMEM;
goto done;
}
gplink_list[num_enabled]->gpo_dn =
talloc_strdup(gplink_list[num_enabled], dn);
if (gplink_list[num_enabled]->gpo_dn == NULL) {
ret = ENOMEM;
goto done;
}
if (gplink_number == 0) {
gplink_list[num_enabled]->enforced = 0;
num_enabled++;
} else if (gplink_number == 2) {
gplink_list[num_enabled]->enforced = 1;
num_enabled++;
} else {
ret = EINVAL;
goto done;
}
ptr = last;
- }
- gplink_list[num_enabled] = NULL;
- *_gplink_list = talloc_steal(mem_ctx, gplink_list);
- ret = EOK;
- done:
- talloc_free(tmp_ctx);
- return ret;
+}
...
+static void +ad_gpo_get_som_attrs_done(struct tevent_req *subreq) +{
- struct tevent_req *req;
- struct ad_gpo_process_som_state *state;
- int ret;
- int dp_error;
- size_t num_results;
- struct sysdb_attrs **results;
- struct ldb_message_element *el = NULL;
- uint8_t *raw_gplink_value;
- uint8_t *raw_gpoptions_value;
- int allow_enforced_only = 0;
- struct gp_som *gp_som;
- req = tevent_req_callback_data(subreq, struct tevent_req);
- state = tevent_req_data(req, struct ad_gpo_process_som_state);
- ret = sdap_get_generic_recv(subreq, state,
&num_results, &results);
- talloc_zfree(subreq);
- if (ret != EOK) {
ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
/* TBD: handle (dp_error == DP_ERR_OFFLINE) case */
DEBUG(SSSDBG_OP_FAILURE,
"Unable to get SOM attributes: [%d](%s)\n",
ret, strerror(ret));
ret = ENOENT;
goto done;
- }
- if ((num_results < 1) || (results == NULL)) {
DEBUG(SSSDBG_OP_FAILURE, "no attrs found for SOM; try next
SOM.\n");
ret = ad_gpo_get_som_attrs_step(req);
goto done;
- } else if (num_results > 1) {
DEBUG(SSSDBG_OP_FAILURE, "Received multiple replies\n");
ret = ERR_INTERNAL;
goto done;
- }
- /* Get the gplink value, if available */
- ret = sysdb_attrs_get_el(results[0], AD_AT_GPLINK, &el);
- if (ret != EOK && ret != ENOENT) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_el() failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- if ((ret == ENOENT) || (el->num_values == 0)) {
DEBUG(SSSDBG_OP_FAILURE, "no attrs found for SOM; try next
SOM\n");
ret = ad_gpo_get_som_attrs_step(req);
goto done;
- }
- raw_gplink_value = el[0].values[0].data;
- ret = sysdb_attrs_get_el(results[0], AD_AT_GPOPTIONS, &el);
- if (ret != EOK && ret != ENOENT) {
DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_el() failed\n");
goto done;
- }
- if ((ret == ENOENT) || (el->num_values == 0)) {
DEBUG(SSSDBG_TRACE_ALL,
"gpoptions attr not found or has no value; defaults to
0\n");
allow_enforced_only = 0;
- } else {
raw_gpoptions_value = el[0].values[0].data;
allow_enforced_only = atoi((char *)raw_gpoptions_value);
see above about atoi();
- }
- gp_som = state->som_list[state->som_index];
- ret = ad_gpo_populate_gplink_list(gp_som,
gp_som->som_dn,
(char *)raw_gplink_value,
&gp_som->gplink_list,
state->allow_enforced_only);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"ad_gpo_populate_gplink_list() failed\n");
goto done;
- }
- if (allow_enforced_only) {
state->allow_enforced_only = 1;
- }
- ret = ad_gpo_get_som_attrs_step(req);
- done:
- if (ret == EOK) {
tevent_req_done(req);
- } else if (ret != EAGAIN) {
tevent_req_error(req, ret);
- }
+}
+int +ad_gpo_process_som_recv(struct tevent_req *req,
TALLOC_CTX *mem_ctx,
struct gp_som ***som_list)
+{
- struct ad_gpo_process_som_state *state =
tevent_req_data(req, struct ad_gpo_process_som_state);
- TEVENT_REQ_RETURN_ON_ERROR(req);
- *som_list = talloc_steal(mem_ctx, state->som_list);
- 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 +ad_gpo_populate_candidate_gpos(TALLOC_CTX *mem_ctx,
struct gp_som **som_list,
struct gp_gpo ***_candidate_gpos,
int *_num_candidate_gpos)
+{
- TALLOC_CTX *tmp_ctx = NULL;
- struct gp_som *gp_som = NULL;
- struct gp_gplink *gp_gplink = NULL;
- struct gp_gpo **candidate_gpos = NULL;
- int num_candidate_gpos = 0;
- char **enforced_gpo_dns = NULL;
- 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;
- tmp_ctx = talloc_new(NULL);
- if (tmp_ctx == NULL) {
ret = ENOMEM;
goto done;
- }
- while (som_list[i]) {
gp_som = som_list[i];
j = 0;
while (gp_som->gplink_list[j]) {
I had to change this to
while (gp_som && gp_som->gplink_list && gp_som->gplink_list[j]) {
to make my basic tests work. Maybe it would be good to add such a NULL check to the som_list while loop as well?
gp_gplink = gp_som->gplink_list[j];
if (gp_gplink == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "unexpected null gp_gplink\n");
ret = EINVAL;
goto done;
}
if (gp_gplink->enforced) {
num_enforced++;
} else {
num_unenforced++;
}
j++;
}
i++;
- }
- num_candidate_gpos = num_enforced + num_unenforced;
- if (num_candidate_gpos == 0) {
*_candidate_gpos = NULL;
*_num_candidate_gpos = 0;
ret = EOK;
goto done;
- }
- enforced_gpo_dns = talloc_array(tmp_ctx, char *, num_enforced + 1);
- if (enforced_gpo_dns == NULL) {
ret = ENOMEM;
goto done;
- }
- unenforced_gpo_dns = talloc_array(tmp_ctx, char *, num_unenforced +
1);
- if (unenforced_gpo_dns == NULL) {
ret = ENOMEM;
goto done;
- }
- i = 0;
- while (som_list[i]) {
gp_som = som_list[i];
j = 0;
while (gp_som->gplink_list[j]) {
see above
gp_gplink = gp_som->gplink_list[j];
if (gp_gplink == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "unexpected null gp_gplink\n");
ret = EINVAL;
goto done;
}
if (gp_gplink->enforced) {
enforced_gpo_dns[enforced_idx] =
talloc_strdup(enforced_gpo_dns, gp_gplink->gpo_dn);
if (enforced_gpo_dns[enforced_idx] == NULL) {
ret = ENOMEM;
goto done;
}
enforced_idx++;
} else {
unenforced_gpo_dns[unenforced_idx] =
talloc_strdup(unenforced_gpo_dns, gp_gplink->gpo_dn);
if (unenforced_gpo_dns[unenforced_idx] == NULL) {
ret = ENOMEM;
goto done;
}
unenforced_idx++;
}
j++;
}
i++;
- }
- enforced_gpo_dns[num_enforced] = NULL;
- unenforced_gpo_dns[num_unenforced] = NULL;
- candidate_gpos = talloc_array(tmp_ctx,
struct gp_gpo *,
num_candidate_gpos + 1);
- if (candidate_gpos == NULL) {
ret = ENOMEM;
goto done;
- }
- gpo_dn_idx = 0;
- for (i = num_unenforced - 1; i >= 0; i--) {
candidate_gpos[gpo_dn_idx] = talloc_zero(candidate_gpos, struct
gp_gpo);
if (candidate_gpos[gpo_dn_idx] == NULL) {
ret = ENOMEM;
goto done;
}
candidate_gpos[gpo_dn_idx]->gpo_dn =
talloc_strdup(candidate_gpos[gpo_dn_idx],
unenforced_gpo_dns[i]);
Is there a reason for adding the unenforced_gpo_dns in reverse order compared to enforced_gpo_dns?
The [MS-GPOL] spec indicates that unenforced gpo_dns must be *prepended*, while enforced_gpo_dns must be *appended*. I implemented prepending of unenforced_gpo_dns by adding them in reverse order.
if (candidate_gpos[gpo_dn_idx]->gpo_dn == NULL) {
ret = ENOMEM;
goto done;
}
DEBUG(SSSDBG_TRACE_FUNC,
"candidate_gpos[%d]->gpo_dn: %s\n",
gpo_dn_idx, candidate_gpos[gpo_dn_idx]->gpo_dn);
gpo_dn_idx++;
- }
- for (i = 0; i < num_enforced; i++) {
candidate_gpos[gpo_dn_idx] = talloc_zero(candidate_gpos, struct
gp_gpo);
if (candidate_gpos[gpo_dn_idx] == NULL) {
ret = ENOMEM;
goto done;
}
candidate_gpos[gpo_dn_idx]->gpo_dn =
talloc_strdup(candidate_gpos[gpo_dn_idx],
enforced_gpo_dns[i]);
if (candidate_gpos[gpo_dn_idx]->gpo_dn == NULL) {
ret = ENOMEM;
goto done;
}
DEBUG(SSSDBG_TRACE_FUNC,
"candidate_gpos[%d]->gpo_dn: %s\n",
gpo_dn_idx, candidate_gpos[gpo_dn_idx]->gpo_dn);
gpo_dn_idx++;
- }
- candidate_gpos[gpo_dn_idx] = NULL;
- *_candidate_gpos = talloc_steal(mem_ctx, candidate_gpos);
- *_num_candidate_gpos = num_candidate_gpos;
- ret = EOK;
- done:
- talloc_free(tmp_ctx);
- return ret;
+}
...
+/*
- 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 * +ad_gpo_process_gpo_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct sdap_id_op *sdap_op,
struct sdap_options *opts,
int timeout,
struct gp_som **som_list)
+{
- struct tevent_req *req;
- struct ad_gpo_process_gpo_state *state;
- errno_t ret;
- req = tevent_req_create(mem_ctx, &state, struct
ad_gpo_process_gpo_state);
- if (req == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
return NULL;
- }
- state->ev = ev;
- state->sdap_op = sdap_op;
- state->opts = opts;
- state->timeout = timeout;
- state->gpo_index = -1;
- state->candidate_gpos = NULL;
- state->num_candidate_gpos = 0;
- ret = ad_gpo_populate_candidate_gpos(state,
som_list,
&state->candidate_gpos,
&state->num_candidate_gpos);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Unable to retrieve GPO List: [%d](%s)\n",
ret, strerror(ret));
ret = ENOENT;
goto immediately;
- }
- if (state->candidate_gpos == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "no gpos found\n");
goto immediately;
I think you should set ret here, otherwise it would be EOK which might mot be what you want.
- }
- ret = ad_gpo_get_gpo_attrs_step(req);
+immediately:
- if (ret == EOK) {
tevent_req_done(req);
tevent_req_post(req, ev);
- } else if (ret != EAGAIN) {
tevent_req_error(req, ret);
tevent_req_post(req, ev);
- }
- return req;
+}
+static errno_t +ad_gpo_get_gpo_attrs_step(struct tevent_req *req) +{
- const char *attrs[] = {AD_AT_NT_SEC_DESC, AD_AT_CN,
AD_AT_DISPLAY_NAME,
AD_AT_FILE_SYS_PATH, AD_AT_VERSION_NUMBER,
AD_AT_MACHINE_EXT_NAMES, AD_AT_FUNC_VERSION,
AD_AT_FLAGS, NULL};
- struct tevent_req *subreq;
- struct ad_gpo_process_gpo_state *state;
- state = tevent_req_data(req, struct ad_gpo_process_gpo_state);
- state->gpo_index++;
- struct gp_gpo *gp_gpo = state->candidate_gpos[state->gpo_index];
- /* gp_gpo is NULL only after all GPOs have been processed */
- if (gp_gpo == NULL) return EOK;
- char *gpo_dn = gp_gpo->gpo_dn;
- subreq = sdap_sd_search_send(state, state->ev,
state->opts,
sdap_id_op_handle(state->sdap_op),
gpo_dn, SECINFO_DACL, attrs,
state->timeout);
- if (subreq == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send failed.\n");
return ENOMEM;
- }
- tevent_req_set_callback(subreq, ad_gpo_get_gpo_attrs_done, req);
- return EAGAIN;
+}
+static void +ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq) +{
- struct tevent_req *req;
- struct ad_gpo_process_gpo_state *state;
- int ret;
- int dp_error;
- size_t num_results;
- struct sysdb_attrs **results;
- struct ldb_message_element *el = NULL;
- const char *gpo_guid = NULL;
- char *smb_uri = NULL;
- req = tevent_req_callback_data(subreq, struct tevent_req);
- state = tevent_req_data(req, struct ad_gpo_process_gpo_state);
- ret = sdap_sd_search_recv(subreq, state, &num_results, &results);
- talloc_zfree(subreq);
- if (ret != EOK) {
ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
/* TBD: handle (dp_error == DP_ERR_OFFLINE) case */
DEBUG(SSSDBG_OP_FAILURE,
"Unable to get GPO attributes: [%d](%s)\n",
ret, strerror(ret));
ret = ENOENT;
goto done;
- }
- if ((num_results < 1) || (results == NULL)) {
DEBUG(SSSDBG_OP_FAILURE, "no attrs found for GPO; try next
GPO.\n");
ret = ad_gpo_get_gpo_attrs_step(req);
goto done;
- }
- else if (num_results > 1) {
DEBUG(SSSDBG_OP_FAILURE, "Received multiple replies\n");
ret = ERR_INTERNAL;
goto done;
- }
- struct gp_gpo *gp_gpo = state->candidate_gpos[state->gpo_index];
- /* retrieve AD_AT_CN */
- ret = sysdb_attrs_get_string(results[0], AD_AT_CN, &gpo_guid);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_string failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- gp_gpo->gpo_guid = talloc_strdup(gp_gpo, gpo_guid);
- if (gp_gpo->gpo_guid == NULL) {
ret = ENOMEM;
goto done;
- }
- DEBUG(SSSDBG_TRACE_ALL, "populating attrs for gpo_guid: %s\n",
gp_gpo->gpo_guid);
- /* retrieve AD_AT_DISPLAY_NAME */
- const char *gpo_display_name = NULL;
Please move the declaration to the beginning of the function.
- ret = sysdb_attrs_get_string(results[0], AD_AT_DISPLAY_NAME,
&gpo_display_name);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_string failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- gp_gpo->gpo_display_name = talloc_strdup(gp_gpo, gpo_display_name);
- if (gp_gpo->gpo_display_name == NULL) {
ret = ENOMEM;
goto done;
- }
- DEBUG(SSSDBG_TRACE_ALL, "gpo_display_name: %s\n",
gp_gpo->gpo_display_name);
- /* retrieve AD_AT_FILE_SYS_PATH */
- const char *raw_file_sys_path = NULL;
see above
- ret = sysdb_attrs_get_string(results[0],
AD_AT_FILE_SYS_PATH,
&raw_file_sys_path);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_string failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- char *file_sys_path = talloc_strdup(gp_gpo, raw_file_sys_path);
see above
- ad_gpo_convert_to_smb_uri(state, file_sys_path, &smb_uri);
- gp_gpo->gpo_file_sys_path = talloc_asprintf(gp_gpo, "%s/Machine",
smb_uri);
- if (gp_gpo->gpo_file_sys_path == NULL) {
ret = ENOMEM;
goto done;
- }
- DEBUG(SSSDBG_TRACE_FUNC, "gpo_file_sys_path: %s\n",
gp_gpo->gpo_file_sys_path);
- /* retrieve AD_AT_VERSION_NUMBER */
- ret = sysdb_attrs_get_uint32_t(results[0], AD_AT_VERSION_NUMBER,
&gp_gpo->gpo_container_version);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_uint32_t failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- DEBUG(SSSDBG_TRACE_ALL, "gpo_container_version: %d\n",
gp_gpo->gpo_container_version);
- /* retrieve AD_AT_MACHINE_EXT_NAMES */
- ret = sysdb_attrs_get_el(results[0], AD_AT_MACHINE_EXT_NAMES, &el);
- if (ret != EOK && ret != ENOENT) {
DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_el() failed\n");
goto done;
- }
- if ((ret == ENOENT) || (el->num_values == 0)) {
DEBUG(SSSDBG_OP_FAILURE,
"machine_ext_names not found or has no value\n");
ret = ENOENT;
goto done;
- }
- uint8_t *raw_machine_ext_names = el[0].values[0].data;
see above
- ret = ad_gpo_parse_machine_ext_names(gp_gpo,
(char *)raw_machine_ext_names,
&gp_gpo->gpo_cse_guids,
&gp_gpo->num_gpo_cse_guids);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"ad_gpo_parse_machine_ext_names() failed\n");
goto done;
- }
- /* retrieve AD_AT_FUNC_VERSION */
- ret = sysdb_attrs_get_int32_t(results[0], AD_AT_FUNC_VERSION,
&gp_gpo->gpo_func_version);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_int32_t failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- DEBUG(SSSDBG_TRACE_ALL, "gpo_func_version: %d\n",
gp_gpo->gpo_func_version);
- /* retrieve AD_AT_FLAGS */
- ret = sysdb_attrs_get_int32_t(results[0], AD_AT_FLAGS,
&gp_gpo->gpo_flags);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_int32_t failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- DEBUG(SSSDBG_TRACE_ALL, "gpo_flags: %d\n", gp_gpo->gpo_flags);
- /* retrieve AD_AT_NT_SEC_DESC */
- ret = sysdb_attrs_get_el(results[0], AD_AT_NT_SEC_DESC, &el);
- if (ret != EOK && ret != ENOENT) {
DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_el() failed\n");
goto done;
- }
- if ((ret == ENOENT) || (el->num_values == 0)) {
DEBUG(SSSDBG_OP_FAILURE,
"nt_sec_desc attribute not found or has no value\n");
ret = ENOENT;
goto done;
- }
- ret = ad_gpo_parse_sd(gp_gpo, el[0].values[0].data,
el[0].values[0].length,
&gp_gpo->gpo_sd);
- ret = ad_gpo_get_gpo_attrs_step(req);
- done:
- if (ret == EOK) {
tevent_req_done(req);
- } else if (ret != EAGAIN) {
tevent_req_error(req, ret);
- }
+}
+int +ad_gpo_process_gpo_recv(struct tevent_req *req,
TALLOC_CTX *mem_ctx,
struct gp_gpo ***candidate_gpos,
int *num_candidate_gpos)
+{
- struct ad_gpo_process_gpo_state *state =
tevent_req_data(req, struct ad_gpo_process_gpo_state);
- TEVENT_REQ_RETURN_ON_ERROR(req);
- *candidate_gpos = talloc_steal(mem_ctx, state->candidate_gpos);
- *num_candidate_gpos = state->num_candidate_gpos;
- return EOK;
+} diff --git a/src/providers/ad/ad_gpo.h b/src/providers/ad/ad_gpo.h new file mode 100644 index 0000000..99baf3c --- /dev/null +++ b/src/providers/ad/ad_gpo.h @@ -0,0 +1,55 @@ +/*
- SSSD
- Authors:
Stephen Gallagher <sgallagh@redhat.com>
- Copyright (C) 2012 Red Hat
I always knew that Stephen is ahead of time :-)
- This program is free software; you can redistribute it and/or modify
- 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/.
+*/
+#ifndef AD_GPO_H_ +#define AD_GPO_H_
+/*
- This pair of functions provides client-side GPO processing.
- While a GPO can target both user and computer objects, this
- implementation only supports targetting of computer objects.
- A GPO overview is at https://fedorahosted.org/sssd/wiki/GpoOverview
- In summary, client-side processing involves:
- determining the target's DN
- extracting the SOM object DNs (i.e. OUs and Domain) from target's DN
- including the target's Site as another SOM object
- determining which GPOs apply to the target's SOMs
- prioritizing GPOs based on SOM, link order, and whether GPO is
"enforced"
- retrieving the corresponding GPO objects
- sending the GPO DNs to the CSE processing engine for policy
application
- policy application currently consists of HBAC-like functionality
- */
+struct tevent_req * +ad_gpo_access_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct sss_domain_info *domain,
struct ad_access_ctx *ctx,
char *user);
+errno_t ad_gpo_access_recv(struct tevent_req *req);
+struct security_descriptor;
+#endif /* AD_GPO_H_ */ diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c index 3425b35..6af073d 100644 --- a/src/providers/ad/ad_id.c +++ b/src/providers/ad/ad_id.c @@ -555,10 +555,11 @@ ad_enumeration_master_done(struct tevent_req *subreq) struct ad_enumeration_state); char *flat_name; char *master_sid;
char *site; char *forest;
ret = ad_master_domain_recv(subreq, state,
&flat_name, &master_sid, &forest);
&flat_name, &master_sid, &site, &forest);
I just realize that I could have made this comment earlier but maybe it would be better to allow NULL arguments in ad_master_domain_recv() for values which are not needed by the caller instead of creating useless variables?
talloc_zfree(subreq); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "Cannot retrieve master domain info\n");
diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c index b191f73..cda6012 100644 --- a/src/providers/ad/ad_subdomains.c +++ b/src/providers/ad/ad_subdomains.c @@ -92,6 +92,7 @@ struct ad_subdomains_req_ctx {
char *master_sid; char *flat_name;
- char *site; char *forest;
};
@@ -572,7 +573,7 @@ static void ad_subdomains_master_dom_done(struct tevent_req *req)
ret = ad_master_domain_recv(req, ctx, &ctx->flat_name, &ctx->master_sid,
&ctx->forest);
talloc_zfree(req); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "Cannot retrieve master domain info\n");&ctx->site, &ctx->forest);
diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c index a3da4ac..5058f8b 100644 --- a/src/providers/ldap/sdap_async.c +++ b/src/providers/ldap/sdap_async.c @@ -19,6 +19,7 @@ along with this program. If not, see http://www.gnu.org/licenses/. */
#include <ctype.h> #include "util/util.h" #include "util/strtonum.h" @@ -1829,6 +1830,181 @@ sdap_x_deref_search_recv(struct tevent_req *req, return EOK; }
+/* ==Security Descriptor (ACL) search=================================== */ +struct sdap_sd_search_state {
- LDAPControl **ctrls;
- struct sdap_options *opts;
- size_t reply_count;
- struct sysdb_attrs **reply;
- struct sdap_reply sreply;
+};
+static int sdap_sd_search_create_control(struct sdap_handle *sh,
int val,
LDAPControl **ctrl);
+static int sdap_sd_search_ctrls_destructor(void *ptr); +static errno_t sdap_sd_search_parse_entry(struct sdap_handle *sh,
struct sdap_msg *msg,
void *pvt);
+static void sdap_sd_search_done(struct tevent_req *subreq);
+struct tevent_req * +sdap_sd_search_send(TALLOC_CTX *memctx, struct tevent_context *ev,
struct sdap_options *opts, struct sdap_handle *sh,
const char *base_dn, int sd_flags,
const char **attrs, int timeout)
+{
- struct tevent_req *req = NULL;
- struct tevent_req *subreq = NULL;
- struct sdap_sd_search_state *state;
- int ret;
- req = tevent_req_create(memctx, &state, struct sdap_sd_search_state);
- if (!req) return NULL;
- state->ctrls = talloc_zero_array(state, LDAPControl *, 2);
- state->opts = opts;
- if (state->ctrls == NULL) {
talloc_zfree(req);
return NULL;
Please use the convention used in other *_send() calls as well. Only return NULL if tevent_req_create() failed. For all errors call tevent_req_error() and tevent_req_post() and return req to the caller.
- }
- talloc_set_destructor((TALLOC_CTX *) state->ctrls,
sdap_sd_search_ctrls_destructor);
- ret = sdap_sd_search_create_control(sh, sd_flags, &state->ctrls[0]);
- if (ret != EOK) {
talloc_zfree(req);
DEBUG(SSSDBG_CRIT_FAILURE, "Could not create SD control\n");
return NULL;
see above
- }
- DEBUG(SSSDBG_TRACE_FUNC, "Searching entry [%s] using SD\n", base_dn);
- subreq = sdap_get_generic_ext_send(state, ev, opts, sh, base_dn,
LDAP_SCOPE_BASE, "(objectclass=*)",
attrs,
false, state->ctrls, NULL, 0,
timeout,
true, sdap_sd_search_parse_entry,
state);
- if (!subreq) {
talloc_zfree(req);
return NULL;
see above
- }
- tevent_req_set_callback(subreq, sdap_sd_search_done, req);
- return req;
+}
sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
On Fri, Apr 11, 2014 at 06:32:05PM -0400, Yassir Elley wrote:
----- Original Message -----
On Thu, Apr 03, 2014 at 12:30:17PM -0400, Yassir Elley wrote:
----- Original Message -----
Hi,
I've read the patches and did some basic tests. You can find my review comments in-line below. I had to add some NULL-checks to make my tests work, see details below as well.
Thanks for the thorough review. Much appreciated!
In general I think you should add an option to disable the GPO checks to be able to resolve regressions in existing setups.
I'm not sure I understand this comment. What do you mean by "existing setups"? If you mean GPO-unaware setups, then the GPO code should not break any existing regressions. If it did, we would want to know about it, right?
There might be setup where the GPO would in theory apply to a Linux client running SSSD but since we didn't support it before access was always granted. If now the GPO is evaluated a user might be rejected although he was able to log in before. In this case it should be possible to disable the GPO check to return the previous behaviour without completely disabling access control.
I don't know if you already have some unit tests in your tree, but there are several functions in your patch where unit tests would be nice.
If I create some unit tests, should I include them in the next patch? Or should the next patch to the list be limited to addressing your review comments? I just don't know the process.
If I add a single new function I typically add the unit tests in the same patch. In your case I think it would be more useful to create a separate patch only with the unit tests.
If to comments are addressed we should push the patch to master to get more testing, coverity checks etc.
Sounds good. Below, I have included a few responses/questions related to your comments. If I have not responded to a comment, that means I agree with it and will make the necessary change. Is that OK? Or is it preferrable to give positive acknowledgment of each comment?
No, comments are only necessary where you disagree.
Thanks, Yassir.
bye, Sumit
From a508e36e67aa881665d0387ff418162e5f1825cd Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Mon, 20 Jan 2014 11:17:06 -0500 Subject: [PATCH] Implemented LDAP component of GPO-based access control
Makefile.am | 3 + src/providers/ad/ad_access.c | 125 ++- src/providers/ad/ad_domain_info.c | 34 +- src/providers/ad/ad_domain_info.h | 1 + src/providers/ad/ad_gpo.c | 2199 +++++++++++++++++++++++++++++++++++++ src/providers/ad/ad_gpo.h | 55 + src/providers/ad/ad_id.c | 3 +- src/providers/ad/ad_subdomains.c | 3 +- src/providers/ldap/sdap_async.c | 176 +++ src/providers/ldap/sdap_async.h | 14 + src/util/sss_ldap.h | 9 + 11 files changed, 2571 insertions(+), 51 deletions(-) create mode 100644 src/providers/ad/ad_gpo.c create mode 100644 src/providers/ad/ad_gpo.h
diff --git a/Makefile.am b/Makefile.am index f319624..2935d15 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1608,6 +1608,7 @@ ad_access_filter_tests_LDADD = \ libsss_ldap_common.la \ libsss_idmap.la \ libsss_krb5_common.la \
- libsss_ad.la \ libsss_test_common.la
I see
CCLD ad_access_filter_tests
*** Warning: Linking the executable ad_access_filter_tests against the loadable module *** libsss_ad.so is not portable!
when running make check, I think you have to replace libsss_ad.la by the list of source files here
I didn't understand this comment. Are you suggesting inserting ${libsss_ad_la_SOURCES} in the LDADD line? I don't think the linker deals with source files.
yes, I meant to add the list of source files to the _SOURCES variable of ad_access_filter_tests.
Also, since we have full control of libsss_ad.so, and since the unit tests will always be built together with the current version of libsss_ad.so, why does it matter that linking against libsss_ad.so is not portable. I thought that this warning would only be relevant if we were linking against a library that we had no control over.
I think this warning indicates that libtool cannot link the symbol of a shared library into a binary (the symbols are resolved at build time here and not at runtime which would be typical for a loadable module) on all platforms. Maybe this is even dependant on the libtool version and might fail on Linux platforms with older libtool?
As an alternative to add the source files you might want to create a libsss_ad_common.la similar to libsss_ldap_common.la or libsss_krb5_common.la?
...
- state->cindex++;
- if (state->clist[state->cindex] == NULL) {
DEBUG(SSSDBG_OP_FAILURE,
"Error retrieving access check result: %s\n",
sss_strerror(ret));
tevent_req_error(req, ret);
return;
/* If possible, retry with LDAP */
state->cindex++;
if (state->clist[state->cindex] == NULL) {
DEBUG(SSSDBG_OP_FAILURE,
"Error retrieving access check result: %s\n",
sss_strerror(ret));
tevent_req_error(req, ret);
return;
}
ret = ad_sdap_access_step(req, state->clist[state->cindex]);
if (ret != EOK) {
tevent_req_error(req, ret);
return;
}
/* Another check in progress */
You have to call return here because ad_sdap_access_step() will create a new request which will return to ad_sdap_access_done().
}
- ret = ad_access_step(req, state->clist[state->cindex]);
- if (ret != EOK) {
tevent_req_error(req, ret);
- subreq = ad_gpo_access_send(state,
state->be_ctx->ev,
state->domain,
state->ctx,
state->pd->user);
- if (!subreq) {
}tevent_req_error(req, ENOMEM); return;
- /* Another check in progress */
- tevent_req_set_callback(subreq, ad_gpo_access_done, req);
}
...
static void -ad_access_check_done(struct tevent_req *req) +ad_access_done(struct tevent_req *req) { errno_t ret; struct be_req *breq = tevent_req_callback_data(req, struct be_req); struct pam_data *pd = talloc_get_type(be_req_get_data(breq), struct pam_data);
Please do not remove this empty line, it separates declarations from the code. There is no formal requirement in the coding-style but I think it helps reading the code nonetheless.
Agreed.
I noticed that some other functions in this file also do not separate declarations from the code with an empty line. Should I fix those as well in this patch (even though those functions are not my code)? Or is that a separate patch? In general, is there a policy of limiting the next patch to only addressing code review comments (and not inserting additional fixes like the ones here).
There is no need to change code which you do not touch. If you find typos, coding style violations or other issues in the code you can send them in a new patch in a new email thread.
About addressing review comments, in general that changes are not send in a separate patch but squashed into the original patches. It's a bit more burden on the reviewer but I think patches especially for new features should stand on their own. Because it should be easy for a person who looks at the patch later to understand it. As with source code I think patches are more often read then reviewed.
Additionally I think it's not the task of the person who commits the patches to squash the changes together before committing the. With patch series it might not be obvious which change belongs to which patch, there might be conflicts which are hard for the committer to resolve because he does not know the code.
ret = ad_access_recv(req); talloc_zfree(req); switch (ret) {
...
- num_enabled = 0;
- ptr = raw_gplink_value;
- for (i = 0; i < gplink_count; i++) {
first = ptr + 1;
last = strchr(first, delim);
if (last == NULL) {
break;
}
*last = '\0';
last++;
dn = first;
if ( strncasecmp(dn, "LDAP://", 7)== 0 ) {
dn = dn + 7;
}
are you sure this can only be LDAP:// ? I wonder if it can be LDAPS:// is the original request is using LDAPS? But since AD does not activate LDAPS by default it might be safe.
The [MS-GPOL] spec indicates that the "LDAP://" prefix should be stripped from each GPO DN before being used in an LDAP search.
Ok, looks like we are safe then.
gplink_options = strchr(first, ';');
if (gplink_options == NULL) {
break;
}
*gplink_options = '\0';
gplink_options++;
gplink_number = atoi(gplink_options);
...
- enforced_gpo_dns[num_enforced] = NULL;
- unenforced_gpo_dns[num_unenforced] = NULL;
- candidate_gpos = talloc_array(tmp_ctx,
struct gp_gpo *,
num_candidate_gpos + 1);
- if (candidate_gpos == NULL) {
ret = ENOMEM;
goto done;
- }
- gpo_dn_idx = 0;
- for (i = num_unenforced - 1; i >= 0; i--) {
candidate_gpos[gpo_dn_idx] = talloc_zero(candidate_gpos, struct
gp_gpo);
if (candidate_gpos[gpo_dn_idx] == NULL) {
ret = ENOMEM;
goto done;
}
candidate_gpos[gpo_dn_idx]->gpo_dn =
talloc_strdup(candidate_gpos[gpo_dn_idx],
unenforced_gpo_dns[i]);
Is there a reason for adding the unenforced_gpo_dns in reverse order compared to enforced_gpo_dns?
The [MS-GPOL] spec indicates that unenforced gpo_dns must be *prepended*, while enforced_gpo_dns must be *appended*. I implemented prepending of unenforced_gpo_dns by adding them in reverse order.
Makes sense, thank you for the explanation.
bye, Sumit
----- Original Message -----
On Fri, Apr 11, 2014 at 06:32:05PM -0400, Yassir Elley wrote:
----- Original Message -----
On Thu, Apr 03, 2014 at 12:30:17PM -0400, Yassir Elley wrote:
----- Original Message -----
Hi,
I've read the patches and did some basic tests. You can find my review comments in-line below. I had to add some NULL-checks to make my tests work, see details below as well.
Thanks for the thorough review. Much appreciated!
In general I think you should add an option to disable the GPO checks to be able to resolve regressions in existing setups.
I'm not sure I understand this comment. What do you mean by "existing setups"? If you mean GPO-unaware setups, then the GPO code should not break any existing regressions. If it did, we would want to know about it, right?
There might be setup where the GPO would in theory apply to a Linux client running SSSD but since we didn't support it before access was always granted. If now the GPO is evaluated a user might be rejected although he was able to log in before. In this case it should be possible to disable the GPO check to return the previous behaviour without completely disabling access control.
For disabling the GPO checks, do you want me to add a disable option to the sssd config, or do you want me to add a disable-FEATURE flag to the configure script?
Thanks, Yassir.
On Mon, 2014-04-14 at 17:01 -0400, Yassir Elley wrote:
----- Original Message -----
On Fri, Apr 11, 2014 at 06:32:05PM -0400, Yassir Elley wrote:
----- Original Message -----
On Thu, Apr 03, 2014 at 12:30:17PM -0400, Yassir Elley wrote:
----- Original Message -----
Hi,
I've read the patches and did some basic tests. You can find my review comments in-line below. I had to add some NULL-checks to make my tests work, see details below as well.
Thanks for the thorough review. Much appreciated!
In general I think you should add an option to disable the GPO checks to be able to resolve regressions in existing setups.
I'm not sure I understand this comment. What do you mean by "existing setups"? If you mean GPO-unaware setups, then the GPO code should not break any existing regressions. If it did, we would want to know about it, right?
There might be setup where the GPO would in theory apply to a Linux client running SSSD but since we didn't support it before access was always granted. If now the GPO is evaluated a user might be rejected although he was able to log in before. In this case it should be possible to disable the GPO check to return the previous behaviour without completely disabling access control.
For disabling the GPO checks, do you want me to add a disable option to the sssd config, or do you want me to add a disable-FEATURE flag to the configure script?
shouldn;t we enable it with something like access_provider = gpo (which would make gpo checks disabled by default as the default access provider is 'permit')
Simo.
----- Original Message -----
On Mon, 2014-04-14 at 17:01 -0400, Yassir Elley wrote:
----- Original Message -----
On Fri, Apr 11, 2014 at 06:32:05PM -0400, Yassir Elley wrote:
----- Original Message -----
On Thu, Apr 03, 2014 at 12:30:17PM -0400, Yassir Elley wrote:
----- Original Message -----
Hi,
I've read the patches and did some basic tests. You can find my review comments in-line below. I had to add some NULL-checks to make my tests work, see details below as well.
Thanks for the thorough review. Much appreciated!
In general I think you should add an option to disable the GPO checks to be able to resolve regressions in existing setups.
I'm not sure I understand this comment. What do you mean by "existing setups"? If you mean GPO-unaware setups, then the GPO code should not break any existing regressions. If it did, we would want to know about it, right?
There might be setup where the GPO would in theory apply to a Linux client running SSSD but since we didn't support it before access was always granted. If now the GPO is evaluated a user might be rejected although he was able to log in before. In this case it should be possible to disable the GPO check to return the previous behaviour without completely disabling access control.
For disabling the GPO checks, do you want me to add a disable option to the sssd config, or do you want me to add a disable-FEATURE flag to the configure script?
shouldn;t we enable it with something like access_provider = gpo (which would make gpo checks disabled by default as the default access provider is 'permit')
Simo.
The idea has been to include GPO-based HBAC checks as one of the checks performed by the AD access provider. In other words, "access_provider = ad" would first check for AD account expiration, then the ad access filter, and then the GPO checks (stopping as soon as one of the conditions fails, of course). Asking an admin to specify "access_provider=gpo" would result in only the GPO-based checks being performed, which is probably not what is desired in AD environments (i.e. they want all of the checks performed).
If the Allow/Deny Logon Locally policy settings have *not* been configured in AD, then the default behavior is to permit access, which will therefore not result in a regression. However, because we are using *existing* AD policy settings, it is quite possible that an admin may have already configured GPO-based HBAC policy settings in AD (pre sssd-gpo implementation), which were not being enforced on Linux hosts (b/c there was no sssd-gpo implementation). For example, a GPO that denies Logon access to user "yelley" might already be linked to the domain (and therefore should be applicable to all hosts in the domain), but the Linux hosts in that domain would not be capable of enforcing it. With the arrival of the sssd-gpo implementation, the GPO linked to the domain will suddenly take effect and could deny user "yelley" access to Linux hosts that "yelley" previously had access to (resulting in an unexpected regression). Since the criteria for which policy settings should apply to which hosts is typically based on site/domain/ou (and not on operating system), it is unlikely that the admin would want different HBAC rules for Linux hosts and different HBAC rules for non-Linux hosts, although it is possible.
As I mentioned, this is only an issue b/c we are using existing AD policy settings. One solution would to use a new AD policy settings folder to hold Linux-specific settings, in which case there would be no ambiguity as to admin intent. Alternatively, I think we should enforce GPO-based HBAC by default for the AD Provider, since this is probably the common case, and since it provides seamless integration with AD GPO without requiring any sssd configuration changes. If desired, we can a "gpo_access_check = False" config option to allow admins to opt-out. An opt-in model is less desirable b/c it is a less likely use case and b/c "opting in" would require a configuration change on each Linux host in the domain.
Regards, Yassir.
On Mon, 2014-04-14 at 21:51 -0400, Yassir Elley wrote:
----- Original Message -----
On Mon, 2014-04-14 at 17:01 -0400, Yassir Elley wrote:
----- Original Message -----
On Fri, Apr 11, 2014 at 06:32:05PM -0400, Yassir Elley wrote:
----- Original Message -----
On Thu, Apr 03, 2014 at 12:30:17PM -0400, Yassir Elley wrote: > > > ----- Original Message -----
Hi,
I've read the patches and did some basic tests. You can find my review comments in-line below. I had to add some NULL-checks to make my tests work, see details below as well.
Thanks for the thorough review. Much appreciated!
In general I think you should add an option to disable the GPO checks to be able to resolve regressions in existing setups.
I'm not sure I understand this comment. What do you mean by "existing setups"? If you mean GPO-unaware setups, then the GPO code should not break any existing regressions. If it did, we would want to know about it, right?
There might be setup where the GPO would in theory apply to a Linux client running SSSD but since we didn't support it before access was always granted. If now the GPO is evaluated a user might be rejected although he was able to log in before. In this case it should be possible to disable the GPO check to return the previous behaviour without completely disabling access control.
For disabling the GPO checks, do you want me to add a disable option to the sssd config, or do you want me to add a disable-FEATURE flag to the configure script?
shouldn;t we enable it with something like access_provider = gpo (which would make gpo checks disabled by default as the default access provider is 'permit')
Simo.
The idea has been to include GPO-based HBAC checks as one of the checks performed by the AD access provider. In other words, "access_provider = ad" would first check for AD account expiration, then the ad access filter, and then the GPO checks (stopping as soon as one of the conditions fails, of course). Asking an admin to specify "access_provider=gpo" would result in only the GPO-based checks being performed, which is probably not what is desired in AD environments (i.e. they want all of the checks performed).
TBH I do not think account expiration has anything to do with access checks, and should be done unconditionally, regardless of what the access_provider is, it has more to do with the auth_provider than anything else.
If the Allow/Deny Logon Locally policy settings have *not* been configured in AD, then the default behavior is to permit access, which will therefore not result in a regression. However, because we are using *existing* AD policy settings, it is quite possible that an admin may have already configured GPO-based HBAC policy settings in AD (pre sssd-gpo implementation), which were not being enforced on Linux hosts (b/c there was no sssd-gpo implementation).
Understood, but not a problem, the default provider is 'permit' at the moment.
For example, a GPO that denies Logon access to user "yelley" might already be linked to the domain (and therefore should be applicable to all hosts in the domain), but the Linux hosts in that domain would not be capable of enforcing it. With the arrival of the sssd-gpo implementation, the GPO linked to the domain will suddenly take effect and could deny user "yelley" access to Linux hosts that "yelley" previously had access to (resulting in an unexpected regression).
It is not a regression, it is an explicit access control check the admins imposed, it is quite correct to respect those policies. The bug is that we are not controlling them now, and the solution is simple, the admin changes the policy (or disables GPO checks).
Since the criteria for which policy settings should apply to which hosts is typically based on site/domain/ou (and not on operating system), it is unlikely that the admin would want different HBAC rules for Linux hosts and different HBAC rules for non-Linux hosts, although it is possible.
And they can do that by moving Linux machines to a different OU, let's not try to work around what domain policies dictate.
As I mentioned, this is only an issue b/c we are using existing AD policy settings. One solution would to use a new AD policy settings folder to hold Linux-specific settings, in which case there would be no ambiguity as to admin intent. Alternatively, I think we should enforce GPO-based HBAC by default for the AD Provider, since this is probably the common case, and since it provides seamless integration with AD GPO without requiring any sssd configuration changes. If desired, we can a "gpo_access_check = False" config option to allow admins to opt-out. An opt-in model is less desirable b/c it is a less likely use case and b/c "opting in" would require a configuration change on each Linux host in the domain.
If you want to disable GPO checks you should set access_provider = permit, if you want them the access_provider = gpo, that's how the system architecture would make you do it if you follow current SSSD design and I do not see a reason to deviate.
Simo.
On Mon, Apr 14, 2014 at 11:51:30PM -0400, Simo Sorce wrote:
On Mon, 2014-04-14 at 21:51 -0400, Yassir Elley wrote:
----- Original Message -----
On Mon, 2014-04-14 at 17:01 -0400, Yassir Elley wrote:
----- Original Message -----
On Fri, Apr 11, 2014 at 06:32:05PM -0400, Yassir Elley wrote:
----- Original Message ----- > On Thu, Apr 03, 2014 at 12:30:17PM -0400, Yassir Elley wrote: > > > > > > ----- Original Message ----- > > Hi, > > I've read the patches and did some basic tests. You can find my > review > comments in-line below. I had to add some NULL-checks to make my > tests > work, see details below as well.
Thanks for the thorough review. Much appreciated!
> > In general I think you should add an option to disable the GPO checks > to > be able to resolve regressions in existing setups.
I'm not sure I understand this comment. What do you mean by "existing setups"? If you mean GPO-unaware setups, then the GPO code should not break any existing regressions. If it did, we would want to know about it, right?
There might be setup where the GPO would in theory apply to a Linux client running SSSD but since we didn't support it before access was always granted. If now the GPO is evaluated a user might be rejected although he was able to log in before. In this case it should be possible to disable the GPO check to return the previous behaviour without completely disabling access control.
For disabling the GPO checks, do you want me to add a disable option to the sssd config, or do you want me to add a disable-FEATURE flag to the configure script?
Yassir, I was thinking of an option for sssd.conf.
A configure option might only be needed if it turns out that the GPO checks have some build dependencies which are not available on older platforms where we still want to offer basic support.
shouldn;t we enable it with something like access_provider = gpo (which would make gpo checks disabled by default as the default access provider is 'permit')
Simo.
The idea has been to include GPO-based HBAC checks as one of the checks performed by the AD access provider. In other words, "access_provider = ad" would first check for AD account expiration, then the ad access filter, and then the GPO checks (stopping as soon as one of the conditions fails, of course). Asking an admin to specify "access_provider=gpo" would result in only the GPO-based checks being performed, which is probably not what is desired in AD environments (i.e. they want all of the checks performed).
TBH I do not think account expiration has anything to do with access checks, and should be done unconditionally, regardless of what the access_provider is, it has more to do with the auth_provider than anything else.
I totally agree that it belongs to the auth_provider but since the auth_provider is bypassed by SSO schemes like GSSAPI or SSH keys it has to be done by the access_provider. With password authentication is is expected that the authentication service will not authenticate expired users.
If the Allow/Deny Logon Locally policy settings have *not* been configured in AD, then the default behavior is to permit access, which will therefore not result in a regression. However, because we are using *existing* AD policy settings, it is quite possible that an admin may have already configured GPO-based HBAC policy settings in AD (pre sssd-gpo implementation), which were not being enforced on Linux hosts (b/c there was no sssd-gpo implementation).
Understood, but not a problem, the default provider is 'permit' at the moment.
For example, a GPO that denies Logon access to user "yelley" might already be linked to the domain (and therefore should be applicable to all hosts in the domain), but the Linux hosts in that domain would not be capable of enforcing it. With the arrival of the sssd-gpo implementation, the GPO linked to the domain will suddenly take effect and could deny user "yelley" access to Linux hosts that "yelley" previously had access to (resulting in an unexpected regression).
It is not a regression, it is an explicit access control check the admins imposed, it is quite correct to respect those policies. The bug is that we are not controlling them now, and the solution is simple, the admin changes the policy (or disables GPO checks).
Since the criteria for which policy settings should apply to which hosts is typically based on site/domain/ou (and not on operating system), it is unlikely that the admin would want different HBAC rules for Linux hosts and different HBAC rules for non-Linux hosts, although it is possible.
And they can do that by moving Linux machines to a different OU, let's not try to work around what domain policies dictate.
As I mentioned, this is only an issue b/c we are using existing AD policy settings. One solution would to use a new AD policy settings folder to hold Linux-specific settings, in which case there would be no ambiguity as to admin intent. Alternatively, I think we should enforce GPO-based HBAC by default for the AD Provider, since this is probably the common case, and since it provides seamless integration with AD GPO without requiring any sssd configuration changes. If desired, we can a "gpo_access_check = False" config option to allow admins to opt-out. An opt-in model is less desirable b/c it is a less likely use case and b/c "opting in" would require a configuration change on each Linux host in the domain.
If you want to disable GPO checks you should set access_provider = permit, if you want them the access_provider = gpo, that's how the system architecture would make you do it if you follow current SSSD design and I do not see a reason to deviate.
There are various options to configure the ldap access provider. I wonder why there shouldn't be a way to configure the behaviour of the AD access provider? I think for the time being the GPO work belongs to the AD access provider. This will change when the access providers become stackable (https://fedorahosted.org/sssd/ticket/1326) but this is scheduled for 1.15 and with the current workload I don't expect to see this for the next two years.
bye, Sumit
Simo.
-- Simo Sorce * Red Hat, Inc * New York
sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
On Mon, Apr 14, 2014 at 11:51:30PM -0400, Simo Sorce wrote:
For example, a GPO that denies Logon access to user "yelley" might already be linked to the domain (and therefore should be applicable to all hosts in the domain), but the Linux hosts in that domain would not be capable of enforcing it. With the arrival of the sssd-gpo implementation, the GPO linked to the domain will suddenly take effect and could deny user "yelley" access to Linux hosts that "yelley" previously had access to (resulting in an unexpected regression).
It is not a regression, it is an explicit access control check the admins imposed, it is quite correct to respect those policies. The bug is that we are not controlling them now, and the solution is simple, the admin changes the policy (or disables GPO checks).
The problem is that we can lock out users completely by fixing the bug. In principle, I agree with you, I just think there should be a transition period where we warn loudly that with the next version account X wouldn't be allowed to log in, go fix your access control scheme.
On Tue, 2014-04-15 at 10:16 +0200, Jakub Hrozek wrote:
On Mon, Apr 14, 2014 at 11:51:30PM -0400, Simo Sorce wrote:
For example, a GPO that denies Logon access to user "yelley" might already be linked to the domain (and therefore should be applicable to all hosts in the domain), but the Linux hosts in that domain would not be capable of enforcing it. With the arrival of the sssd-gpo implementation, the GPO linked to the domain will suddenly take effect and could deny user "yelley" access to Linux hosts that "yelley" previously had access to (resulting in an unexpected regression).
It is not a regression, it is an explicit access control check the admins imposed, it is quite correct to respect those policies. The bug is that we are not controlling them now, and the solution is simple, the admin changes the policy (or disables GPO checks).
The problem is that we can lock out users completely by fixing the bug. In principle, I agree with you, I just think there should be a transition period where we warn loudly that with the next version account X wouldn't be allowed to log in, go fix your access control scheme.
Ok so what you are proposing essentially is to have a gpo_enforcing yes/no option
with yes we block the user with no we do not but loudly complain it will be blocked
we start with a default of gpo_enforcing = no, and later we change the default to yes.
Simo.
On Tue, Apr 15, 2014 at 10:03:35AM -0400, Simo Sorce wrote:
On Tue, 2014-04-15 at 10:16 +0200, Jakub Hrozek wrote:
On Mon, Apr 14, 2014 at 11:51:30PM -0400, Simo Sorce wrote:
For example, a GPO that denies Logon access to user "yelley" might already be linked to the domain (and therefore should be applicable to all hosts in the domain), but the Linux hosts in that domain would not be capable of enforcing it. With the arrival of the sssd-gpo implementation, the GPO linked to the domain will suddenly take effect and could deny user "yelley" access to Linux hosts that "yelley" previously had access to (resulting in an unexpected regression).
It is not a regression, it is an explicit access control check the admins imposed, it is quite correct to respect those policies. The bug is that we are not controlling them now, and the solution is simple, the admin changes the policy (or disables GPO checks).
The problem is that we can lock out users completely by fixing the bug. In principle, I agree with you, I just think there should be a transition period where we warn loudly that with the next version account X wouldn't be allowed to log in, go fix your access control scheme.
Ok so what you are proposing essentially is to have a gpo_enforcing yes/no option
with yes we block the user with no we do not but loudly complain it will be blocked
we start with a default of gpo_enforcing = no, and later we change the default to yes.
Yes, pretty much.
I just wonder if it would make sense to follow exactly the model LDAP provider has and have an 'order' option that would default to 'expire, filter' currently and later when we enable GPOs would turn into 'expire, gpo, filter'. But that's probably too much complexity.
----- Original Message -----
On Tue, Apr 15, 2014 at 10:03:35AM -0400, Simo Sorce wrote:
On Tue, 2014-04-15 at 10:16 +0200, Jakub Hrozek wrote:
On Mon, Apr 14, 2014 at 11:51:30PM -0400, Simo Sorce wrote:
For example, a GPO that denies Logon access to user "yelley" might already be linked to the domain (and therefore should be applicable to all hosts in the domain), but the Linux hosts in that domain would not be capable of enforcing it. With the arrival of the sssd-gpo implementation, the GPO linked to the domain will suddenly take effect and could deny user "yelley" access to Linux hosts that "yelley" previously had access to (resulting in an unexpected regression).
It is not a regression, it is an explicit access control check the admins imposed, it is quite correct to respect those policies. The bug is that we are not controlling them now, and the solution is simple, the admin changes the policy (or disables GPO checks).
The problem is that we can lock out users completely by fixing the bug. In principle, I agree with you, I just think there should be a transition period where we warn loudly that with the next version account X wouldn't be allowed to log in, go fix your access control scheme.
Ok so what you are proposing essentially is to have a gpo_enforcing yes/no option
with yes we block the user with no we do not but loudly complain it will be blocked
we start with a default of gpo_enforcing = no, and later we change the default to yes.
Yes, pretty much.
I just wonder if it would make sense to follow exactly the model LDAP provider has and have an 'order' option that would default to 'expire, filter' currently and later when we enable GPOs would turn into 'expire, gpo, filter'. But that's probably too much complexity. _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
This seems like a reasonable compromise, although it may delay deployment until the version in which gpo_hbac is enforced by default. The alternative, of course, is to enable it by default and have the "loud complaining" be denied access!! :) Just kidding.
I have a few comments/questions about the proposal:
* When we are talking about setting the default to "no", and changing the default to "yes" in the next version, what do we mean by "next version"? Are we talking sssd version (1.11.x=no, 1.12.x=yes), fedora version (f21=no, f22=yes), rhel version (rhel 7.1=no, rhel 7.2=yes), something else?
* Since one of the goals of having an AD provider is to simplify configuration for AD environments (as compared to the LDAP provider), I think that using the 'order' option might undermine that goal (by introducing complexity).
* We should agree upon a name for the options field. How about "ad_enforce_gpo_hbac"= {True, False}?
* When we talk about "loudly complaining", is this simply a log message with high priority? If so, how high should it be (FATAL_FAILURE? CRIT_FAILURE? OP_FAILURE?)
Regards, Yassir.
On Tue, Apr 15, 2014 at 01:13:32PM -0400, Yassir Elley wrote:
----- Original Message -----
On Tue, Apr 15, 2014 at 10:03:35AM -0400, Simo Sorce wrote:
On Tue, 2014-04-15 at 10:16 +0200, Jakub Hrozek wrote:
On Mon, Apr 14, 2014 at 11:51:30PM -0400, Simo Sorce wrote:
For example, a GPO that denies Logon access to user "yelley" might already be linked to the domain (and therefore should be applicable to all hosts in the domain), but the Linux hosts in that domain would not be capable of enforcing it. With the arrival of the sssd-gpo implementation, the GPO linked to the domain will suddenly take effect and could deny user "yelley" access to Linux hosts that "yelley" previously had access to (resulting in an unexpected regression).
It is not a regression, it is an explicit access control check the admins imposed, it is quite correct to respect those policies. The bug is that we are not controlling them now, and the solution is simple, the admin changes the policy (or disables GPO checks).
The problem is that we can lock out users completely by fixing the bug. In principle, I agree with you, I just think there should be a transition period where we warn loudly that with the next version account X wouldn't be allowed to log in, go fix your access control scheme.
Ok so what you are proposing essentially is to have a gpo_enforcing yes/no option
with yes we block the user with no we do not but loudly complain it will be blocked
we start with a default of gpo_enforcing = no, and later we change the default to yes.
Yes, pretty much.
I just wonder if it would make sense to follow exactly the model LDAP provider has and have an 'order' option that would default to 'expire, filter' currently and later when we enable GPOs would turn into 'expire, gpo, filter'. But that's probably too much complexity. _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
This seems like a reasonable compromise, although it may delay deployment until the version in which gpo_hbac is enforced by default. The alternative, of course, is to enable it by default and have the "loud complaining" be denied access!! :) Just kidding.
You're right, although a x.1 version usually follows x.0 in no more than a month.
Alternatively, we could enable this warning for pre-release versions and make the switch to GPO enforcing in x.0, but I'm not sure how many users actually run the pre-releases (short of Rawhide users..)
I have a few comments/questions about the proposal:
- When we are talking about setting the default to "no", and changing the
default to "yes" in the next version, what do we mean by "next version"? Are we talking sssd version (1.11.x=no, 1.12.x=yes), fedora version (f21=no, f22=yes), rhel version (rhel 7.1=no, rhel 7.2=yes), something else?
I was thinking 1.12.0 would be 'no' and 1.12.1 would be 'yes'. 1.12.0 should be what F-21 ships with and 1.12.1 would be delivered as an update to F-21. I'm not sure about RHEL 7.1, but I suppose we'd have 1.12.1 or 1.12.2 in 7.1 (and I'm not making any prediction on RHEL the product now. I'm just a random dude who happens to have a .redhat.com address).
- Since one of the goals of having an AD provider is to simplify
configuration for AD environments (as compared to the LDAP provider), I think that using the 'order' option might undermine that goal (by introducing complexity).
I agree. The order was not a good idea, a boolean is much simpler to understand. If the ad_acess_filter was defined, it should have been used without any additional 'order'..
- We should agree upon a name for the options field. How about "ad_enforce_gpo_hbac"= {True, False}?
Is HBAC a term that would be easily understood by AD admins? I understand it, but I know what it means from working on IPA..
What about ad_access_enforce_gpo?
- When we talk about "loudly complaining", is this simply a
log message with high priority? If so, how high should it be (FATAL_FAILURE? CRIT_FAILURE? OP_FAILURE?)
I was thinking FATAL_FAILURE. I think a syslog message is probably an overkill.
----- Original Message -----
On Tue, Apr 15, 2014 at 01:13:32PM -0400, Yassir Elley wrote:
----- Original Message -----
On Tue, Apr 15, 2014 at 10:03:35AM -0400, Simo Sorce wrote:
On Tue, 2014-04-15 at 10:16 +0200, Jakub Hrozek wrote:
On Mon, Apr 14, 2014 at 11:51:30PM -0400, Simo Sorce wrote:
> For example, a GPO that denies Logon access to user "yelley" > might > already be linked to the domain (and therefore should be > applicable > to > all hosts in the domain), but the Linux hosts in that domain > would > not > be capable of enforcing it. With the arrival of the sssd-gpo > implementation, the GPO linked to the domain will suddenly take > effect > and could deny user "yelley" access to Linux hosts that "yelley" > previously had access to (resulting in an unexpected regression).
It is not a regression, it is an explicit access control check the admins imposed, it is quite correct to respect those policies. The bug is that we are not controlling them now, and the solution is simple, the admin changes the policy (or disables GPO checks).
The problem is that we can lock out users completely by fixing the bug. In principle, I agree with you, I just think there should be a transition period where we warn loudly that with the next version account X wouldn't be allowed to log in, go fix your access control scheme.
Ok so what you are proposing essentially is to have a gpo_enforcing yes/no option
with yes we block the user with no we do not but loudly complain it will be blocked
we start with a default of gpo_enforcing = no, and later we change the default to yes.
Yes, pretty much.
I just wonder if it would make sense to follow exactly the model LDAP provider has and have an 'order' option that would default to 'expire, filter' currently and later when we enable GPOs would turn into 'expire, gpo, filter'. But that's probably too much complexity. _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
This seems like a reasonable compromise, although it may delay deployment until the version in which gpo_hbac is enforced by default. The alternative, of course, is to enable it by default and have the "loud complaining" be denied access!! :) Just kidding.
You're right, although a x.1 version usually follows x.0 in no more than a month.
Alternatively, we could enable this warning for pre-release versions and make the switch to GPO enforcing in x.0, but I'm not sure how many users actually run the pre-releases (short of Rawhide users..)
I have a few comments/questions about the proposal:
- When we are talking about setting the default to "no", and changing the
default to "yes" in the next version, what do we mean by "next version"? Are we talking sssd version (1.11.x=no, 1.12.x=yes), fedora version (f21=no, f22=yes), rhel version (rhel 7.1=no, rhel 7.2=yes), something else?
I was thinking 1.12.0 would be 'no' and 1.12.1 would be 'yes'. 1.12.0 should be what F-21 ships with and 1.12.1 would be delivered as an update to F-21. I'm not sure about RHEL 7.1, but I suppose we'd have 1.12.1 or 1.12.2 in 7.1 (and I'm not making any prediction on RHEL the product now. I'm just a random dude who happens to have a .redhat.com address).
You have a better sense of the customer base, so I will defer to whatever you (and the list) decide (i.e. whether disabling default gpo only for the initial minor release, disabling it for pre-release, etc). Regardless of that decision, I think there is agreement that the initial default should be "no".
- Since one of the goals of having an AD provider is to simplify
configuration for AD environments (as compared to the LDAP provider), I think that using the 'order' option might undermine that goal (by introducing complexity).
I agree. The order was not a good idea, a boolean is much simpler to understand. If the ad_acess_filter was defined, it should have been used without any additional 'order'..
- We should agree upon a name for the options field. How about
"ad_enforce_gpo_hbac"= {True, False}?
Is HBAC a term that would be easily understood by AD admins? I understand it, but I know what it means from working on IPA..
What about ad_access_enforce_gpo?
That sounds good to me!
- When we talk about "loudly complaining", is this simply a
log message with high priority? If so, how high should it be (FATAL_FAILURE? CRIT_FAILURE? OP_FAILURE?)
I was thinking FATAL_FAILURE. I think a syslog message is probably an overkill.
You first say that we should output a syslog message with FATAL_FAILURE, but then you say that a syslog message is probably an overkill! Can you clarify?
Regards, Yassir.
On Tue, Apr 15, 2014 at 02:49:36PM -0400, Yassir Elley wrote:
- When we talk about "loudly complaining", is this simply a
log message with high priority? If so, how high should it be (FATAL_FAILURE? CRIT_FAILURE? OP_FAILURE?)
I was thinking FATAL_FAILURE. I think a syslog message is probably an overkill.
You first say that we should output a syslog message with FATAL_FAILURE, but then you say that a syslog message is probably an overkill! Can you clarify?
Regards, Yassir.
I was considering doing both:
DEBUG(SSSDBG_FATAL_FAILURE, "User was denied access by GPO!\n"); sss_log(SSS_LOG_ERR, "User was denied access by GPO!\n");
But I'm not sure if this would be acceptable or whether the sss_log call would pollute the syslog/journal too much..
On Tue, 2014-04-15 at 20:59 +0200, Jakub Hrozek wrote:
On Tue, Apr 15, 2014 at 02:49:36PM -0400, Yassir Elley wrote:
- When we talk about "loudly complaining", is this simply a
log message with high priority? If so, how high should it be (FATAL_FAILURE? CRIT_FAILURE? OP_FAILURE?)
I was thinking FATAL_FAILURE. I think a syslog message is probably an overkill.
You first say that we should output a syslog message with FATAL_FAILURE, but then you say that a syslog message is probably an overkill! Can you clarify?
Regards, Yassir.
I was considering doing both:
DEBUG(SSSDBG_FATAL_FAILURE, "User was denied access by GPO!\n"); sss_log(SSS_LOG_ERR, "User was denied access by GPO!\n");
But I'm not sure if this would be acceptable or whether the sss_log call would pollute the syslog/journal too much..
No it is not too much, it happens only when a user is denied, just like pam_unix and other modules do. Simo.
On Tue, 2014-04-15 at 20:25 +0200, Jakub Hrozek wrote:
On Tue, Apr 15, 2014 at 01:13:32PM -0400, Yassir Elley wrote:
----- Original Message -----
On Tue, Apr 15, 2014 at 10:03:35AM -0400, Simo Sorce wrote:
On Tue, 2014-04-15 at 10:16 +0200, Jakub Hrozek wrote:
On Mon, Apr 14, 2014 at 11:51:30PM -0400, Simo Sorce wrote:
> For example, a GPO that denies Logon access to user "yelley" might > already be linked to the domain (and therefore should be applicable > to > all hosts in the domain), but the Linux hosts in that domain would > not > be capable of enforcing it. With the arrival of the sssd-gpo > implementation, the GPO linked to the domain will suddenly take > effect > and could deny user "yelley" access to Linux hosts that "yelley" > previously had access to (resulting in an unexpected regression).
It is not a regression, it is an explicit access control check the admins imposed, it is quite correct to respect those policies. The bug is that we are not controlling them now, and the solution is simple, the admin changes the policy (or disables GPO checks).
The problem is that we can lock out users completely by fixing the bug. In principle, I agree with you, I just think there should be a transition period where we warn loudly that with the next version account X wouldn't be allowed to log in, go fix your access control scheme.
Ok so what you are proposing essentially is to have a gpo_enforcing yes/no option
with yes we block the user with no we do not but loudly complain it will be blocked
we start with a default of gpo_enforcing = no, and later we change the default to yes.
Yes, pretty much.
I just wonder if it would make sense to follow exactly the model LDAP provider has and have an 'order' option that would default to 'expire, filter' currently and later when we enable GPOs would turn into 'expire, gpo, filter'. But that's probably too much complexity. _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
This seems like a reasonable compromise, although it may delay deployment until the version in which gpo_hbac is enforced by default. The alternative, of course, is to enable it by default and have the "loud complaining" be denied access!! :) Just kidding.
You're right, although a x.1 version usually follows x.0 in no more than a month.
Alternatively, we could enable this warning for pre-release versions and make the switch to GPO enforcing in x.0, but I'm not sure how many users actually run the pre-releases (short of Rawhide users..)
I have a few comments/questions about the proposal:
- When we are talking about setting the default to "no", and changing the
default to "yes" in the next version, what do we mean by "next version"? Are we talking sssd version (1.11.x=no, 1.12.x=yes), fedora version (f21=no, f22=yes), rhel version (rhel 7.1=no, rhel 7.2=yes), something else?
I was thinking 1.12.0 would be 'no' and 1.12.1 would be 'yes'. 1.12.0 should be what F-21 ships with and 1.12.1 would be delivered as an update to F-21. I'm not sure about RHEL 7.1, but I suppose we'd have 1.12.1 or 1.12.2 in 7.1 (and I'm not making any prediction on RHEL the product now. I'm just a random dude who happens to have a .redhat.com address).
This would change behavior midrelease, it is not nice, I would say 1.12.0 ships in permissive mode by default, and 1.13.0 switches to enforcing by default.
Interested users can always manually switch to enforcing on their own, there is no need to rush this onto them.
- Since one of the goals of having an AD provider is to simplify
configuration for AD environments (as compared to the LDAP provider), I think that using the 'order' option might undermine that goal (by introducing complexity).
I agree. The order was not a good idea, a boolean is much simpler to understand. If the ad_acess_filter was defined, it should have been used without any additional 'order'..
- We should agree upon a name for the options field. How about "ad_enforce_gpo_hbac"= {True, False}?
Is HBAC a term that would be easily understood by AD admins? I understand it, but I know what it means from working on IPA..
What about ad_access_enforce_gpo?
why the ad_ prefix ?
gpo_access_control = disabled|permissive|enforcing
- When we talk about "loudly complaining", is this simply a
log message with high priority? If so, how high should it be (FATAL_FAILURE? CRIT_FAILURE? OP_FAILURE?)
I was thinking FATAL_FAILURE. I think a syslog message is probably an overkill.
It is not a failure, let alone fatal. It should be something that gets logged in the security log however, so whatever ends up there by default is fine.
This is auditing, we shouldn't use *debug* messages for this.
Simo.
On Tue, Apr 15, 2014 at 03:03:55PM -0400, Simo Sorce wrote:
I was thinking 1.12.0 would be 'no' and 1.12.1 would be 'yes'. 1.12.0 should be what F-21 ships with and 1.12.1 would be delivered as an update to F-21. I'm not sure about RHEL 7.1, but I suppose we'd have 1.12.1 or 1.12.2 in 7.1 (and I'm not making any prediction on RHEL the product now. I'm just a random dude who happens to have a .redhat.com address).
This would change behavior midrelease, it is not nice, I would say 1.12.0 ships in permissive mode by default, and 1.13.0 switches to enforcing by default.
I realize changing a default in a minor release is not great, but 1.13 will be at least 6 months from 1.12, realistically a year later..what is the point of a cool feature like GPO if nobody will use it?
Interested users can always manually switch to enforcing on their own, there is no need to rush this onto them.
I think most users will use whatever realmd sets up..I'm not sure, but is it still the simple access provider? Should we consider changing the default to 'ad' when we switch the GPO feature to 'enforcing' ?
- Since one of the goals of having an AD provider is to simplify
configuration for AD environments (as compared to the LDAP provider), I think that using the 'order' option might undermine that goal (by introducing complexity).
I agree. The order was not a good idea, a boolean is much simpler to understand. If the ad_acess_filter was defined, it should have been used without any additional 'order'..
- We should agree upon a name for the options field. How about "ad_enforce_gpo_hbac"= {True, False}?
Is HBAC a term that would be easily understood by AD admins? I understand it, but I know what it means from working on IPA..
What about ad_access_enforce_gpo?
why the ad_ prefix ?
gpo_access_control = disabled|permissive|enforcing
This would sound like GPO is supported (or planned to be supported) by all backends. Compare entry_cache_timeout with ldap_search_timeout.
- When we talk about "loudly complaining", is this simply a
log message with high priority? If so, how high should it be (FATAL_FAILURE? CRIT_FAILURE? OP_FAILURE?)
I was thinking FATAL_FAILURE. I think a syslog message is probably an overkill.
It is not a failure, let alone fatal. It should be something that gets logged in the security log however, so whatever ends up there by default is fine.
This is auditing, we shouldn't use *debug* messages for this.
Fine, whatever is better visible to the admin. I was just trying to think as the poor locked out admin "Oh sssd denied access? tail /var/log/sssd/*.log".
On Tue, 2014-04-15 at 21:23 +0200, Jakub Hrozek wrote:
On Tue, Apr 15, 2014 at 03:03:55PM -0400, Simo Sorce wrote:
I was thinking 1.12.0 would be 'no' and 1.12.1 would be 'yes'. 1.12.0 should be what F-21 ships with and 1.12.1 would be delivered as an update to F-21. I'm not sure about RHEL 7.1, but I suppose we'd have 1.12.1 or 1.12.2 in 7.1 (and I'm not making any prediction on RHEL the product now. I'm just a random dude who happens to have a .redhat.com address).
This would change behavior midrelease, it is not nice, I would say 1.12.0 ships in permissive mode by default, and 1.13.0 switches to enforcing by default.
I realize changing a default in a minor release is not great, but 1.13 will be at least 6 months from 1.12, realistically a year later..what is the point of a cool feature like GPO if nobody will use it?
Interested users can always manually switch to enforcing on their own, there is no need to rush this onto them.
I think most users will use whatever realmd sets up..I'm not sure, but is it still the simple access provider? Should we consider changing the default to 'ad' when we switch the GPO feature to 'enforcing' ?
If it is cool people will use it, also nobody will prohib Fedora/other distro's to change the distro default.
So if 1.13.0 is not out yet when F22 ship, F22 can still just turn it on.
- Since one of the goals of having an AD provider is to simplify
configuration for AD environments (as compared to the LDAP provider), I think that using the 'order' option might undermine that goal (by introducing complexity).
I agree. The order was not a good idea, a boolean is much simpler to understand. If the ad_acess_filter was defined, it should have been used without any additional 'order'..
- We should agree upon a name for the options field. How about "ad_enforce_gpo_hbac"= {True, False}?
Is HBAC a term that would be easily understood by AD admins? I understand it, but I know what it means from working on IPA..
What about ad_access_enforce_gpo?
why the ad_ prefix ?
gpo_access_control = disabled|permissive|enforcing
This would sound like GPO is supported (or planned to be supported) by all backends. Compare entry_cache_timeout with ldap_search_timeout.
uhmm ok ad_gpo_access_control is ok as well, I think it is quite clear that GPO is an AD specific technology, but I may be biased from years of mental association :-)
- When we talk about "loudly complaining", is this simply a
log message with high priority? If so, how high should it be (FATAL_FAILURE? CRIT_FAILURE? OP_FAILURE?)
I was thinking FATAL_FAILURE. I think a syslog message is probably an overkill.
It is not a failure, let alone fatal. It should be something that gets logged in the security log however, so whatever ends up there by default is fine.
This is auditing, we shouldn't use *debug* messages for this.
Fine, whatever is better visible to the admin. I was just trying to think as the poor locked out admin "Oh sssd denied access? tail /var/log/sssd/*.log".
Nothing prevent us from also emitting a debug message I guess, but normally sssd debug logs are not enabled, and the system log is a better place for these kind of messages.
Simo.
----- Original Message -----
On Tue, 2014-04-15 at 21:23 +0200, Jakub Hrozek wrote:
On Tue, Apr 15, 2014 at 03:03:55PM -0400, Simo Sorce wrote:
I was thinking 1.12.0 would be 'no' and 1.12.1 would be 'yes'. 1.12.0 should be what F-21 ships with and 1.12.1 would be delivered as an update to F-21. I'm not sure about RHEL 7.1, but I suppose we'd have 1.12.1 or 1.12.2 in 7.1 (and I'm not making any prediction on RHEL the product now. I'm just a random dude who happens to have a .redhat.com address).
This would change behavior midrelease, it is not nice, I would say 1.12.0 ships in permissive mode by default, and 1.13.0 switches to enforcing by default.
I realize changing a default in a minor release is not great, but 1.13 will be at least 6 months from 1.12, realistically a year later..what is the point of a cool feature like GPO if nobody will use it?
Interested users can always manually switch to enforcing on their own, there is no need to rush this onto them.
I think most users will use whatever realmd sets up..I'm not sure, but is it still the simple access provider? Should we consider changing the default to 'ad' when we switch the GPO feature to 'enforcing' ?
If it is cool people will use it, also nobody will prohib Fedora/other distro's to change the distro default.
So if 1.13.0 is not out yet when F22 ship, F22 can still just turn it on.
- Since one of the goals of having an AD provider is to simplify
configuration for AD environments (as compared to the LDAP provider), I think that using the 'order' option might undermine that goal (by introducing complexity).
I agree. The order was not a good idea, a boolean is much simpler to understand. If the ad_acess_filter was defined, it should have been used without any additional 'order'..
- We should agree upon a name for the options field. How about
"ad_enforce_gpo_hbac"= {True, False}?
Is HBAC a term that would be easily understood by AD admins? I understand it, but I know what it means from working on IPA..
What about ad_access_enforce_gpo?
why the ad_ prefix ?
gpo_access_control = disabled|permissive|enforcing
This would sound like GPO is supported (or planned to be supported) by all backends. Compare entry_cache_timeout with ldap_search_timeout.
uhmm ok ad_gpo_access_control is ok as well, I think it is quite clear that GPO is an AD specific technology, but I may be biased from years of mental association :-)
- When we talk about "loudly complaining", is this simply a
log message with high priority? If so, how high should it be (FATAL_FAILURE? CRIT_FAILURE? OP_FAILURE?)
I was thinking FATAL_FAILURE. I think a syslog message is probably an overkill.
It is not a failure, let alone fatal. It should be something that gets logged in the security log however, so whatever ends up there by default is fine.
This is auditing, we shouldn't use *debug* messages for this.
Fine, whatever is better visible to the admin. I was just trying to think as the poor locked out admin "Oh sssd denied access? tail /var/log/sssd/*.log".
Nothing prevent us from also emitting a debug message I guess, but normally sssd debug logs are not enabled, and the system log is a better place for these kind of messages.
Simo.
--
Hi Sumit,
As you know, the gpo-ldap patch you reviewed does not actually perform an access control check (it only determines which GPOs are applicable to the policy target). Enforcing the access control requires retrieval of files via SMB, which will be in a subsequent gpo-smb patch. As such, I am thinking that it would make more sense to postpone implementation of this config option until the gpo-smb patch, at which time the config option can actually be tested. Do you agree?
Thanks, Yassir.
On Tue, Apr 15, 2014 at 07:05:10PM -0400, Yassir Elley wrote:
----- Original Message -----
On Tue, 2014-04-15 at 21:23 +0200, Jakub Hrozek wrote:
On Tue, Apr 15, 2014 at 03:03:55PM -0400, Simo Sorce wrote:
I was thinking 1.12.0 would be 'no' and 1.12.1 would be 'yes'. 1.12.0 should be what F-21 ships with and 1.12.1 would be delivered as an update to F-21. I'm not sure about RHEL 7.1, but I suppose we'd have 1.12.1 or 1.12.2 in 7.1 (and I'm not making any prediction on RHEL the product now. I'm just a random dude who happens to have a .redhat.com address).
This would change behavior midrelease, it is not nice, I would say 1.12.0 ships in permissive mode by default, and 1.13.0 switches to enforcing by default.
I realize changing a default in a minor release is not great, but 1.13 will be at least 6 months from 1.12, realistically a year later..what is the point of a cool feature like GPO if nobody will use it?
Interested users can always manually switch to enforcing on their own, there is no need to rush this onto them.
I think most users will use whatever realmd sets up..I'm not sure, but is it still the simple access provider? Should we consider changing the default to 'ad' when we switch the GPO feature to 'enforcing' ?
If it is cool people will use it, also nobody will prohib Fedora/other distro's to change the distro default.
So if 1.13.0 is not out yet when F22 ship, F22 can still just turn it on.
- Since one of the goals of having an AD provider is to simplify
configuration for AD environments (as compared to the LDAP provider), I think that using the 'order' option might undermine that goal (by introducing complexity).
I agree. The order was not a good idea, a boolean is much simpler to understand. If the ad_acess_filter was defined, it should have been used without any additional 'order'..
- We should agree upon a name for the options field. How about
"ad_enforce_gpo_hbac"= {True, False}?
Is HBAC a term that would be easily understood by AD admins? I understand it, but I know what it means from working on IPA..
What about ad_access_enforce_gpo?
why the ad_ prefix ?
gpo_access_control = disabled|permissive|enforcing
This would sound like GPO is supported (or planned to be supported) by all backends. Compare entry_cache_timeout with ldap_search_timeout.
uhmm ok ad_gpo_access_control is ok as well, I think it is quite clear that GPO is an AD specific technology, but I may be biased from years of mental association :-)
- When we talk about "loudly complaining", is this simply a
log message with high priority? If so, how high should it be (FATAL_FAILURE? CRIT_FAILURE? OP_FAILURE?)
I was thinking FATAL_FAILURE. I think a syslog message is probably an overkill.
It is not a failure, let alone fatal. It should be something that gets logged in the security log however, so whatever ends up there by default is fine.
This is auditing, we shouldn't use *debug* messages for this.
Fine, whatever is better visible to the admin. I was just trying to think as the poor locked out admin "Oh sssd denied access? tail /var/log/sssd/*.log".
Nothing prevent us from also emitting a debug message I guess, but normally sssd debug logs are not enabled, and the system log is a better place for these kind of messages.
Simo.
--
Hi Sumit,
As you know, the gpo-ldap patch you reviewed does not actually perform an access control check (it only determines which GPOs are applicable to the policy target). Enforcing the access control requires retrieval of files via SMB, which will be in a subsequent gpo-smb patch. As such, I am thinking that it would make more sense to postpone implementation of this config option until the gpo-smb patch, at which time the config option can actually be tested. Do you agree?
Well, the config option can be tested by watching the log files even now. But I think it is ok to add them with your next round of patches since the current one will "only" go to master. But please note that people still might have issues with it because failures in the GPO code will lead to access denied.
I you prefer you can send the patch for the new option in a separate patch as well.
bye, Sumit
Thanks, Yassir. _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
----- Original Message -----
On Tue, Apr 15, 2014 at 07:05:10PM -0400, Yassir Elley wrote:
----- Original Message -----
On Tue, 2014-04-15 at 21:23 +0200, Jakub Hrozek wrote:
On Tue, Apr 15, 2014 at 03:03:55PM -0400, Simo Sorce wrote:
I was thinking 1.12.0 would be 'no' and 1.12.1 would be 'yes'. 1.12.0 should be what F-21 ships with and 1.12.1 would be delivered as an update to F-21. I'm not sure about RHEL 7.1, but I suppose we'd have 1.12.1 or 1.12.2 in 7.1 (and I'm not making any prediction on RHEL the product now. I'm just a random dude who happens to have a .redhat.com address).
This would change behavior midrelease, it is not nice, I would say 1.12.0 ships in permissive mode by default, and 1.13.0 switches to enforcing by default.
I realize changing a default in a minor release is not great, but 1.13 will be at least 6 months from 1.12, realistically a year later..what is the point of a cool feature like GPO if nobody will use it?
Interested users can always manually switch to enforcing on their own, there is no need to rush this onto them.
I think most users will use whatever realmd sets up..I'm not sure, but is it still the simple access provider? Should we consider changing the default to 'ad' when we switch the GPO feature to 'enforcing' ?
If it is cool people will use it, also nobody will prohib Fedora/other distro's to change the distro default.
So if 1.13.0 is not out yet when F22 ship, F22 can still just turn it on.
> * Since one of the goals of having an AD provider is to simplify > configuration for AD environments (as compared to the LDAP > provider), > I think that using the 'order' option might undermine that goal > (by > introducing complexity).
I agree. The order was not a good idea, a boolean is much simpler to understand. If the ad_acess_filter was defined, it should have been used without any additional 'order'..
> > * We should agree upon a name for the options field. How about > "ad_enforce_gpo_hbac"= {True, False}?
Is HBAC a term that would be easily understood by AD admins? I understand it, but I know what it means from working on IPA..
What about ad_access_enforce_gpo?
why the ad_ prefix ?
gpo_access_control = disabled|permissive|enforcing
This would sound like GPO is supported (or planned to be supported) by all backends. Compare entry_cache_timeout with ldap_search_timeout.
uhmm ok ad_gpo_access_control is ok as well, I think it is quite clear that GPO is an AD specific technology, but I may be biased from years of mental association :-)
> * When we talk about "loudly complaining", is this simply a > log message with high priority? If so, how high should it be > (FATAL_FAILURE? CRIT_FAILURE? OP_FAILURE?)
I was thinking FATAL_FAILURE. I think a syslog message is probably an overkill.
It is not a failure, let alone fatal. It should be something that gets logged in the security log however, so whatever ends up there by default is fine.
This is auditing, we shouldn't use *debug* messages for this.
Fine, whatever is better visible to the admin. I was just trying to think as the poor locked out admin "Oh sssd denied access? tail /var/log/sssd/*.log".
Nothing prevent us from also emitting a debug message I guess, but normally sssd debug logs are not enabled, and the system log is a better place for these kind of messages.
Simo.
--
Hi Sumit,
As you know, the gpo-ldap patch you reviewed does not actually perform an access control check (it only determines which GPOs are applicable to the policy target). Enforcing the access control requires retrieval of files via SMB, which will be in a subsequent gpo-smb patch. As such, I am thinking that it would make more sense to postpone implementation of this config option until the gpo-smb patch, at which time the config option can actually be tested. Do you agree?
Well, the config option can be tested by watching the log files even now. But I think it is ok to add them with your next round of patches since the current one will "only" go to master. But please note that people still might have issues with it because failures in the GPO code will lead to access denied.
I you prefer you can send the patch for the new option in a separate patch as well.
bye, Sumit
Actually, I agree that it may be useful to disable gpo settings. As such, I will include the config option with this patch. "Disabled" mode will not perform any gpo processing, while "permissive" mode and "enforcing" mode *will* perform gpo processing (which doesn't currently include any enforcement). Once the gpo-smb patch is complete (i.e. once gpo evaluation and enforcement are implemented), the semantics for the gpo_access_control_mode config option will be: * "disabled": no gpo evaluation or gpo enforcement * "permissive": gpo evaluation, but no gpo enforcement (if access would be denied, syslog message is output) * "enforcing": both gpo evaluation and gpo enforcement (if access is denied, access is denied!)
Regards, Yassir.
----- Original Message -----
----- Original Message -----
On Tue, Apr 15, 2014 at 07:05:10PM -0400, Yassir Elley wrote:
----- Original Message -----
On Tue, 2014-04-15 at 21:23 +0200, Jakub Hrozek wrote:
On Tue, Apr 15, 2014 at 03:03:55PM -0400, Simo Sorce wrote:
> I was thinking 1.12.0 would be 'no' and 1.12.1 would be 'yes'. > 1.12.0 > should be what F-21 ships with and 1.12.1 would be delivered as > an > update > to F-21. I'm not sure about RHEL 7.1, but I suppose we'd have > 1.12.1 > or 1.12.2 in 7.1 (and I'm not making any prediction on RHEL the > product > now. I'm just a random dude who happens to have a .redhat.com > address).
This would change behavior midrelease, it is not nice, I would say 1.12.0 ships in permissive mode by default, and 1.13.0 switches to enforcing by default.
I realize changing a default in a minor release is not great, but 1.13 will be at least 6 months from 1.12, realistically a year later..what is the point of a cool feature like GPO if nobody will use it?
Interested users can always manually switch to enforcing on their own, there is no need to rush this onto them.
I think most users will use whatever realmd sets up..I'm not sure, but is it still the simple access provider? Should we consider changing the default to 'ad' when we switch the GPO feature to 'enforcing' ?
If it is cool people will use it, also nobody will prohib Fedora/other distro's to change the distro default.
So if 1.13.0 is not out yet when F22 ship, F22 can still just turn it on.
> > * Since one of the goals of having an AD provider is to > > simplify > > configuration for AD environments (as compared to the LDAP > > provider), > > I think that using the 'order' option might undermine that goal > > (by > > introducing complexity). > > I agree. The order was not a good idea, a boolean is much simpler > to > understand. If the ad_acess_filter was defined, it should have > been > used > without any additional 'order'.. > > > > > * We should agree upon a name for the options field. How about > > "ad_enforce_gpo_hbac"= {True, False}? > > Is HBAC a term that would be easily understood by AD admins? I > understand it, but I know what it means from working on IPA.. > > What about ad_access_enforce_gpo?
why the ad_ prefix ?
gpo_access_control = disabled|permissive|enforcing
This would sound like GPO is supported (or planned to be supported) by all backends. Compare entry_cache_timeout with ldap_search_timeout.
uhmm ok ad_gpo_access_control is ok as well, I think it is quite clear that GPO is an AD specific technology, but I may be biased from years of mental association :-)
> > * When we talk about "loudly complaining", is this simply a > > log message with high priority? If so, how high should it be > > (FATAL_FAILURE? CRIT_FAILURE? OP_FAILURE?) > > I was thinking FATAL_FAILURE. I think a syslog message is > probably > an > overkill.
It is not a failure, let alone fatal. It should be something that gets logged in the security log however, so whatever ends up there by default is fine.
This is auditing, we shouldn't use *debug* messages for this.
Fine, whatever is better visible to the admin. I was just trying to think as the poor locked out admin "Oh sssd denied access? tail /var/log/sssd/*.log".
Nothing prevent us from also emitting a debug message I guess, but normally sssd debug logs are not enabled, and the system log is a better place for these kind of messages.
Simo.
--
Hi Sumit,
As you know, the gpo-ldap patch you reviewed does not actually perform an access control check (it only determines which GPOs are applicable to the policy target). Enforcing the access control requires retrieval of files via SMB, which will be in a subsequent gpo-smb patch. As such, I am thinking that it would make more sense to postpone implementation of this config option until the gpo-smb patch, at which time the config option can actually be tested. Do you agree?
Well, the config option can be tested by watching the log files even now. But I think it is ok to add them with your next round of patches since the current one will "only" go to master. But please note that people still might have issues with it because failures in the GPO code will lead to access denied.
I you prefer you can send the patch for the new option in a separate patch as well.
bye, Sumit
Actually, I agree that it may be useful to disable gpo settings. As such, I will include the config option with this patch. "Disabled" mode will not perform any gpo processing, while "permissive" mode and "enforcing" mode *will* perform gpo processing (which doesn't currently include any enforcement). Once the gpo-smb patch is complete (i.e. once gpo evaluation and enforcement are implemented), the semantics for the gpo_access_control_mode config option will be:
- "disabled": no gpo evaluation or gpo enforcement
- "permissive": gpo evaluation, but no gpo enforcement (if access would be
denied, syslog message is output)
- "enforcing": both gpo evaluation and gpo enforcement (if access is denied,
access is denied!)
I have attached a new patch, which squashes the changes made to address Sumit's code review comments with the original patch. Among other things, this new patch includes the ad_gpo_access_control config setting, and it also addresses the "libsss_ad.so is not portable!" warning (by introducing libsss_ad_common.la).
Regards, Yassir.
On Wed, Apr 16, 2014 at 08:47:17PM -0400, Yassir Elley wrote:
----- Original Message -----
----- Original Message -----
On Tue, Apr 15, 2014 at 07:05:10PM -0400, Yassir Elley wrote:
----- Original Message -----
On Tue, 2014-04-15 at 21:23 +0200, Jakub Hrozek wrote:
On Tue, Apr 15, 2014 at 03:03:55PM -0400, Simo Sorce wrote: > > I was thinking 1.12.0 would be 'no' and 1.12.1 would be 'yes'. > > 1.12.0 > > should be what F-21 ships with and 1.12.1 would be delivered as > > an > > update > > to F-21. I'm not sure about RHEL 7.1, but I suppose we'd have > > 1.12.1 > > or 1.12.2 in 7.1 (and I'm not making any prediction on RHEL the > > product > > now. I'm just a random dude who happens to have a .redhat.com > > address). > > This would change behavior midrelease, it is not nice, > I would say > 1.12.0 ships in permissive mode by default, and 1.13.0 switches to > enforcing by default.
I realize changing a default in a minor release is not great, but 1.13 will be at least 6 months from 1.12, realistically a year later..what is the point of a cool feature like GPO if nobody will use it?
> > Interested users can always manually switch to enforcing on their > own, > there is no need to rush this onto them.
I think most users will use whatever realmd sets up..I'm not sure, but is it still the simple access provider? Should we consider changing the default to 'ad' when we switch the GPO feature to 'enforcing' ?
If it is cool people will use it, also nobody will prohib Fedora/other distro's to change the distro default.
So if 1.13.0 is not out yet when F22 ship, F22 can still just turn it on.
> > > * Since one of the goals of having an AD provider is to > > > simplify > > > configuration for AD environments (as compared to the LDAP > > > provider), > > > I think that using the 'order' option might undermine that goal > > > (by > > > introducing complexity). > > > > I agree. The order was not a good idea, a boolean is much simpler > > to > > understand. If the ad_acess_filter was defined, it should have > > been > > used > > without any additional 'order'.. > > > > > > > > * We should agree upon a name for the options field. How about > > > "ad_enforce_gpo_hbac"= {True, False}? > > > > Is HBAC a term that would be easily understood by AD admins? I > > understand it, but I know what it means from working on IPA.. > > > > What about ad_access_enforce_gpo? > > why the ad_ prefix ? > > gpo_access_control = disabled|permissive|enforcing
This would sound like GPO is supported (or planned to be supported) by all backends. Compare entry_cache_timeout with ldap_search_timeout.
uhmm ok ad_gpo_access_control is ok as well, I think it is quite clear that GPO is an AD specific technology, but I may be biased from years of mental association :-)
> > > * When we talk about "loudly complaining", is this simply a > > > log message with high priority? If so, how high should it be > > > (FATAL_FAILURE? CRIT_FAILURE? OP_FAILURE?) > > > > I was thinking FATAL_FAILURE. I think a syslog message is > > probably > > an > > overkill. > > It is not a failure, let alone fatal. > It should be something that gets logged in the security log > however, > so > whatever ends up there by default is fine. > > This is auditing, we shouldn't use *debug* messages for this.
Fine, whatever is better visible to the admin. I was just trying to think as the poor locked out admin "Oh sssd denied access? tail /var/log/sssd/*.log".
Nothing prevent us from also emitting a debug message I guess, but normally sssd debug logs are not enabled, and the system log is a better place for these kind of messages.
Simo.
--
Hi Sumit,
As you know, the gpo-ldap patch you reviewed does not actually perform an access control check (it only determines which GPOs are applicable to the policy target). Enforcing the access control requires retrieval of files via SMB, which will be in a subsequent gpo-smb patch. As such, I am thinking that it would make more sense to postpone implementation of this config option until the gpo-smb patch, at which time the config option can actually be tested. Do you agree?
Well, the config option can be tested by watching the log files even now. But I think it is ok to add them with your next round of patches since the current one will "only" go to master. But please note that people still might have issues with it because failures in the GPO code will lead to access denied.
I you prefer you can send the patch for the new option in a separate patch as well.
bye, Sumit
Actually, I agree that it may be useful to disable gpo settings. As such, I will include the config option with this patch. "Disabled" mode will not perform any gpo processing, while "permissive" mode and "enforcing" mode *will* perform gpo processing (which doesn't currently include any enforcement). Once the gpo-smb patch is complete (i.e. once gpo evaluation and enforcement are implemented), the semantics for the gpo_access_control_mode config option will be:
- "disabled": no gpo evaluation or gpo enforcement
- "permissive": gpo evaluation, but no gpo enforcement (if access would be
denied, syslog message is output)
- "enforcing": both gpo evaluation and gpo enforcement (if access is denied,
access is denied!)
I have attached a new patch, which squashes the changes made to address Sumit's code review comments with the original patch. Among other things, this new patch includes the ad_gpo_access_control config setting, and it also addresses the "libsss_ad.so is not portable!" warning (by introducing libsss_ad_common.la).
Regards, Yassir.
Thank you. Patch applies and is working as expected. I still have a few minor comments and questions (see below), but in general it is an ACK from my side.
Jakub, it would be nice if you can continue here. The only nice-to-have fix is to use tmp_ctx (see below). But this can be fixed in the next round as well.
bye, Sumit
- /* supported GPO access control modes */
- enum gpo_access_control_mode {
GPO_ACCESS_CONTROL_DISABLED = 1,
GPO_ACCESS_CONTROL_PERMISSIVE = 2,
GPO_ACCESS_CONTROL_ENFORCING = 3,
- } gpo_access_control_mode;
};
I prefer to include 0 when I used enums with explicit values because then there is a know value if the enum is initialized with zeros. If you think it is not needed I wonder why you assing explicit values at all? Please note, there is no need to change anything here, I just would like to know the reasoning.
+static char *ad_gpo_parent_dn(TALLOC_CTX *mem_ctx,
struct ldb_context *ldb_ctx,
const char *dn)
+{
- struct ldb_dn *ldb_dn;
- struct ldb_dn *parent_ldb_dn;
- const char *p;
- char *parent_dn;
- ldb_dn = ldb_dn_new(mem_ctx, ldb_ctx, dn);
- parent_ldb_dn = ldb_dn_get_parent(mem_ctx, ldb_dn);
- p = ldb_dn_get_linearized(parent_ldb_dn);
- parent_dn = talloc_strdup(mem_ctx, p);
- return parent_dn;
just a comment as well. You are leaking ldb_dn here. This is ok with talloc and your usage, because mem_ctx is the state of a request and will go way soon. But you have to be careful when you call this function with a long living memory context.
+}
+/*
- 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 +ad_gpo_populate_som_list(TALLOC_CTX *mem_ctx,
struct ldb_context *ldb_ctx,
char *target_dn,
int *_num_soms,
struct gp_som ***_som_list)
+{
- TALLOC_CTX *tmp_ctx = NULL;
- int ret;
- int rdn_count = 0;
- int som_idx = 0;
- struct gp_som **som_list;
- char *parent_dn = NULL;
- char *tmp_dn = NULL;
- struct ldb_dn *ldb_target_dn;
- tmp_ctx = talloc_new(NULL);
- if (tmp_ctx == NULL) {
ret = ENOMEM;
goto done;
- }
- ldb_target_dn = ldb_dn_new(mem_ctx, ldb_ctx, target_dn);
Since you already have a tmp_ctx, I would suggest to use it.
- rdn_count = ldb_dn_get_comp_num(ldb_target_dn);
- if (rdn_count == 0) {
*_som_list = NULL;
ret = EOK;
goto done;
- }
- /* assume the worst-case, in which every parent is a SOM */
- /* include space for Site SOM and NULL: rdn_count + 1 + 1 */
- som_list = talloc_array(tmp_ctx, struct gp_som *, rdn_count + 1 + 1);
- if (som_list == NULL) {
ret = ENOMEM;
goto done;
- }
- /* first, populate the OU and Domain SOMs */
- tmp_dn = target_dn;;
- while ((parent_dn = ad_gpo_parent_dn(mem_ctx, ldb_ctx, tmp_dn))) {
I think you can even use tmp_ctx here as well, because you talloc_strdup() the parent_dn later with another memory context.
if ((strncasecmp(parent_dn, "OU=", strlen("OU=")) == 0) ||
(strncasecmp(parent_dn, "DC=", strlen("DC=")) == 0)) {
som_list[som_idx] = talloc_zero(som_list, struct gp_som);
if (som_list[som_idx] == NULL) {
ret = ENOMEM;
goto done;
}
som_list[som_idx]->som_dn = talloc_strdup(som_list[som_idx],
parent_dn);
if (som_list[som_idx]->som_dn == NULL) {
ret = ENOMEM;
goto done;
}
som_idx++;
}
if (strncasecmp(parent_dn, "DC=", strlen("DC=")) == 0) {
break;
}
tmp_dn = parent_dn;
- }
- som_list[som_idx] = NULL;
- *_num_soms = som_idx;
- *_som_list = talloc_steal(mem_ctx, som_list);
- ret = EOK;
- done:
- talloc_free(tmp_ctx);
- return ret;
+}
+/*
On Fri, Apr 18, 2014 at 02:08:44PM +0200, Sumit Bose wrote:
Jakub, it would be nice if you can continue here. The only nice-to-have fix is to use tmp_ctx (see below). But this can be fixed in the next round as well.
Sure, today I will concentrate on getting some more dbus related patches to the list, but I'll return to the review on Tuesday.
----- Original Message -----
On Wed, Apr 16, 2014 at 08:47:17PM -0400, Yassir Elley wrote:
----- Original Message -----
----- Original Message -----
On Tue, Apr 15, 2014 at 07:05:10PM -0400, Yassir Elley wrote:
----- Original Message -----
On Tue, 2014-04-15 at 21:23 +0200, Jakub Hrozek wrote: > On Tue, Apr 15, 2014 at 03:03:55PM -0400, Simo Sorce wrote: > > > I was thinking 1.12.0 would be 'no' and 1.12.1 would be > > > 'yes'. > > > 1.12.0 > > > should be what F-21 ships with and 1.12.1 would be delivered > > > as > > > an > > > update > > > to F-21. I'm not sure about RHEL 7.1, but I suppose we'd have > > > 1.12.1 > > > or 1.12.2 in 7.1 (and I'm not making any prediction on RHEL > > > the > > > product > > > now. I'm just a random dude who happens to have a .redhat.com > > > address). > > > > This would change behavior midrelease, it is not nice, > > I would say > > 1.12.0 ships in permissive mode by default, and 1.13.0 switches > > to > > enforcing by default. > > I realize changing a default in a minor release is not great, but > 1.13 > will be at least 6 months from 1.12, realistically a year > later..what > is > the point of a cool feature like GPO if nobody will use it? > > > > > Interested users can always manually switch to enforcing on > > their > > own, > > there is no need to rush this onto them. > > I think most users will use whatever realmd sets up..I'm not > sure, > but > is it still the simple access provider? Should we consider > changing > the > default to 'ad' when we switch the GPO feature to 'enforcing' ?
If it is cool people will use it, also nobody will prohib Fedora/other distro's to change the distro default.
So if 1.13.0 is not out yet when F22 ship, F22 can still just turn it on.
> > > > * Since one of the goals of having an AD provider is to > > > > simplify > > > > configuration for AD environments (as compared to the LDAP > > > > provider), > > > > I think that using the 'order' option might undermine that > > > > goal > > > > (by > > > > introducing complexity). > > > > > > I agree. The order was not a good idea, a boolean is much > > > simpler > > > to > > > understand. If the ad_acess_filter was defined, it should > > > have > > > been > > > used > > > without any additional 'order'.. > > > > > > > > > > > * We should agree upon a name for the options field. How > > > > about > > > > "ad_enforce_gpo_hbac"= {True, False}? > > > > > > Is HBAC a term that would be easily understood by AD admins? > > > I > > > understand it, but I know what it means from working on IPA.. > > > > > > What about ad_access_enforce_gpo? > > > > why the ad_ prefix ? > > > > gpo_access_control = disabled|permissive|enforcing > > This would sound like GPO is supported (or planned to be > supported) > by > all backends. Compare entry_cache_timeout with > ldap_search_timeout.
uhmm ok ad_gpo_access_control is ok as well, I think it is quite clear that GPO is an AD specific technology, but I may be biased from years of mental association :-)
> > > > * When we talk about "loudly complaining", is this simply a > > > > log message with high priority? If so, how high should it > > > > be > > > > (FATAL_FAILURE? CRIT_FAILURE? OP_FAILURE?) > > > > > > I was thinking FATAL_FAILURE. I think a syslog message is > > > probably > > > an > > > overkill. > > > > It is not a failure, let alone fatal. > > It should be something that gets logged in the security log > > however, > > so > > whatever ends up there by default is fine. > > > > This is auditing, we shouldn't use *debug* messages for this. > > Fine, whatever is better visible to the admin. I was just trying > to > think > as the poor locked out admin "Oh sssd denied access? tail > /var/log/sssd/*.log".
Nothing prevent us from also emitting a debug message I guess, but normally sssd debug logs are not enabled, and the system log is a better place for these kind of messages.
Simo.
--
Hi Sumit,
As you know, the gpo-ldap patch you reviewed does not actually perform an access control check (it only determines which GPOs are applicable to the policy target). Enforcing the access control requires retrieval of files via SMB, which will be in a subsequent gpo-smb patch. As such, I am thinking that it would make more sense to postpone implementation of this config option until the gpo-smb patch, at which time the config option can actually be tested. Do you agree?
Well, the config option can be tested by watching the log files even now. But I think it is ok to add them with your next round of patches since the current one will "only" go to master. But please note that people still might have issues with it because failures in the GPO code will lead to access denied.
I you prefer you can send the patch for the new option in a separate patch as well.
bye, Sumit
Actually, I agree that it may be useful to disable gpo settings. As such, I will include the config option with this patch. "Disabled" mode will not perform any gpo processing, while "permissive" mode and "enforcing" mode *will* perform gpo processing (which doesn't currently include any enforcement). Once the gpo-smb patch is complete (i.e. once gpo evaluation and enforcement are implemented), the semantics for the gpo_access_control_mode config option will be:
- "disabled": no gpo evaluation or gpo enforcement
- "permissive": gpo evaluation, but no gpo enforcement (if access would
be denied, syslog message is output)
- "enforcing": both gpo evaluation and gpo enforcement (if access is
denied, access is denied!)
I have attached a new patch, which squashes the changes made to address Sumit's code review comments with the original patch. Among other things, this new patch includes the ad_gpo_access_control config setting, and it also addresses the "libsss_ad.so is not portable!" warning (by introducing libsss_ad_common.la).
Regards, Yassir.
Thank you. Patch applies and is working as expected. I still have a few minor comments and questions (see below), but in general it is an ACK from my side.
Jakub, it would be nice if you can continue here. The only nice-to-have fix is to use tmp_ctx (see below). But this can be fixed in the next round as well.
bye, Sumit
Hi,
I responded to your comments (see below) and have attached a revised patch, which includes changes for: * numbering the enum from 0 * using tmp_ctx in ad_gpo_populate_som_list * rewriting ad_gpo_parent_dn so there is no leak
Thanks, Yassir.
- /* supported GPO access control modes */
- enum gpo_access_control_mode {
GPO_ACCESS_CONTROL_DISABLED = 1,
GPO_ACCESS_CONTROL_PERMISSIVE = 2,
GPO_ACCESS_CONTROL_ENFORCING = 3,
- } gpo_access_control_mode;
};
I prefer to include 0 when I used enums with explicit values because then there is a know value if the enum is initialized with zeros. If you think it is not needed I wonder why you assing explicit values at all? Please note, there is no need to change anything here, I just would like to know the reasoning.
I think I was pattern-matching off "enum schema_type", which starts with a 1. But I agree that there is no reason to use explicit values at all here. I have made the necessary change.
+static char *ad_gpo_parent_dn(TALLOC_CTX *mem_ctx,
struct ldb_context *ldb_ctx,
const char *dn)
+{
- struct ldb_dn *ldb_dn;
- struct ldb_dn *parent_ldb_dn;
- const char *p;
- char *parent_dn;
- ldb_dn = ldb_dn_new(mem_ctx, ldb_ctx, dn);
- parent_ldb_dn = ldb_dn_get_parent(mem_ctx, ldb_dn);
- p = ldb_dn_get_linearized(parent_ldb_dn);
- parent_dn = talloc_strdup(mem_ctx, p);
- return parent_dn;
just a comment as well. You are leaking ldb_dn here. This is ok with talloc and your usage, because mem_ctx is the state of a request and will go way soon. But you have to be careful when you call this function with a long living memory context.
I agree with your comment. I have rewritten the ad_gpo_parent_dn function so that there are no memory leaks.
+}
+/*
- 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 +ad_gpo_populate_som_list(TALLOC_CTX *mem_ctx,
struct ldb_context *ldb_ctx,
char *target_dn,
int *_num_soms,
struct gp_som ***_som_list)
+{
- TALLOC_CTX *tmp_ctx = NULL;
- int ret;
- int rdn_count = 0;
- int som_idx = 0;
- struct gp_som **som_list;
- char *parent_dn = NULL;
- char *tmp_dn = NULL;
- struct ldb_dn *ldb_target_dn;
- tmp_ctx = talloc_new(NULL);
- if (tmp_ctx == NULL) {
ret = ENOMEM;
goto done;
- }
- ldb_target_dn = ldb_dn_new(mem_ctx, ldb_ctx, target_dn);
Since you already have a tmp_ctx, I would suggest to use it.
I agree. I have made the change.
- rdn_count = ldb_dn_get_comp_num(ldb_target_dn);
- if (rdn_count == 0) {
*_som_list = NULL;
ret = EOK;
goto done;
- }
- /* assume the worst-case, in which every parent is a SOM */
- /* include space for Site SOM and NULL: rdn_count + 1 + 1 */
- som_list = talloc_array(tmp_ctx, struct gp_som *, rdn_count + 1 + 1);
- if (som_list == NULL) {
ret = ENOMEM;
goto done;
- }
- /* first, populate the OU and Domain SOMs */
- tmp_dn = target_dn;;
- while ((parent_dn = ad_gpo_parent_dn(mem_ctx, ldb_ctx, tmp_dn))) {
I think you can even use tmp_ctx here as well, because you talloc_strdup() the parent_dn later with another memory context.
I agree. I have made the change.
Yassir.
On Fri, Apr 18, 2014 at 12:56:27PM -0400, Yassir Elley wrote:
I agree. I have made the change.
Yassir.
Hi,
sorry I didn't get to continue the review sooner, but the patch is massive and I've been trying to get some dbus-related code to master last week. I hope you don't mind Yassir as you were on a vacation last week :)
In general the code is good, I only have some more suggestions about coding style or talloc hierarchy and one question about the default, see below.
From 3617c7b171a4d7090aa6e4c2673769686b4a5919 Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Mon, 20 Jan 2014 11:17:06 -0500 Subject: [PATCH] Implemented LDAP component of GPO-based access control
[...]
There are three supported values for this option:
<itemizedlist>
<listitem>
<para>
disabled: GPO-based access control rules
are neither evaluated nor enforced.
</para>
</listitem>
<listitem>
<para>
enforcing: GPO-based access control
rules are evaluated and enforced.
</para>
</listitem>
<listitem>
<para>
permissive: GPO-based access control
rules are evaluated, but not enforced.
Instead, a syslog message will be
emitted indicating that the user would
have been denied access if this option's
value were set to enforcing.
</para>
</listitem>
</itemizedlist>
</para>
<para>
Default: disabled
Is there a reason the default is disabled rather than permissive? Don't we want to identify the cases where a user would be locked out soon?
</para>
</listitem>
</varlistentry>
<varlistentry> <term>dyndns_update (boolean)</term> <listitem> <para>
diff --git a/src/providers/ad/ad_access.c b/src/providers/ad/ad_access.c index cae075d4222bba3c05d2c09e26da9b19f4507cc1..cd51129f63bafb2d1d40a9a2880ea02c7617f0db 100644 --- a/src/providers/ad/ad_access.c +++ b/src/providers/ad/ad_access.c
[...]
static errno_t @@ -385,7 +420,26 @@ ad_access_recv(struct tevent_req *req) }
In general with tevent requests, the _recv() function should be the last one in the code, so when you read the code and find the recv() function, you know you can stop reading and the rest is either another request or helper functions. Here you have _done after _recv.
static void -ad_access_check_done(struct tevent_req *req); +ad_gpo_access_done(struct tevent_req *subreq) +{
- struct tevent_req *req;
- errno_t ret;
- req = tevent_req_callback_data(subreq, struct tevent_req);
- ret = ad_gpo_access_recv(subreq);
- talloc_zfree(subreq);
It would be nice to have a DEBUG message here, at least for failure.
- if (ret == EOK) {
tevent_req_done(req);
- } else {
tevent_req_error(req, ret);
- }
+} diff --git a/src/providers/ad/ad_gpo.c b/src/providers/ad/ad_gpo.c new file mode 100644 index 0000000000000000000000000000000000000000..935e1924905fb25461ebf57db2e42eb1e8348417 --- /dev/null +++ b/src/providers/ad/ad_gpo.c @@ -0,0 +1,2221 @@
[...]
+/*
- This function determines whether the input ACE includes 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 security_ace *ace,
bool *_included)
I wonder if this function could simply return a boolean, the caller ignores the return code anyway.
+{
- int i = 0;
- struct dom_sid ace_dom_sid;
- struct dom_sid user_dom_sid;
- struct dom_sid group_dom_sid;
- char buf[SID_MAX_LEN + 1];
- ace_dom_sid = ace->trustee;
- dom_sid_string_buf(&ace_dom_sid, buf, SID_MAX_LEN);
- if (!string_to_sid(&user_dom_sid, user_sid)) {
DEBUG(SSSDBG_OP_FAILURE, "string_to_sid failed\n");
return EINVAL;
- }
- if (dom_sid_equal(&ace_dom_sid, &user_dom_sid)) {
*_included = true;
return EOK;
- }
- for (i = 0; i < group_size; i++) {
if (!string_to_sid(&group_dom_sid, group_sids[i])) {
DEBUG(SSSDBG_OP_FAILURE, "string_to_sid failed\n");
return EINVAL;
}
if (dom_sid_equal(&ace_dom_sid, &group_dom_sid)) {
*_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.
- 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
Thanks for the comments, they are really helpful. What does 'M' stands for here, though?
- 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.
- */
+static enum ace_eval_status ad_gpo_evaluate_ace(struct security_ace *ace,
const char *user_sid,
const char **group_sids,
int group_size)
[...]
+static errno_t +ad_gpo_filter_gpos_by_dacl(TALLOC_CTX *mem_ctx,
char *user,
struct sss_domain_info *domain,
struct gp_gpo **candidate_gpos,
int num_candidate_gpos,
struct gp_gpo ***_dacl_filtered_gpos,
int *_num_dacl_filtered_gpos)
+{
- TALLOC_CTX *tmp_ctx = NULL;
- int i = 0;
- int ret = 0;
- struct gp_gpo *candidate_gpo = NULL;
- struct security_descriptor *sd = NULL;
- struct security_acl *dacl = NULL;
- const char *user_sid = NULL;
- const char **group_sids = NULL;
- int group_size = 0;
- int gpo_dn_idx = 0;
- bool access_allowed = false;
- struct gp_gpo **dacl_filtered_gpos = NULL;
- tmp_ctx = talloc_new(NULL);
- if (tmp_ctx == NULL) {
ret = ENOMEM;
goto done;
- }
- ret = ad_gpo_get_sids(tmp_ctx, user, domain, &user_sid,
&group_sids, &group_size);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Unable to retrieve SIDs: [%d](%s)\n", ret, strerror(ret));
ret = ENOENT;
I wonder if ENOENT is the right error code, the information is not missing it just can't be processed, right? You can even create your own specific error codes if you find that the standard errno codes don't express the error well..
goto done;
- }
- dacl_filtered_gpos = talloc_array(tmp_ctx,
struct gp_gpo *,
num_candidate_gpos + 1);
[...]
- if (reply_count < 1) {
DEBUG(SSSDBG_OP_FAILURE, "No DN retrieved for policy target.\n");
ret = ENOENT;
goto done;
- } else if (reply_count > 1) {
DEBUG(SSSDBG_OP_FAILURE, "Multiple replies for policy target\n");
ret = ERR_INTERNAL;
goto done;
- } else if (reply == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "reply_count is 1, but reply is NULL\n");
ret = ERR_INTERNAL;
goto done;
- }
- /* reply[0] holds requested attributes of single reply */
- ret = sysdb_attrs_get_string(reply[0], AD_AT_DN, &target_dn);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_string failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- state->target_dn = talloc_strdup(state, target_dn);
- if (state->target_dn == NULL) {
ret = ENOMEM;
goto done;
- }
The target_dn here is already allocated on state, it's just part of reply, (state->reply->target_dn), I think you can just assign to the state->target_dn pointer or if you'd like to free reply later on, steal target dn onto state instead of duplicating the string. This issue is repeated in the code couple of times.
- ret = sysdb_attrs_get_uint32_t(reply[0], AD_AT_UAC, &uac);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_uint32_t failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- /* we only support computer policy targets, not users */
- if (!(uac & UAC_WORKSTATION_TRUST_ACCOUNT)) {
ret = EINVAL;
goto done;
- }
- state->target_dn = talloc_strdup(state, target_dn);
- if (state->target_dn == NULL) {
ret = ENOMEM;
goto done;
- }
- num_enabled = 0;
- ptr = raw_gplink_value;
[...]
- for (i = 0; i < gplink_count; i++) {
first = ptr + 1;
last = strchr(first, delim);
if (last == NULL) {
break;
}
*last = '\0';
last++;
dn = first;
if ( strncasecmp(dn, "LDAP://", 7)== 0 ) {
dn = dn + 7;
}
gplink_options = strchr(first, ';');
if (gplink_options == NULL) {
break;
Shouldn't we juts continue to the next item instead of breaking? Or does this condition mean that the end of the list was reached?
}
*gplink_options = '\0';
gplink_options++;
gplink_number = strtouint32(gplink_options, NULL, 10);
I think you should check errno for failures after the strotouint32 call.
DEBUG(SSSDBG_TRACE_ALL,
"gplink_list[%d]: [%s; %d]\n", num_enabled, dn, gplink_number);
if ((gplink_number == 1) || (gplink_number ==3)) {
/* ignore flag is set */
DEBUG(SSSDBG_TRACE_ALL, "ignored gpo skipped\n");
ptr = last;
continue;
}
if (allow_enforced_only && (gplink_number == 0)) {
/* unenforced flag is set; only enforced gpos allowed */
DEBUG(SSSDBG_TRACE_ALL, "unenforced gpo skipped\n");
ptr = last;
continue;
}
gplink_list[num_enabled] = talloc_zero(gplink_list, struct gp_gplink);
if (gplink_list[num_enabled] == NULL) {
ret = ENOMEM;
goto done;
}
gplink_list[num_enabled]->gpo_dn =
talloc_strdup(gplink_list[num_enabled], dn);
if (gplink_list[num_enabled]->gpo_dn == NULL) {
ret = ENOMEM;
goto done;
}
if (gplink_number == 0) {
gplink_list[num_enabled]->enforced = 0;
num_enabled++;
} else if (gplink_number == 2) {
gplink_list[num_enabled]->enforced = 1;
num_enabled++;
} else {
ret = EINVAL;
goto done;
}
ptr = last;
- }
[...]
+ad_gpo_site_dn_retrieval_done(struct tevent_req *subreq) +{
- struct tevent_req *req;
- struct ad_gpo_process_som_state *state;
- int ret;
- int dp_error;
- int i = 0;
- size_t reply_count;
- struct sysdb_attrs **reply;
- const char *configNC;
- req = tevent_req_callback_data(subreq, struct tevent_req);
- state = tevent_req_data(req, struct ad_gpo_process_som_state);
Can you add a comment that would mention that this attribute should be read from the rootdse in the future and maybe even link to https://fedorahosted.org/sssd/ticket/2276 ?
- ret = sdap_get_generic_recv(subreq, state,
&reply_count, &reply);
- talloc_zfree(subreq);
- if (ret != EOK) {
ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
/* TBD: handle (dp_error == DP_ERR_OFFLINE) case */
DEBUG(SSSDBG_OP_FAILURE,
"Unable to get configNC: [%d](%s)\n", ret, strerror(ret));
ret = ENOENT;
goto done;
- }
[...]
- /* note that space was allocated for site_dn when allocating som_list */
- state->som_list[state->num_soms] =
talloc_zero(state->som_list, struct gp_som);
- if (state->som_list[state->num_soms] == NULL) {
ret = ENOMEM;
goto done;
- }
- state->som_list[state->num_soms]->som_dn =
talloc_strdup(state->som_list[state->num_soms], state->site_dn);
I think talloc_steal can be used safely here, no need to strdup.
- if (state->som_list[state->num_soms]->som_dn == NULL) {
ret = ENOMEM;
goto done;
- }
- state->num_soms++;
- state->som_list[state->num_soms] = NULL;
- i = 0;
- while (state->som_list[i]) {
DEBUG(SSSDBG_TRACE_FUNC, "som_list[%d]->som_dn is %s\n", i,
state->som_list[i]->som_dn);
i++;
- }
- ret = ad_gpo_get_som_attrs_step(req);
- done:
- if (ret == EOK) {
tevent_req_done(req);
Can we even have EOK here? Looks like the functions either return EAGAIN or outright failure..
- } else if (ret != EAGAIN) {
tevent_req_error(req, ret);
- }
+} +static errno_t +ad_gpo_get_som_attrs_step(struct tevent_req *req) +{
- const char *attrs[] = {AD_AT_GPLINK, AD_AT_GPOPTIONS, NULL};
- struct tevent_req *subreq;
- struct ad_gpo_process_som_state *state;
- state = tevent_req_data(req, struct ad_gpo_process_som_state);
- state->som_index++;
- struct gp_som *gp_som = state->som_list[state->som_index];
- /* gp_som is NULL only after all SOMs have been processed */
- if (gp_som == NULL) return EOK;
- char *som_dn = gp_som->som_dn;
- subreq = sdap_get_generic_send(state, state->ev, state->opts,
sdap_id_op_handle(state->sdap_op),
som_dn, LDAP_SCOPE_BASE,
"(objectclass=*)", attrs, NULL, 0,
state->timeout,
false);
- if (subreq == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send failed.\n");
return ENOMEM;
- }
- tevent_req_set_callback(subreq, ad_gpo_get_som_attrs_done, req);
- return EAGAIN;
+}
[...]
diff --git a/src/util/sss_ldap.h b/src/util/sss_ldap.h index f298b2fbb30cf1532f8e94504ffb83ef73880b81..083c89141987f38313906d7d7a9a003a74c10cef 100644 --- a/src/util/sss_ldap.h +++ b/src/util/sss_ldap.h @@ -55,6 +55,15 @@ int sss_ldap_get_diagnostic_msg(TALLOC_CTX *mem_ctx, #define LDAP_SERVER_ASQ_OID "1.2.840.113556.1.4.1504" #endif /* LDAP_SERVER_ASQ_OID */
+#ifndef LDAP_SERVER_SD_OID +#define LDAP_SERVER_SD_OID "1.2.840.113556.1.4.801" +#endif /* LDAP_SERVER_SD_OID */
+#define SECINFO_OWNER ( 0x00000001 ) +#define SECINFO_GROUP ( 0x00000002 ) +#define SECINFO_DACL ( 0x00000004 ) +#define SECINFO_SACL ( 0x00000008 )
Of all these constants, only SECINFO_DACL is used, is the rest included for posterity? Do these magic values come from some technet article or MSDN documentation? If so, it might be nice to add a link or a comment.
int sss_ldap_control_create(const char *oid, int iscritical, struct berval *value, int dupval, LDAPControl **ctrlp); -- 1.8.5.3
In general, it's really nice work, thank you!
On Mon, Apr 28, 2014 at 01:28:00PM +0200, Jakub Hrozek wrote:
On Fri, Apr 18, 2014 at 12:56:27PM -0400, Yassir Elley wrote:
I agree. I have made the change.
Yassir.
Hi,
sorry I didn't get to continue the review sooner, but the patch is massive and I've been trying to get some dbus-related code to master last week. I hope you don't mind Yassir as you were on a vacation last week :)
In general the code is good, I only have some more suggestions about coding style or talloc hierarchy and one question about the default, see below.
From 3617c7b171a4d7090aa6e4c2673769686b4a5919 Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Mon, 20 Jan 2014 11:17:06 -0500 Subject: [PATCH] Implemented LDAP component of GPO-based access control
[...]
There are three supported values for this option:
<itemizedlist>
<listitem>
<para>
disabled: GPO-based access control rules
are neither evaluated nor enforced.
</para>
</listitem>
<listitem>
<para>
enforcing: GPO-based access control
rules are evaluated and enforced.
</para>
</listitem>
<listitem>
<para>
permissive: GPO-based access control
rules are evaluated, but not enforced.
Instead, a syslog message will be
emitted indicating that the user would
have been denied access if this option's
value were set to enforcing.
</para>
</listitem>
</itemizedlist>
</para>
<para>
Default: disabled
Is there a reason the default is disabled rather than permissive? Don't we want to identify the cases where a user would be locked out soon?
I think this was based on one of my earlier comments. If there are any issues in the GPO code the access validation might fail even if the default value is permissive.
bye, Sumit
On Mon, Apr 28, 2014 at 01:51:42PM +0200, Sumit Bose wrote:
On Mon, Apr 28, 2014 at 01:28:00PM +0200, Jakub Hrozek wrote:
On Fri, Apr 18, 2014 at 12:56:27PM -0400, Yassir Elley wrote:
I agree. I have made the change.
Yassir.
Hi,
sorry I didn't get to continue the review sooner, but the patch is massive and I've been trying to get some dbus-related code to master last week. I hope you don't mind Yassir as you were on a vacation last week :)
In general the code is good, I only have some more suggestions about coding style or talloc hierarchy and one question about the default, see below.
From 3617c7b171a4d7090aa6e4c2673769686b4a5919 Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Mon, 20 Jan 2014 11:17:06 -0500 Subject: [PATCH] Implemented LDAP component of GPO-based access control
[...]
There are three supported values for this option:
<itemizedlist>
<listitem>
<para>
disabled: GPO-based access control rules
are neither evaluated nor enforced.
</para>
</listitem>
<listitem>
<para>
enforcing: GPO-based access control
rules are evaluated and enforced.
</para>
</listitem>
<listitem>
<para>
permissive: GPO-based access control
rules are evaluated, but not enforced.
Instead, a syslog message will be
emitted indicating that the user would
have been denied access if this option's
value were set to enforcing.
</para>
</listitem>
</itemizedlist>
</para>
<para>
Default: disabled
Is there a reason the default is disabled rather than permissive? Don't we want to identify the cases where a user would be locked out soon?
I think this was based on one of my earlier comments. If there are any issues in the GPO code the access validation might fail even if the default value is permissive.
bye, Sumit
Hmm, that makes sense, so the plan is to switch directly to enforcing in 1.13 ?
On Mon, 2014-04-28 at 13:59 +0200, Jakub Hrozek wrote:
On Mon, Apr 28, 2014 at 01:51:42PM +0200, Sumit Bose wrote:
On Mon, Apr 28, 2014 at 01:28:00PM +0200, Jakub Hrozek wrote:
On Fri, Apr 18, 2014 at 12:56:27PM -0400, Yassir Elley wrote:
I agree. I have made the change.
Yassir.
Hi,
sorry I didn't get to continue the review sooner, but the patch is massive and I've been trying to get some dbus-related code to master last week. I hope you don't mind Yassir as you were on a vacation last week :)
In general the code is good, I only have some more suggestions about coding style or talloc hierarchy and one question about the default, see below.
From 3617c7b171a4d7090aa6e4c2673769686b4a5919 Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Mon, 20 Jan 2014 11:17:06 -0500 Subject: [PATCH] Implemented LDAP component of GPO-based access control
[...]
There are three supported values for this option:
<itemizedlist>
<listitem>
<para>
disabled: GPO-based access control rules
are neither evaluated nor enforced.
</para>
</listitem>
<listitem>
<para>
enforcing: GPO-based access control
rules are evaluated and enforced.
</para>
</listitem>
<listitem>
<para>
permissive: GPO-based access control
rules are evaluated, but not enforced.
Instead, a syslog message will be
emitted indicating that the user would
have been denied access if this option's
value were set to enforcing.
</para>
</listitem>
</itemizedlist>
</para>
<para>
Default: disabled
Is there a reason the default is disabled rather than permissive? Don't we want to identify the cases where a user would be locked out soon?
I think this was based on one of my earlier comments. If there are any issues in the GPO code the access validation might fail even if the default value is permissive.
That would be just a bug and we can tell users the workaround is to set it disabled.
Hmm, that makes sense, so the plan is to switch directly to enforcing in 1.13 ?
I would really like to have it in permissive by default on the first release this code goes in.
Simo.
----- Original Message -----
On Fri, Apr 18, 2014 at 12:56:27PM -0400, Yassir Elley wrote:
I agree. I have made the change.
Yassir.
Hi,
sorry I didn't get to continue the review sooner, but the patch is massive and I've been trying to get some dbus-related code to master last week. I hope you don't mind Yassir as you were on a vacation last week :)
No problem at all. Thanks for the review! I agree with your comments and will address them in my next patch. I have included answers to specific questions below.
In general the code is good, I only have some more suggestions about coding style or talloc hierarchy and one question about the default, see below.
From 3617c7b171a4d7090aa6e4c2673769686b4a5919 Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Mon, 20 Jan 2014 11:17:06 -0500 Subject: [PATCH] Implemented LDAP component of GPO-based access control
[...]
There are three supported values for this
option:
<itemizedlist>
<listitem>
<para>
disabled: GPO-based access control
rules
are neither evaluated nor
enforced.
</para>
</listitem>
<listitem>
<para>
enforcing: GPO-based access
control
rules are evaluated and enforced.
</para>
</listitem>
<listitem>
<para>
permissive: GPO-based access
control
rules are evaluated, but not
enforced.
Instead, a syslog message will be
emitted indicating that the user
would
have been denied access if this
option's
value were set to enforcing.
</para>
</listitem>
</itemizedlist>
</para>
<para>
Default: disabled
Is there a reason the default is disabled rather than permissive? Don't we want to identify the cases where a user would be locked out soon?
I agree that the initial default should be "permissive". My mistake.
</para>
</listitem>
</varlistentry>
<varlistentry> <term>dyndns_update (boolean)</term> <listitem> <para>
diff --git a/src/providers/ad/ad_access.c b/src/providers/ad/ad_access.c index cae075d4222bba3c05d2c09e26da9b19f4507cc1..cd51129f63bafb2d1d40a9a2880ea02c7617f0db 100644 --- a/src/providers/ad/ad_access.c +++ b/src/providers/ad/ad_access.c
[...]
static errno_t @@ -385,7 +420,26 @@ ad_access_recv(struct tevent_req *req) }
In general with tevent requests, the _recv() function should be the last one in the code, so when you read the code and find the recv() function, you know you can stop reading and the rest is either another request or helper functions. Here you have _done after _recv.
static void -ad_access_check_done(struct tevent_req *req); +ad_gpo_access_done(struct tevent_req *subreq) +{
- struct tevent_req *req;
- errno_t ret;
- req = tevent_req_callback_data(subreq, struct tevent_req);
- ret = ad_gpo_access_recv(subreq);
- talloc_zfree(subreq);
It would be nice to have a DEBUG message here, at least for failure.
- if (ret == EOK) {
tevent_req_done(req);
- } else {
tevent_req_error(req, ret);
- }
+} diff --git a/src/providers/ad/ad_gpo.c b/src/providers/ad/ad_gpo.c new file mode 100644 index 0000000000000000000000000000000000000000..935e1924905fb25461ebf57db2e42eb1e8348417 --- /dev/null +++ b/src/providers/ad/ad_gpo.c @@ -0,0 +1,2221 @@
[...]
+/*
- This function determines whether the input ACE includes 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 security_ace *ace,
bool *_included)
I wonder if this function could simply return a boolean, the caller ignores the return code anyway.
+{
- int i = 0;
- struct dom_sid ace_dom_sid;
- struct dom_sid user_dom_sid;
- struct dom_sid group_dom_sid;
- char buf[SID_MAX_LEN + 1];
- ace_dom_sid = ace->trustee;
- dom_sid_string_buf(&ace_dom_sid, buf, SID_MAX_LEN);
- if (!string_to_sid(&user_dom_sid, user_sid)) {
DEBUG(SSSDBG_OP_FAILURE, "string_to_sid failed\n");
return EINVAL;
- }
- if (dom_sid_equal(&ace_dom_sid, &user_dom_sid)) {
*_included = true;
return EOK;
- }
- for (i = 0; i < group_size; i++) {
if (!string_to_sid(&group_dom_sid, group_sids[i])) {
DEBUG(SSSDBG_OP_FAILURE, "string_to_sid failed\n");
return EINVAL;
}
if (dom_sid_equal(&ace_dom_sid, &group_dom_sid)) {
*_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.
- 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
Thanks for the comments, they are really helpful. What does 'M' stands for here, though?
This is taken straight from the spec. The 'M' stands for "access mask", which represents the set of access rights (e.g. RIGHT_GENERIC_READ, RIGHT_DS_CREATE_CHILD, RIGHT_DS_CONTROL_ACCESS, etc) 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 access right is identified by a GUID in the ACE's ObjectType. In our case, this is the GUID corresponding to ApplyGroupPolicy.
- 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.
- */
+static enum ace_eval_status ad_gpo_evaluate_ace(struct security_ace *ace,
const char *user_sid,
const char **group_sids,
int group_size)
[...]
+static errno_t +ad_gpo_filter_gpos_by_dacl(TALLOC_CTX *mem_ctx,
char *user,
struct sss_domain_info *domain,
struct gp_gpo **candidate_gpos,
int num_candidate_gpos,
struct gp_gpo ***_dacl_filtered_gpos,
int *_num_dacl_filtered_gpos)
+{
- TALLOC_CTX *tmp_ctx = NULL;
- int i = 0;
- int ret = 0;
- struct gp_gpo *candidate_gpo = NULL;
- struct security_descriptor *sd = NULL;
- struct security_acl *dacl = NULL;
- const char *user_sid = NULL;
- const char **group_sids = NULL;
- int group_size = 0;
- int gpo_dn_idx = 0;
- bool access_allowed = false;
- struct gp_gpo **dacl_filtered_gpos = NULL;
- tmp_ctx = talloc_new(NULL);
- if (tmp_ctx == NULL) {
ret = ENOMEM;
goto done;
- }
- ret = ad_gpo_get_sids(tmp_ctx, user, domain, &user_sid,
&group_sids, &group_size);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Unable to retrieve SIDs: [%d](%s)\n", ret, strerror(ret));
ret = ENOENT;
I wonder if ENOENT is the right error code, the information is not missing it just can't be processed, right? You can even create your own specific error codes if you find that the standard errno codes don't express the error well..
You are correct. I will address this in the next patch.
goto done;
- }
- dacl_filtered_gpos = talloc_array(tmp_ctx,
struct gp_gpo *,
num_candidate_gpos + 1);
[...]
- if (reply_count < 1) {
DEBUG(SSSDBG_OP_FAILURE, "No DN retrieved for policy target.\n");
ret = ENOENT;
goto done;
- } else if (reply_count > 1) {
DEBUG(SSSDBG_OP_FAILURE, "Multiple replies for policy target\n");
ret = ERR_INTERNAL;
goto done;
- } else if (reply == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "reply_count is 1, but reply is NULL\n");
ret = ERR_INTERNAL;
goto done;
- }
- /* reply[0] holds requested attributes of single reply */
- ret = sysdb_attrs_get_string(reply[0], AD_AT_DN, &target_dn);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_string failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- state->target_dn = talloc_strdup(state, target_dn);
- if (state->target_dn == NULL) {
ret = ENOMEM;
goto done;
- }
The target_dn here is already allocated on state, it's just part of reply, (state->reply->target_dn), I think you can just assign to the state->target_dn pointer or if you'd like to free reply later on, steal target dn onto state instead of duplicating the string. This issue is repeated in the code couple of times.
- ret = sysdb_attrs_get_uint32_t(reply[0], AD_AT_UAC, &uac);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_uint32_t failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- /* we only support computer policy targets, not users */
- if (!(uac & UAC_WORKSTATION_TRUST_ACCOUNT)) {
ret = EINVAL;
goto done;
- }
- state->target_dn = talloc_strdup(state, target_dn);
- if (state->target_dn == NULL) {
ret = ENOMEM;
goto done;
- }
- num_enabled = 0;
- ptr = raw_gplink_value;
[...]
- for (i = 0; i < gplink_count; i++) {
first = ptr + 1;
last = strchr(first, delim);
if (last == NULL) {
break;
}
*last = '\0';
last++;
dn = first;
if ( strncasecmp(dn, "LDAP://", 7)== 0 ) {
dn = dn + 7;
}
gplink_options = strchr(first, ';');
if (gplink_options == NULL) {
break;
Shouldn't we juts continue to the next item instead of breaking? Or does this condition mean that the end of the list was reached?
I think you are right. We should just continue to the next item.
}
*gplink_options = '\0';
gplink_options++;
gplink_number = strtouint32(gplink_options, NULL, 10);
I think you should check errno for failures after the strotouint32 call.
DEBUG(SSSDBG_TRACE_ALL,
"gplink_list[%d]: [%s; %d]\n", num_enabled, dn,
gplink_number);
if ((gplink_number == 1) || (gplink_number ==3)) {
/* ignore flag is set */
DEBUG(SSSDBG_TRACE_ALL, "ignored gpo skipped\n");
ptr = last;
continue;
}
if (allow_enforced_only && (gplink_number == 0)) {
/* unenforced flag is set; only enforced gpos allowed */
DEBUG(SSSDBG_TRACE_ALL, "unenforced gpo skipped\n");
ptr = last;
continue;
}
gplink_list[num_enabled] = talloc_zero(gplink_list, struct
gp_gplink);
if (gplink_list[num_enabled] == NULL) {
ret = ENOMEM;
goto done;
}
gplink_list[num_enabled]->gpo_dn =
talloc_strdup(gplink_list[num_enabled], dn);
if (gplink_list[num_enabled]->gpo_dn == NULL) {
ret = ENOMEM;
goto done;
}
if (gplink_number == 0) {
gplink_list[num_enabled]->enforced = 0;
num_enabled++;
} else if (gplink_number == 2) {
gplink_list[num_enabled]->enforced = 1;
num_enabled++;
} else {
ret = EINVAL;
goto done;
}
ptr = last;
- }
[...]
+ad_gpo_site_dn_retrieval_done(struct tevent_req *subreq) +{
- struct tevent_req *req;
- struct ad_gpo_process_som_state *state;
- int ret;
- int dp_error;
- int i = 0;
- size_t reply_count;
- struct sysdb_attrs **reply;
- const char *configNC;
- req = tevent_req_callback_data(subreq, struct tevent_req);
- state = tevent_req_data(req, struct ad_gpo_process_som_state);
Can you add a comment that would mention that this attribute should be read from the rootdse in the future and maybe even link to https://fedorahosted.org/sssd/ticket/2276 ?
Yes.
- ret = sdap_get_generic_recv(subreq, state,
&reply_count, &reply);
- talloc_zfree(subreq);
- if (ret != EOK) {
ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
/* TBD: handle (dp_error == DP_ERR_OFFLINE) case */
DEBUG(SSSDBG_OP_FAILURE,
"Unable to get configNC: [%d](%s)\n", ret, strerror(ret));
ret = ENOENT;
goto done;
- }
[...]
- /* note that space was allocated for site_dn when allocating som_list
*/
- state->som_list[state->num_soms] =
talloc_zero(state->som_list, struct gp_som);
- if (state->som_list[state->num_soms] == NULL) {
ret = ENOMEM;
goto done;
- }
- state->som_list[state->num_soms]->som_dn =
talloc_strdup(state->som_list[state->num_soms], state->site_dn);
I think talloc_steal can be used safely here, no need to strdup.
- if (state->som_list[state->num_soms]->som_dn == NULL) {
ret = ENOMEM;
goto done;
- }
- state->num_soms++;
- state->som_list[state->num_soms] = NULL;
- i = 0;
- while (state->som_list[i]) {
DEBUG(SSSDBG_TRACE_FUNC, "som_list[%d]->som_dn is %s\n", i,
state->som_list[i]->som_dn);
i++;
- }
- ret = ad_gpo_get_som_attrs_step(req);
- done:
- if (ret == EOK) {
tevent_req_done(req);
Can we even have EOK here? Looks like the functions either return EAGAIN or outright failure..
ad_gpo_get_som_attrs_step() returns EOK after all SOMs have been processed. There is a comment to that effect in the code.
- } else if (ret != EAGAIN) {
tevent_req_error(req, ret);
- }
+} +static errno_t +ad_gpo_get_som_attrs_step(struct tevent_req *req) +{
- const char *attrs[] = {AD_AT_GPLINK, AD_AT_GPOPTIONS, NULL};
- struct tevent_req *subreq;
- struct ad_gpo_process_som_state *state;
- state = tevent_req_data(req, struct ad_gpo_process_som_state);
- state->som_index++;
- struct gp_som *gp_som = state->som_list[state->som_index];
- /* gp_som is NULL only after all SOMs have been processed */
- if (gp_som == NULL) return EOK;
- char *som_dn = gp_som->som_dn;
- subreq = sdap_get_generic_send(state, state->ev, state->opts,
sdap_id_op_handle(state->sdap_op),
som_dn, LDAP_SCOPE_BASE,
"(objectclass=*)", attrs, NULL, 0,
state->timeout,
false);
- if (subreq == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send failed.\n");
return ENOMEM;
- }
- tevent_req_set_callback(subreq, ad_gpo_get_som_attrs_done, req);
- return EAGAIN;
+}
[...]
diff --git a/src/util/sss_ldap.h b/src/util/sss_ldap.h index f298b2fbb30cf1532f8e94504ffb83ef73880b81..083c89141987f38313906d7d7a9a003a74c10cef 100644 --- a/src/util/sss_ldap.h +++ b/src/util/sss_ldap.h @@ -55,6 +55,15 @@ int sss_ldap_get_diagnostic_msg(TALLOC_CTX *mem_ctx, #define LDAP_SERVER_ASQ_OID "1.2.840.113556.1.4.1504" #endif /* LDAP_SERVER_ASQ_OID */
+#ifndef LDAP_SERVER_SD_OID +#define LDAP_SERVER_SD_OID "1.2.840.113556.1.4.801" +#endif /* LDAP_SERVER_SD_OID */
+#define SECINFO_OWNER ( 0x00000001 ) +#define SECINFO_GROUP ( 0x00000002 ) +#define SECINFO_DACL ( 0x00000004 ) +#define SECINFO_SACL ( 0x00000008 )
Of all these constants, only SECINFO_DACL is used, is the rest included for posterity? Do these magic values come from some technet article or MSDN documentation? If so, it might be nice to add a link or a comment.
These four flags indicate which security descriptor parts to retrieve during a search. They are defined at http://msdn.microsoft.com/en-us/library/aa366987(v=vs.85).aspx. While we only use a single flag (SECINFO_DACL), I thought I would include all four flags for completeness. Should I remove the other three flags that are not being used?
int sss_ldap_control_create(const char *oid, int iscritical, struct berval *value, int dupval, LDAPControl **ctrlp); -- 1.8.5.3
In general, it's really nice work, thank you! _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
I will now work on a new patch to address the comments you have raised.
Regards, Yassir.
On Mon, Apr 28, 2014 at 03:13:36PM -0400, Yassir Elley wrote:
- done:
- if (ret == EOK) {
tevent_req_done(req);
Can we even have EOK here? Looks like the functions either return EAGAIN or outright failure..
ad_gpo_get_som_attrs_step() returns EOK after all SOMs have been processed. There is a comment to that effect in the code.
Ah, my bad, sorry. And thanks for explanation!
- } else if (ret != EAGAIN) {
tevent_req_error(req, ret);
- }
+} +static errno_t +ad_gpo_get_som_attrs_step(struct tevent_req *req) +{
- const char *attrs[] = {AD_AT_GPLINK, AD_AT_GPOPTIONS, NULL};
- struct tevent_req *subreq;
- struct ad_gpo_process_som_state *state;
- state = tevent_req_data(req, struct ad_gpo_process_som_state);
- state->som_index++;
- struct gp_som *gp_som = state->som_list[state->som_index];
- /* gp_som is NULL only after all SOMs have been processed */
- if (gp_som == NULL) return EOK;
- char *som_dn = gp_som->som_dn;
- subreq = sdap_get_generic_send(state, state->ev, state->opts,
sdap_id_op_handle(state->sdap_op),
som_dn, LDAP_SCOPE_BASE,
"(objectclass=*)", attrs, NULL, 0,
state->timeout,
false);
- if (subreq == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send failed.\n");
return ENOMEM;
- }
- tevent_req_set_callback(subreq, ad_gpo_get_som_attrs_done, req);
- return EAGAIN;
+}
[...]
diff --git a/src/util/sss_ldap.h b/src/util/sss_ldap.h index f298b2fbb30cf1532f8e94504ffb83ef73880b81..083c89141987f38313906d7d7a9a003a74c10cef 100644 --- a/src/util/sss_ldap.h +++ b/src/util/sss_ldap.h @@ -55,6 +55,15 @@ int sss_ldap_get_diagnostic_msg(TALLOC_CTX *mem_ctx, #define LDAP_SERVER_ASQ_OID "1.2.840.113556.1.4.1504" #endif /* LDAP_SERVER_ASQ_OID */
+#ifndef LDAP_SERVER_SD_OID +#define LDAP_SERVER_SD_OID "1.2.840.113556.1.4.801" +#endif /* LDAP_SERVER_SD_OID */
+#define SECINFO_OWNER ( 0x00000001 ) +#define SECINFO_GROUP ( 0x00000002 ) +#define SECINFO_DACL ( 0x00000004 ) +#define SECINFO_SACL ( 0x00000008 )
Of all these constants, only SECINFO_DACL is used, is the rest included for posterity? Do these magic values come from some technet article or MSDN documentation? If so, it might be nice to add a link or a comment.
These four flags indicate which security descriptor parts to retrieve during a search. They are defined at http://msdn.microsoft.com/en-us/library/aa366987(v=vs.85).aspx. While we only use a single flag (SECINFO_DACL), I thought I would include all four flags for completeness. Should I remove the other three flags that are not being used?
No, I agree it's better to include them all. If you're changing the patch anyway, can you put a comment above the constants?
int sss_ldap_control_create(const char *oid, int iscritical, struct berval *value, int dupval, LDAPControl **ctrlp); -- 1.8.5.3
In general, it's really nice work, thank you! _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
I will now work on a new patch to address the comments you have raised.
Thank you for the patience during review!
----- Original Message -----
----- Original Message -----
On Fri, Apr 18, 2014 at 12:56:27PM -0400, Yassir Elley wrote:
I agree. I have made the change.
Yassir.
Hi,
sorry I didn't get to continue the review sooner, but the patch is massive and I've been trying to get some dbus-related code to master last week. I hope you don't mind Yassir as you were on a vacation last week :)
No problem at all. Thanks for the review! I agree with your comments and will address them in my next patch. I have included answers to specific questions below.
In general the code is good, I only have some more suggestions about coding style or talloc hierarchy and one question about the default, see below.
From 3617c7b171a4d7090aa6e4c2673769686b4a5919 Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Mon, 20 Jan 2014 11:17:06 -0500 Subject: [PATCH] Implemented LDAP component of GPO-based access control
[...]
There are three supported values for this
option:
<itemizedlist>
<listitem>
<para>
disabled: GPO-based access
control rules
are neither evaluated nor
enforced.
</para>
</listitem>
<listitem>
<para>
enforcing: GPO-based access
control
rules are evaluated and
enforced.
</para>
</listitem>
<listitem>
<para>
permissive: GPO-based access
control
rules are evaluated, but not
enforced.
Instead, a syslog message will
be
emitted indicating that the user
would
have been denied access if this
option's
value were set to enforcing.
</para>
</listitem>
</itemizedlist>
</para>
<para>
Default: disabled
Is there a reason the default is disabled rather than permissive? Don't we want to identify the cases where a user would be locked out soon?
I agree that the initial default should be "permissive". My mistake.
</para>
</listitem>
</varlistentry>
<varlistentry> <term>dyndns_update (boolean)</term> <listitem> <para>
diff --git a/src/providers/ad/ad_access.c b/src/providers/ad/ad_access.c index cae075d4222bba3c05d2c09e26da9b19f4507cc1..cd51129f63bafb2d1d40a9a2880ea02c7617f0db 100644 --- a/src/providers/ad/ad_access.c +++ b/src/providers/ad/ad_access.c
[...]
static errno_t @@ -385,7 +420,26 @@ ad_access_recv(struct tevent_req *req) }
In general with tevent requests, the _recv() function should be the last one in the code, so when you read the code and find the recv() function, you know you can stop reading and the rest is either another request or helper functions. Here you have _done after _recv.
static void -ad_access_check_done(struct tevent_req *req); +ad_gpo_access_done(struct tevent_req *subreq) +{
- struct tevent_req *req;
- errno_t ret;
- req = tevent_req_callback_data(subreq, struct tevent_req);
- ret = ad_gpo_access_recv(subreq);
- talloc_zfree(subreq);
It would be nice to have a DEBUG message here, at least for failure.
- if (ret == EOK) {
tevent_req_done(req);
- } else {
tevent_req_error(req, ret);
- }
+} diff --git a/src/providers/ad/ad_gpo.c b/src/providers/ad/ad_gpo.c new file mode 100644 index 0000000000000000000000000000000000000000..935e1924905fb25461ebf57db2e42eb1e8348417 --- /dev/null +++ b/src/providers/ad/ad_gpo.c @@ -0,0 +1,2221 @@
[...]
+/*
- This function determines whether the input ACE includes 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 security_ace *ace,
bool *_included)
I wonder if this function could simply return a boolean, the caller ignores the return code anyway.
+{
- int i = 0;
- struct dom_sid ace_dom_sid;
- struct dom_sid user_dom_sid;
- struct dom_sid group_dom_sid;
- char buf[SID_MAX_LEN + 1];
- ace_dom_sid = ace->trustee;
- dom_sid_string_buf(&ace_dom_sid, buf, SID_MAX_LEN);
- if (!string_to_sid(&user_dom_sid, user_sid)) {
DEBUG(SSSDBG_OP_FAILURE, "string_to_sid failed\n");
return EINVAL;
- }
- if (dom_sid_equal(&ace_dom_sid, &user_dom_sid)) {
*_included = true;
return EOK;
- }
- for (i = 0; i < group_size; i++) {
if (!string_to_sid(&group_dom_sid, group_sids[i])) {
DEBUG(SSSDBG_OP_FAILURE, "string_to_sid failed\n");
return EINVAL;
}
if (dom_sid_equal(&ace_dom_sid, &group_dom_sid)) {
*_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.
- 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
Thanks for the comments, they are really helpful. What does 'M' stands for here, though?
This is taken straight from the spec. The 'M' stands for "access mask", which represents the set of access rights (e.g. RIGHT_GENERIC_READ, RIGHT_DS_CREATE_CHILD, RIGHT_DS_CONTROL_ACCESS, etc) 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 access right is identified by a GUID in the ACE's ObjectType. In our case, this is the GUID corresponding to ApplyGroupPolicy.
- 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.
- */
+static enum ace_eval_status ad_gpo_evaluate_ace(struct security_ace *ace,
const char *user_sid,
const char **group_sids,
int group_size)
[...]
+static errno_t +ad_gpo_filter_gpos_by_dacl(TALLOC_CTX *mem_ctx,
char *user,
struct sss_domain_info *domain,
struct gp_gpo **candidate_gpos,
int num_candidate_gpos,
struct gp_gpo ***_dacl_filtered_gpos,
int *_num_dacl_filtered_gpos)
+{
- TALLOC_CTX *tmp_ctx = NULL;
- int i = 0;
- int ret = 0;
- struct gp_gpo *candidate_gpo = NULL;
- struct security_descriptor *sd = NULL;
- struct security_acl *dacl = NULL;
- const char *user_sid = NULL;
- const char **group_sids = NULL;
- int group_size = 0;
- int gpo_dn_idx = 0;
- bool access_allowed = false;
- struct gp_gpo **dacl_filtered_gpos = NULL;
- tmp_ctx = talloc_new(NULL);
- if (tmp_ctx == NULL) {
ret = ENOMEM;
goto done;
- }
- ret = ad_gpo_get_sids(tmp_ctx, user, domain, &user_sid,
&group_sids, &group_size);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Unable to retrieve SIDs: [%d](%s)\n", ret,
strerror(ret));
ret = ENOENT;
I wonder if ENOENT is the right error code, the information is not missing it just can't be processed, right? You can even create your own specific error codes if you find that the standard errno codes don't express the error well..
You are correct. I will address this in the next patch.
goto done;
- }
- dacl_filtered_gpos = talloc_array(tmp_ctx,
struct gp_gpo *,
num_candidate_gpos + 1);
[...]
- if (reply_count < 1) {
DEBUG(SSSDBG_OP_FAILURE, "No DN retrieved for policy
target.\n");
ret = ENOENT;
goto done;
- } else if (reply_count > 1) {
DEBUG(SSSDBG_OP_FAILURE, "Multiple replies for policy
target\n");
ret = ERR_INTERNAL;
goto done;
- } else if (reply == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "reply_count is 1, but reply is
NULL\n");
ret = ERR_INTERNAL;
goto done;
- }
- /* reply[0] holds requested attributes of single reply */
- ret = sysdb_attrs_get_string(reply[0], AD_AT_DN, &target_dn);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_string failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- state->target_dn = talloc_strdup(state, target_dn);
- if (state->target_dn == NULL) {
ret = ENOMEM;
goto done;
- }
The target_dn here is already allocated on state, it's just part of reply, (state->reply->target_dn), I think you can just assign to the state->target_dn pointer or if you'd like to free reply later on, steal target dn onto state instead of duplicating the string. This issue is repeated in the code couple of times.
- ret = sysdb_attrs_get_uint32_t(reply[0], AD_AT_UAC, &uac);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_uint32_t failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- /* we only support computer policy targets, not users */
- if (!(uac & UAC_WORKSTATION_TRUST_ACCOUNT)) {
ret = EINVAL;
goto done;
- }
- state->target_dn = talloc_strdup(state, target_dn);
- if (state->target_dn == NULL) {
ret = ENOMEM;
goto done;
- }
- num_enabled = 0;
- ptr = raw_gplink_value;
[...]
- for (i = 0; i < gplink_count; i++) {
first = ptr + 1;
last = strchr(first, delim);
if (last == NULL) {
break;
}
*last = '\0';
last++;
dn = first;
if ( strncasecmp(dn, "LDAP://", 7)== 0 ) {
dn = dn + 7;
}
gplink_options = strchr(first, ';');
if (gplink_options == NULL) {
break;
Shouldn't we juts continue to the next item instead of breaking? Or does this condition mean that the end of the list was reached?
I think you are right. We should just continue to the next item.
}
*gplink_options = '\0';
gplink_options++;
gplink_number = strtouint32(gplink_options, NULL, 10);
I think you should check errno for failures after the strotouint32 call.
DEBUG(SSSDBG_TRACE_ALL,
"gplink_list[%d]: [%s; %d]\n", num_enabled, dn,
gplink_number);
if ((gplink_number == 1) || (gplink_number ==3)) {
/* ignore flag is set */
DEBUG(SSSDBG_TRACE_ALL, "ignored gpo skipped\n");
ptr = last;
continue;
}
if (allow_enforced_only && (gplink_number == 0)) {
/* unenforced flag is set; only enforced gpos allowed */
DEBUG(SSSDBG_TRACE_ALL, "unenforced gpo skipped\n");
ptr = last;
continue;
}
gplink_list[num_enabled] = talloc_zero(gplink_list, struct
gp_gplink);
if (gplink_list[num_enabled] == NULL) {
ret = ENOMEM;
goto done;
}
gplink_list[num_enabled]->gpo_dn =
talloc_strdup(gplink_list[num_enabled], dn);
if (gplink_list[num_enabled]->gpo_dn == NULL) {
ret = ENOMEM;
goto done;
}
if (gplink_number == 0) {
gplink_list[num_enabled]->enforced = 0;
num_enabled++;
} else if (gplink_number == 2) {
gplink_list[num_enabled]->enforced = 1;
num_enabled++;
} else {
ret = EINVAL;
goto done;
}
ptr = last;
- }
[...]
+ad_gpo_site_dn_retrieval_done(struct tevent_req *subreq) +{
- struct tevent_req *req;
- struct ad_gpo_process_som_state *state;
- int ret;
- int dp_error;
- int i = 0;
- size_t reply_count;
- struct sysdb_attrs **reply;
- const char *configNC;
- req = tevent_req_callback_data(subreq, struct tevent_req);
- state = tevent_req_data(req, struct ad_gpo_process_som_state);
Can you add a comment that would mention that this attribute should be read from the rootdse in the future and maybe even link to https://fedorahosted.org/sssd/ticket/2276 ?
Yes.
- ret = sdap_get_generic_recv(subreq, state,
&reply_count, &reply);
- talloc_zfree(subreq);
- if (ret != EOK) {
ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
/* TBD: handle (dp_error == DP_ERR_OFFLINE) case */
DEBUG(SSSDBG_OP_FAILURE,
"Unable to get configNC: [%d](%s)\n", ret, strerror(ret));
ret = ENOENT;
goto done;
- }
[...]
- /* note that space was allocated for site_dn when allocating
som_list */
- state->som_list[state->num_soms] =
talloc_zero(state->som_list, struct gp_som);
- if (state->som_list[state->num_soms] == NULL) {
ret = ENOMEM;
goto done;
- }
- state->som_list[state->num_soms]->som_dn =
talloc_strdup(state->som_list[state->num_soms], state->site_dn);
I think talloc_steal can be used safely here, no need to strdup.
- if (state->som_list[state->num_soms]->som_dn == NULL) {
ret = ENOMEM;
goto done;
- }
- state->num_soms++;
- state->som_list[state->num_soms] = NULL;
- i = 0;
- while (state->som_list[i]) {
DEBUG(SSSDBG_TRACE_FUNC, "som_list[%d]->som_dn is %s\n", i,
state->som_list[i]->som_dn);
i++;
- }
- ret = ad_gpo_get_som_attrs_step(req);
- done:
- if (ret == EOK) {
tevent_req_done(req);
Can we even have EOK here? Looks like the functions either return EAGAIN or outright failure..
ad_gpo_get_som_attrs_step() returns EOK after all SOMs have been processed. There is a comment to that effect in the code.
- } else if (ret != EAGAIN) {
tevent_req_error(req, ret);
- }
+} +static errno_t +ad_gpo_get_som_attrs_step(struct tevent_req *req) +{
- const char *attrs[] = {AD_AT_GPLINK, AD_AT_GPOPTIONS, NULL};
- struct tevent_req *subreq;
- struct ad_gpo_process_som_state *state;
- state = tevent_req_data(req, struct ad_gpo_process_som_state);
- state->som_index++;
- struct gp_som *gp_som = state->som_list[state->som_index];
- /* gp_som is NULL only after all SOMs have been processed */
- if (gp_som == NULL) return EOK;
- char *som_dn = gp_som->som_dn;
- subreq = sdap_get_generic_send(state, state->ev, state->opts,
sdap_id_op_handle(state->sdap_op),
som_dn, LDAP_SCOPE_BASE,
"(objectclass=*)", attrs, NULL, 0,
state->timeout,
false);
- if (subreq == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send failed.\n");
return ENOMEM;
- }
- tevent_req_set_callback(subreq, ad_gpo_get_som_attrs_done, req);
- return EAGAIN;
+}
[...]
diff --git a/src/util/sss_ldap.h b/src/util/sss_ldap.h index f298b2fbb30cf1532f8e94504ffb83ef73880b81..083c89141987f38313906d7d7a9a003a74c10cef 100644 --- a/src/util/sss_ldap.h +++ b/src/util/sss_ldap.h @@ -55,6 +55,15 @@ int sss_ldap_get_diagnostic_msg(TALLOC_CTX *mem_ctx, #define LDAP_SERVER_ASQ_OID "1.2.840.113556.1.4.1504" #endif /* LDAP_SERVER_ASQ_OID */
+#ifndef LDAP_SERVER_SD_OID +#define LDAP_SERVER_SD_OID "1.2.840.113556.1.4.801" +#endif /* LDAP_SERVER_SD_OID */
+#define SECINFO_OWNER ( 0x00000001 ) +#define SECINFO_GROUP ( 0x00000002 ) +#define SECINFO_DACL ( 0x00000004 ) +#define SECINFO_SACL ( 0x00000008 )
Of all these constants, only SECINFO_DACL is used, is the rest included for posterity? Do these magic values come from some technet article or MSDN documentation? If so, it might be nice to add a link or a comment.
These four flags indicate which security descriptor parts to retrieve during a search. They are defined at http://msdn.microsoft.com/en-us/library/aa366987(v=vs.85).aspx. While we only use a single flag (SECINFO_DACL), I thought I would include all four flags for completeness. Should I remove the other three flags that are not being used?
int sss_ldap_control_create(const char *oid, int iscritical, struct berval *value, int dupval, LDAPControl **ctrlp); -- 1.8.5.3
In general, it's really nice work, thank you! _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
I will now work on a new patch to address the comments you have raised.
Regards, Yassir. _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
Hi Jakub,
I have attached a revised cumulative patch that aims to address the review comments.
Additionally, I made the following changes en masse: * changed talloc_strdup() to talloc_steal() when applicable (i.e. when dealing with a talloc context) * changed all occurrences of strerror() to sss_strerror() in order to use sssd-specific error strings
Thanks, Yassir.
On (29/04/14 19:22), Yassir Elley wrote:
Hi Jakub,
I have attached a revised cumulative patch that aims to address the review comments.
Additionally, I made the following changes en masse:
- changed talloc_strdup() to talloc_steal() when applicable (i.e. when dealing with a talloc context)
- changed all occurrences of strerror() to sss_strerror() in order to use sssd-specific error strings
Thanks, Yassir.
From 5fd5fec3d92daa38e324195de01f8f596b03a0d7 Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Mon, 20 Jan 2014 11:17:06 -0500 Subject: [PATCH] Implemented LDAP component of GPO-based access control
There were same warnings in your patch. warning: assignment discards 'const' qualifier from pointer target type
Attached diff fixes some of them.
There is also another warning. src/providers/ad/ad_gpo.c: In function 'ad_gpo_get_gpo_attrs_done': sssd/src/providers/ad/ad_gpo.c:2131:19: error: assignment discards 'const' qualifier from pointer target type [-Werror] file_sys_path = talloc_steal(gp_gpo, raw_file_sys_path); ^
There should be strdup, because file_sys_path is modified in function ad_gpo_convert_to_smb_uri.
LS
----- Original Message -----
On (29/04/14 19:22), Yassir Elley wrote:
Hi Jakub,
I have attached a revised cumulative patch that aims to address the review comments.
Additionally, I made the following changes en masse:
- changed talloc_strdup() to talloc_steal() when applicable (i.e. when
dealing with a talloc context)
- changed all occurrences of strerror() to sss_strerror() in order to use
sssd-specific error strings
Thanks, Yassir.
From 5fd5fec3d92daa38e324195de01f8f596b03a0d7 Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Mon, 20 Jan 2014 11:17:06 -0500 Subject: [PATCH] Implemented LDAP component of GPO-based access control
There were same warnings in your patch. warning: assignment discards 'const' qualifier from pointer target type
Attached diff fixes some of them.
There is also another warning. src/providers/ad/ad_gpo.c: In function 'ad_gpo_get_gpo_attrs_done': sssd/src/providers/ad/ad_gpo.c:2131:19: error: assignment discards 'const' qualifier from pointer target type [-Werror] file_sys_path = talloc_steal(gp_gpo, raw_file_sys_path); ^
There should be strdup, because file_sys_path is modified in function ad_gpo_convert_to_smb_uri.
LS
sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
Hi Lukas,
Thanks for catching this. I sometimes compile, see the warnings, and think I'll get back to fixing them later (after I install and test code). However, if I don't change the code and want to see the warnings again (since they have scrolled off the screen by this point), a subsequent compile doesn't show the warnings. I guess I should fix them right when I see them, rather than waiting.
My revised cumulative patch is attached. In addition to your suggestions, I have added the "const" qualifier to a few more places.
Thanks, Yassir.
On Tue, Apr 29, 2014 at 07:22:37PM -0400, Yassir Elley wrote:
----- Original Message -----
----- Original Message -----
On Fri, Apr 18, 2014 at 12:56:27PM -0400, Yassir Elley wrote:
I agree. I have made the change.
Yassir.
Hi,
sorry I didn't get to continue the review sooner, but the patch is massive and I've been trying to get some dbus-related code to master last week. I hope you don't mind Yassir as you were on a vacation last week :)
No problem at all. Thanks for the review! I agree with your comments and will address them in my next patch. I have included answers to specific questions below.
In general the code is good, I only have some more suggestions about coding style or talloc hierarchy and one question about the default, see below.
From 3617c7b171a4d7090aa6e4c2673769686b4a5919 Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Mon, 20 Jan 2014 11:17:06 -0500 Subject: [PATCH] Implemented LDAP component of GPO-based access control
[...]
There are three supported values for this
option:
<itemizedlist>
<listitem>
<para>
disabled: GPO-based access
control rules
are neither evaluated nor
enforced.
</para>
</listitem>
<listitem>
<para>
enforcing: GPO-based access
control
rules are evaluated and
enforced.
</para>
</listitem>
<listitem>
<para>
permissive: GPO-based access
control
rules are evaluated, but not
enforced.
Instead, a syslog message will
be
emitted indicating that the user
would
have been denied access if this
option's
value were set to enforcing.
</para>
</listitem>
</itemizedlist>
</para>
<para>
Default: disabled
Is there a reason the default is disabled rather than permissive? Don't we want to identify the cases where a user would be locked out soon?
I agree that the initial default should be "permissive". My mistake.
</para>
</listitem>
</varlistentry>
<varlistentry> <term>dyndns_update (boolean)</term> <listitem> <para>
diff --git a/src/providers/ad/ad_access.c b/src/providers/ad/ad_access.c index cae075d4222bba3c05d2c09e26da9b19f4507cc1..cd51129f63bafb2d1d40a9a2880ea02c7617f0db 100644 --- a/src/providers/ad/ad_access.c +++ b/src/providers/ad/ad_access.c
[...]
static errno_t @@ -385,7 +420,26 @@ ad_access_recv(struct tevent_req *req) }
In general with tevent requests, the _recv() function should be the last one in the code, so when you read the code and find the recv() function, you know you can stop reading and the rest is either another request or helper functions. Here you have _done after _recv.
static void -ad_access_check_done(struct tevent_req *req); +ad_gpo_access_done(struct tevent_req *subreq) +{
- struct tevent_req *req;
- errno_t ret;
- req = tevent_req_callback_data(subreq, struct tevent_req);
- ret = ad_gpo_access_recv(subreq);
- talloc_zfree(subreq);
It would be nice to have a DEBUG message here, at least for failure.
- if (ret == EOK) {
tevent_req_done(req);
- } else {
tevent_req_error(req, ret);
- }
+} diff --git a/src/providers/ad/ad_gpo.c b/src/providers/ad/ad_gpo.c new file mode 100644 index 0000000000000000000000000000000000000000..935e1924905fb25461ebf57db2e42eb1e8348417 --- /dev/null +++ b/src/providers/ad/ad_gpo.c @@ -0,0 +1,2221 @@
[...]
+/*
- This function determines whether the input ACE includes 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 security_ace *ace,
bool *_included)
I wonder if this function could simply return a boolean, the caller ignores the return code anyway.
+{
- int i = 0;
- struct dom_sid ace_dom_sid;
- struct dom_sid user_dom_sid;
- struct dom_sid group_dom_sid;
- char buf[SID_MAX_LEN + 1];
- ace_dom_sid = ace->trustee;
- dom_sid_string_buf(&ace_dom_sid, buf, SID_MAX_LEN);
- if (!string_to_sid(&user_dom_sid, user_sid)) {
DEBUG(SSSDBG_OP_FAILURE, "string_to_sid failed\n");
return EINVAL;
- }
- if (dom_sid_equal(&ace_dom_sid, &user_dom_sid)) {
*_included = true;
return EOK;
- }
- for (i = 0; i < group_size; i++) {
if (!string_to_sid(&group_dom_sid, group_sids[i])) {
DEBUG(SSSDBG_OP_FAILURE, "string_to_sid failed\n");
return EINVAL;
}
if (dom_sid_equal(&ace_dom_sid, &group_dom_sid)) {
*_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.
- 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
Thanks for the comments, they are really helpful. What does 'M' stands for here, though?
This is taken straight from the spec. The 'M' stands for "access mask", which represents the set of access rights (e.g. RIGHT_GENERIC_READ, RIGHT_DS_CREATE_CHILD, RIGHT_DS_CONTROL_ACCESS, etc) 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 access right is identified by a GUID in the ACE's ObjectType. In our case, this is the GUID corresponding to ApplyGroupPolicy.
- 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.
- */
+static enum ace_eval_status ad_gpo_evaluate_ace(struct security_ace *ace,
const char *user_sid,
const char **group_sids,
int group_size)
[...]
+static errno_t +ad_gpo_filter_gpos_by_dacl(TALLOC_CTX *mem_ctx,
char *user,
struct sss_domain_info *domain,
struct gp_gpo **candidate_gpos,
int num_candidate_gpos,
struct gp_gpo ***_dacl_filtered_gpos,
int *_num_dacl_filtered_gpos)
+{
- TALLOC_CTX *tmp_ctx = NULL;
- int i = 0;
- int ret = 0;
- struct gp_gpo *candidate_gpo = NULL;
- struct security_descriptor *sd = NULL;
- struct security_acl *dacl = NULL;
- const char *user_sid = NULL;
- const char **group_sids = NULL;
- int group_size = 0;
- int gpo_dn_idx = 0;
- bool access_allowed = false;
- struct gp_gpo **dacl_filtered_gpos = NULL;
- tmp_ctx = talloc_new(NULL);
- if (tmp_ctx == NULL) {
ret = ENOMEM;
goto done;
- }
- ret = ad_gpo_get_sids(tmp_ctx, user, domain, &user_sid,
&group_sids, &group_size);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Unable to retrieve SIDs: [%d](%s)\n", ret,
strerror(ret));
ret = ENOENT;
I wonder if ENOENT is the right error code, the information is not missing it just can't be processed, right? You can even create your own specific error codes if you find that the standard errno codes don't express the error well..
You are correct. I will address this in the next patch.
goto done;
- }
- dacl_filtered_gpos = talloc_array(tmp_ctx,
struct gp_gpo *,
num_candidate_gpos + 1);
[...]
- if (reply_count < 1) {
DEBUG(SSSDBG_OP_FAILURE, "No DN retrieved for policy
target.\n");
ret = ENOENT;
goto done;
- } else if (reply_count > 1) {
DEBUG(SSSDBG_OP_FAILURE, "Multiple replies for policy
target\n");
ret = ERR_INTERNAL;
goto done;
- } else if (reply == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "reply_count is 1, but reply is
NULL\n");
ret = ERR_INTERNAL;
goto done;
- }
- /* reply[0] holds requested attributes of single reply */
- ret = sysdb_attrs_get_string(reply[0], AD_AT_DN, &target_dn);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_string failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- state->target_dn = talloc_strdup(state, target_dn);
- if (state->target_dn == NULL) {
ret = ENOMEM;
goto done;
- }
The target_dn here is already allocated on state, it's just part of reply, (state->reply->target_dn), I think you can just assign to the state->target_dn pointer or if you'd like to free reply later on, steal target dn onto state instead of duplicating the string. This issue is repeated in the code couple of times.
- ret = sysdb_attrs_get_uint32_t(reply[0], AD_AT_UAC, &uac);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_uint32_t failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- /* we only support computer policy targets, not users */
- if (!(uac & UAC_WORKSTATION_TRUST_ACCOUNT)) {
ret = EINVAL;
goto done;
- }
- state->target_dn = talloc_strdup(state, target_dn);
- if (state->target_dn == NULL) {
ret = ENOMEM;
goto done;
- }
- num_enabled = 0;
- ptr = raw_gplink_value;
[...]
- for (i = 0; i < gplink_count; i++) {
first = ptr + 1;
last = strchr(first, delim);
if (last == NULL) {
break;
}
*last = '\0';
last++;
dn = first;
if ( strncasecmp(dn, "LDAP://", 7)== 0 ) {
dn = dn + 7;
}
gplink_options = strchr(first, ';');
if (gplink_options == NULL) {
break;
Shouldn't we juts continue to the next item instead of breaking? Or does this condition mean that the end of the list was reached?
I think you are right. We should just continue to the next item.
}
*gplink_options = '\0';
gplink_options++;
gplink_number = strtouint32(gplink_options, NULL, 10);
I think you should check errno for failures after the strotouint32 call.
DEBUG(SSSDBG_TRACE_ALL,
"gplink_list[%d]: [%s; %d]\n", num_enabled, dn,
gplink_number);
if ((gplink_number == 1) || (gplink_number ==3)) {
/* ignore flag is set */
DEBUG(SSSDBG_TRACE_ALL, "ignored gpo skipped\n");
ptr = last;
continue;
}
if (allow_enforced_only && (gplink_number == 0)) {
/* unenforced flag is set; only enforced gpos allowed */
DEBUG(SSSDBG_TRACE_ALL, "unenforced gpo skipped\n");
ptr = last;
continue;
}
gplink_list[num_enabled] = talloc_zero(gplink_list, struct
gp_gplink);
if (gplink_list[num_enabled] == NULL) {
ret = ENOMEM;
goto done;
}
gplink_list[num_enabled]->gpo_dn =
talloc_strdup(gplink_list[num_enabled], dn);
if (gplink_list[num_enabled]->gpo_dn == NULL) {
ret = ENOMEM;
goto done;
}
if (gplink_number == 0) {
gplink_list[num_enabled]->enforced = 0;
num_enabled++;
} else if (gplink_number == 2) {
gplink_list[num_enabled]->enforced = 1;
num_enabled++;
} else {
ret = EINVAL;
goto done;
}
ptr = last;
- }
[...]
+ad_gpo_site_dn_retrieval_done(struct tevent_req *subreq) +{
- struct tevent_req *req;
- struct ad_gpo_process_som_state *state;
- int ret;
- int dp_error;
- int i = 0;
- size_t reply_count;
- struct sysdb_attrs **reply;
- const char *configNC;
- req = tevent_req_callback_data(subreq, struct tevent_req);
- state = tevent_req_data(req, struct ad_gpo_process_som_state);
Can you add a comment that would mention that this attribute should be read from the rootdse in the future and maybe even link to https://fedorahosted.org/sssd/ticket/2276 ?
Yes.
- ret = sdap_get_generic_recv(subreq, state,
&reply_count, &reply);
- talloc_zfree(subreq);
- if (ret != EOK) {
ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
/* TBD: handle (dp_error == DP_ERR_OFFLINE) case */
DEBUG(SSSDBG_OP_FAILURE,
"Unable to get configNC: [%d](%s)\n", ret, strerror(ret));
ret = ENOENT;
goto done;
- }
[...]
- /* note that space was allocated for site_dn when allocating
som_list */
- state->som_list[state->num_soms] =
talloc_zero(state->som_list, struct gp_som);
- if (state->som_list[state->num_soms] == NULL) {
ret = ENOMEM;
goto done;
- }
- state->som_list[state->num_soms]->som_dn =
talloc_strdup(state->som_list[state->num_soms], state->site_dn);
I think talloc_steal can be used safely here, no need to strdup.
- if (state->som_list[state->num_soms]->som_dn == NULL) {
ret = ENOMEM;
goto done;
- }
- state->num_soms++;
- state->som_list[state->num_soms] = NULL;
- i = 0;
- while (state->som_list[i]) {
DEBUG(SSSDBG_TRACE_FUNC, "som_list[%d]->som_dn is %s\n", i,
state->som_list[i]->som_dn);
i++;
- }
- ret = ad_gpo_get_som_attrs_step(req);
- done:
- if (ret == EOK) {
tevent_req_done(req);
Can we even have EOK here? Looks like the functions either return EAGAIN or outright failure..
ad_gpo_get_som_attrs_step() returns EOK after all SOMs have been processed. There is a comment to that effect in the code.
- } else if (ret != EAGAIN) {
tevent_req_error(req, ret);
- }
+} +static errno_t +ad_gpo_get_som_attrs_step(struct tevent_req *req) +{
- const char *attrs[] = {AD_AT_GPLINK, AD_AT_GPOPTIONS, NULL};
- struct tevent_req *subreq;
- struct ad_gpo_process_som_state *state;
- state = tevent_req_data(req, struct ad_gpo_process_som_state);
- state->som_index++;
- struct gp_som *gp_som = state->som_list[state->som_index];
- /* gp_som is NULL only after all SOMs have been processed */
- if (gp_som == NULL) return EOK;
- char *som_dn = gp_som->som_dn;
- subreq = sdap_get_generic_send(state, state->ev, state->opts,
sdap_id_op_handle(state->sdap_op),
som_dn, LDAP_SCOPE_BASE,
"(objectclass=*)", attrs, NULL, 0,
state->timeout,
false);
- if (subreq == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send failed.\n");
return ENOMEM;
- }
- tevent_req_set_callback(subreq, ad_gpo_get_som_attrs_done, req);
- return EAGAIN;
+}
[...]
diff --git a/src/util/sss_ldap.h b/src/util/sss_ldap.h index f298b2fbb30cf1532f8e94504ffb83ef73880b81..083c89141987f38313906d7d7a9a003a74c10cef 100644 --- a/src/util/sss_ldap.h +++ b/src/util/sss_ldap.h @@ -55,6 +55,15 @@ int sss_ldap_get_diagnostic_msg(TALLOC_CTX *mem_ctx, #define LDAP_SERVER_ASQ_OID "1.2.840.113556.1.4.1504" #endif /* LDAP_SERVER_ASQ_OID */
+#ifndef LDAP_SERVER_SD_OID +#define LDAP_SERVER_SD_OID "1.2.840.113556.1.4.801" +#endif /* LDAP_SERVER_SD_OID */
+#define SECINFO_OWNER ( 0x00000001 ) +#define SECINFO_GROUP ( 0x00000002 ) +#define SECINFO_DACL ( 0x00000004 ) +#define SECINFO_SACL ( 0x00000008 )
Of all these constants, only SECINFO_DACL is used, is the rest included for posterity? Do these magic values come from some technet article or MSDN documentation? If so, it might be nice to add a link or a comment.
These four flags indicate which security descriptor parts to retrieve during a search. They are defined at http://msdn.microsoft.com/en-us/library/aa366987(v=vs.85).aspx. While we only use a single flag (SECINFO_DACL), I thought I would include all four flags for completeness. Should I remove the other three flags that are not being used?
int sss_ldap_control_create(const char *oid, int iscritical, struct berval *value, int dupval, LDAPControl **ctrlp); -- 1.8.5.3
In general, it's really nice work, thank you! _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
I will now work on a new patch to address the comments you have raised.
Regards, Yassir. _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
Hi Jakub,
I have attached a revised cumulative patch that aims to address the review comments.
Additionally, I made the following changes en masse:
- changed talloc_strdup() to talloc_steal() when applicable (i.e. when dealing with a talloc context)
- changed all occurrences of strerror() to sss_strerror() in order to use sssd-specific error strings
Thanks, Yassir.
Hi,
I have some more comments, sorry I haven't caught them earlier. I ran the patch through Coverity and it uncovered several warnings, see below:
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function 'ad_gpo_target_dn_retrieval_done' sssd-1.11.90/src/providers/ad/ad_gpo.c:711:22: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # state->target_dn = talloc_steal(state, target_dn); # ^
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function 'ad_gpo_parent_dn' sssd-1.11.90/src/providers/ad/ad_gpo.c:902:17: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # *_parent_dn = talloc_steal(mem_ctx, p); # ^
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1053: cond_notnull: Condition "ptr = __coverity_strchr(ptr, 93)", taking true branch. Now the value of "ptr" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: dead_error_line: Execution cannot reach this statement "break;".
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1768: cond_notnull: Condition "ptr = __coverity_strchr(ptr, 92)", taking true branch. Now the value of "ptr" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: dead_error_line: Execution cannot reach this statement "break;".
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1812: cond_notnull: Condition "raw_machine_ext_names_value == NULL", taking false branch. Now the value of "raw_machine_ext_names_value" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1824: assignment: Assigning: "ptr" = "raw_machine_ext_names_value". sssd-1.11.90/src/providers/ad/ad_gpo.c:1825: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1825: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1826: dead_error_begin: Execution cannot reach this statement "ret = 12;".
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1830: cond_notnull: Condition "ptr = __coverity_strchr(ptr, 93)", taking true branch. Now the value of "ptr" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: dead_error_line: Execution cannot reach this statement "break;".
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function 'ad_gpo_get_gpo_attrs_done' sssd-1.11.90/src/providers/ad/ad_gpo.c:2091:22: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # gp_gpo->gpo_guid = talloc_steal(gp_gpo, gpo_guid); # ^
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c:2110:30: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # gp_gpo->gpo_display_name = talloc_steal(gp_gpo, gpo_display_name); # ^
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c:2131:19: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # file_sys_path = talloc_steal(gp_gpo, raw_file_sys_path); # ^
Also, when I set ad_gpo_access_control=permissive and tried to log in as Administrator, I was kicked out. I thought that permissive setting would ignore the errors, but maybe I stumbled upon something unexpected?
The relevant domain log messages are: (Tue May 6 14:56:15 2014) [sssd[be[WIN.EXAMPLE.COM]]] [ad_gpo_connect_done] (0x0400): sam_account_name is adclient.win.example.com$ (Tue May 6 14:56:15 2014) [sssd[be[WIN.EXAMPLE.COM]]] [sdap_get_generic_ext_step] (0x0400): calling ldap_search_ext with [(&(objectclass=user)(sAMAccountName=adclient.win.example.com$))][dc=win,dc=example,dc=com]. (Tue May 6 14:56:15 2014) [sssd[be[WIN.EXAMPLE.COM]]] [sdap_get_generic_ext_step] (0x1000): Requesting attrs: [distinguishedName] (Tue May 6 14:56:15 2014) [sssd[be[WIN.EXAMPLE.COM]]] [sdap_get_generic_ext_step] (0x1000): Requesting attrs: [userAccountControl] (Tue May 6 14:56:15 2014) [sssd[be[WIN.EXAMPLE.COM]]] [sdap_get_generic_ext_step] (0x2000): ldap_search_ext called, msgid = 18 (Tue May 6 14:56:15 2014) [sssd[be[WIN.EXAMPLE.COM]]] [sdap_process_result] (0x2000): Trace: sh[0xfa31d0], connected[1], ops[0xf53520], ldap[0xf67d30] (Tue May 6 14:56:15 2014) [sssd[be[WIN.EXAMPLE.COM]]] [sdap_process_message] (0x4000): Message type: [LDAP_RES_SEARCH_REFERENCE] (Tue May 6 14:56:15 2014) [sssd[be[WIN.EXAMPLE.COM]]] [sdap_process_result] (0x2000): Trace: sh[0xfa31d0], connected[1], ops[0xf53520], ldap[0xf67d30] (Tue May 6 14:56:15 2014) [sssd[be[WIN.EXAMPLE.COM]]] [sdap_process_message] (0x4000): Message type: [LDAP_RES_SEARCH_REFERENCE] (Tue May 6 14:56:15 2014) [sssd[be[WIN.EXAMPLE.COM]]] [sdap_process_result] (0x2000): Trace: sh[0xfa31d0], connected[1], ops[0xf53520], ldap[0xf67d30] (Tue May 6 14:56:15 2014) [sssd[be[WIN.EXAMPLE.COM]]] [sdap_process_message] (0x4000): Message type: [LDAP_RES_SEARCH_REFERENCE] (Tue May 6 14:56:15 2014) [sssd[be[WIN.EXAMPLE.COM]]] [sdap_process_result] (0x2000): Trace: sh[0xfa31d0], connected[1], ops[0xf53520], ldap[0xf67d30] (Tue May 6 14:56:15 2014) [sssd[be[WIN.EXAMPLE.COM]]] [sdap_process_message] (0x4000): Message type: [LDAP_RES_SEARCH_REFERENCE] (Tue May 6 14:56:15 2014) [sssd[be[WIN.EXAMPLE.COM]]] [sdap_process_result] (0x2000): Trace: sh[0xfa31d0], connected[1], ops[0xf53520], ldap[0xf67d30] (Tue May 6 14:56:15 2014) [sssd[be[WIN.EXAMPLE.COM]]] [sdap_process_message] (0x4000): Message type: [LDAP_RES_SEARCH_RESULT] (Tue May 6 14:56:15 2014) [sssd[be[WIN.EXAMPLE.COM]]] [sdap_get_generic_ext_done] (0x0400): Search result: Success(0), no errmsg set (Tue May 6 14:56:15 2014) [sssd[be[WIN.EXAMPLE.COM]]] [ad_gpo_target_dn_retrieval_done] (0x0040): No DN retrieved for policy target. (Tue May 6 14:56:15 2014) [sssd[be[WIN.EXAMPLE.COM]]] [sdap_id_op_destroy] (0x4000): releasing operation connection (Tue May 6 14:56:15 2014) [sssd[be[WIN.EXAMPLE.COM]]] [ad_gpo_access_done] (0x0040): GPO-based access control failed. (Tue May 6 14:56:15 2014) [sssd[be[WIN.EXAMPLE.COM]]] [be_pam_handler_callback] (0x0100): Backend returned: (3, 4, No such file or directory) [Internal Error (System error)]
----- Original Message -----
On Tue, Apr 29, 2014 at 07:22:37PM -0400, Yassir Elley wrote:
----- Original Message -----
----- Original Message -----
On Fri, Apr 18, 2014 at 12:56:27PM -0400, Yassir Elley wrote:
I agree. I have made the change.
Yassir.
Hi,
sorry I didn't get to continue the review sooner, but the patch is massive and I've been trying to get some dbus-related code to master last week. I hope you don't mind Yassir as you were on a vacation last week :)
No problem at all. Thanks for the review! I agree with your comments and will address them in my next patch. I have included answers to specific questions below.
In general the code is good, I only have some more suggestions about coding style or talloc hierarchy and one question about the default, see below.
From 3617c7b171a4d7090aa6e4c2673769686b4a5919 Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Mon, 20 Jan 2014 11:17:06 -0500 Subject: [PATCH] Implemented LDAP component of GPO-based access control
[...]
There are three supported values for
this option:
<itemizedlist>
<listitem>
<para>
disabled: GPO-based access
control rules
are neither evaluated nor
enforced.
</para>
</listitem>
<listitem>
<para>
enforcing: GPO-based access
control
rules are evaluated and
enforced.
</para>
</listitem>
<listitem>
<para>
permissive: GPO-based access
control
rules are evaluated, but not
enforced.
Instead, a syslog message
will be
emitted indicating that the
user would
have been denied access if
this option's
value were set to enforcing.
</para>
</listitem>
</itemizedlist>
</para>
<para>
Default: disabled
Is there a reason the default is disabled rather than permissive? Don't we want to identify the cases where a user would be locked out soon?
I agree that the initial default should be "permissive". My mistake.
</para>
</listitem>
</varlistentry>
<varlistentry> <term>dyndns_update (boolean)</term> <listitem> <para>
diff --git a/src/providers/ad/ad_access.c b/src/providers/ad/ad_access.c index cae075d4222bba3c05d2c09e26da9b19f4507cc1..cd51129f63bafb2d1d40a9a2880ea02c7617f0db 100644 --- a/src/providers/ad/ad_access.c +++ b/src/providers/ad/ad_access.c
[...]
static errno_t @@ -385,7 +420,26 @@ ad_access_recv(struct tevent_req *req) }
In general with tevent requests, the _recv() function should be the last one in the code, so when you read the code and find the recv() function, you know you can stop reading and the rest is either another request or helper functions. Here you have _done after _recv.
static void -ad_access_check_done(struct tevent_req *req); +ad_gpo_access_done(struct tevent_req *subreq) +{
- struct tevent_req *req;
- errno_t ret;
- req = tevent_req_callback_data(subreq, struct tevent_req);
- ret = ad_gpo_access_recv(subreq);
- talloc_zfree(subreq);
It would be nice to have a DEBUG message here, at least for failure.
- if (ret == EOK) {
tevent_req_done(req);
- } else {
tevent_req_error(req, ret);
- }
+} diff --git a/src/providers/ad/ad_gpo.c b/src/providers/ad/ad_gpo.c new file mode 100644 index 0000000000000000000000000000000000000000..935e1924905fb25461ebf57db2e42eb1e8348417 --- /dev/null +++ b/src/providers/ad/ad_gpo.c @@ -0,0 +1,2221 @@
[...]
+/*
- This function determines whether the input ACE includes 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 security_ace *ace,
bool *_included)
I wonder if this function could simply return a boolean, the caller ignores the return code anyway.
+{
- int i = 0;
- struct dom_sid ace_dom_sid;
- struct dom_sid user_dom_sid;
- struct dom_sid group_dom_sid;
- char buf[SID_MAX_LEN + 1];
- ace_dom_sid = ace->trustee;
- dom_sid_string_buf(&ace_dom_sid, buf, SID_MAX_LEN);
- if (!string_to_sid(&user_dom_sid, user_sid)) {
DEBUG(SSSDBG_OP_FAILURE, "string_to_sid failed\n");
return EINVAL;
- }
- if (dom_sid_equal(&ace_dom_sid, &user_dom_sid)) {
*_included = true;
return EOK;
- }
- for (i = 0; i < group_size; i++) {
if (!string_to_sid(&group_dom_sid, group_sids[i])) {
DEBUG(SSSDBG_OP_FAILURE, "string_to_sid failed\n");
return EINVAL;
}
if (dom_sid_equal(&ace_dom_sid, &group_dom_sid)) {
*_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.
- 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
Thanks for the comments, they are really helpful. What does 'M' stands for here, though?
This is taken straight from the spec. The 'M' stands for "access mask", which represents the set of access rights (e.g. RIGHT_GENERIC_READ, RIGHT_DS_CREATE_CHILD, RIGHT_DS_CONTROL_ACCESS, etc) 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 access right is identified by a GUID in the ACE's ObjectType. In our case, this is the GUID corresponding to ApplyGroupPolicy.
- 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.
- */
+static enum ace_eval_status ad_gpo_evaluate_ace(struct security_ace *ace,
const char
*user_sid,
const char
**group_sids,
int group_size)
[...]
+static errno_t +ad_gpo_filter_gpos_by_dacl(TALLOC_CTX *mem_ctx,
char *user,
struct sss_domain_info *domain,
struct gp_gpo **candidate_gpos,
int num_candidate_gpos,
struct gp_gpo ***_dacl_filtered_gpos,
int *_num_dacl_filtered_gpos)
+{
- TALLOC_CTX *tmp_ctx = NULL;
- int i = 0;
- int ret = 0;
- struct gp_gpo *candidate_gpo = NULL;
- struct security_descriptor *sd = NULL;
- struct security_acl *dacl = NULL;
- const char *user_sid = NULL;
- const char **group_sids = NULL;
- int group_size = 0;
- int gpo_dn_idx = 0;
- bool access_allowed = false;
- struct gp_gpo **dacl_filtered_gpos = NULL;
- tmp_ctx = talloc_new(NULL);
- if (tmp_ctx == NULL) {
ret = ENOMEM;
goto done;
- }
- ret = ad_gpo_get_sids(tmp_ctx, user, domain, &user_sid,
&group_sids, &group_size);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Unable to retrieve SIDs: [%d](%s)\n", ret,
strerror(ret));
ret = ENOENT;
I wonder if ENOENT is the right error code, the information is not missing it just can't be processed, right? You can even create your own specific error codes if you find that the standard errno codes don't express the error well..
You are correct. I will address this in the next patch.
goto done;
- }
- dacl_filtered_gpos = talloc_array(tmp_ctx,
struct gp_gpo *,
num_candidate_gpos + 1);
[...]
- if (reply_count < 1) {
DEBUG(SSSDBG_OP_FAILURE, "No DN retrieved for policy
target.\n");
ret = ENOENT;
goto done;
- } else if (reply_count > 1) {
DEBUG(SSSDBG_OP_FAILURE, "Multiple replies for policy
target\n");
ret = ERR_INTERNAL;
goto done;
- } else if (reply == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "reply_count is 1, but reply is
NULL\n");
ret = ERR_INTERNAL;
goto done;
- }
- /* reply[0] holds requested attributes of single reply */
- ret = sysdb_attrs_get_string(reply[0], AD_AT_DN, &target_dn);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_string failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- state->target_dn = talloc_strdup(state, target_dn);
- if (state->target_dn == NULL) {
ret = ENOMEM;
goto done;
- }
The target_dn here is already allocated on state, it's just part of reply, (state->reply->target_dn), I think you can just assign to the state->target_dn pointer or if you'd like to free reply later on, steal target dn onto state instead of duplicating the string. This issue is repeated in the code couple of times.
- ret = sysdb_attrs_get_uint32_t(reply[0], AD_AT_UAC, &uac);
- if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_uint32_t failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
- }
- /* we only support computer policy targets, not users */
- if (!(uac & UAC_WORKSTATION_TRUST_ACCOUNT)) {
ret = EINVAL;
goto done;
- }
- state->target_dn = talloc_strdup(state, target_dn);
- if (state->target_dn == NULL) {
ret = ENOMEM;
goto done;
- }
- num_enabled = 0;
- ptr = raw_gplink_value;
[...]
- for (i = 0; i < gplink_count; i++) {
first = ptr + 1;
last = strchr(first, delim);
if (last == NULL) {
break;
}
*last = '\0';
last++;
dn = first;
if ( strncasecmp(dn, "LDAP://", 7)== 0 ) {
dn = dn + 7;
}
gplink_options = strchr(first, ';');
if (gplink_options == NULL) {
break;
Shouldn't we juts continue to the next item instead of breaking? Or does this condition mean that the end of the list was reached?
I think you are right. We should just continue to the next item.
}
*gplink_options = '\0';
gplink_options++;
gplink_number = strtouint32(gplink_options, NULL, 10);
I think you should check errno for failures after the strotouint32 call.
DEBUG(SSSDBG_TRACE_ALL,
"gplink_list[%d]: [%s; %d]\n", num_enabled, dn,
gplink_number);
if ((gplink_number == 1) || (gplink_number ==3)) {
/* ignore flag is set */
DEBUG(SSSDBG_TRACE_ALL, "ignored gpo skipped\n");
ptr = last;
continue;
}
if (allow_enforced_only && (gplink_number == 0)) {
/* unenforced flag is set; only enforced gpos allowed */
DEBUG(SSSDBG_TRACE_ALL, "unenforced gpo skipped\n");
ptr = last;
continue;
}
gplink_list[num_enabled] = talloc_zero(gplink_list, struct
gp_gplink);
if (gplink_list[num_enabled] == NULL) {
ret = ENOMEM;
goto done;
}
gplink_list[num_enabled]->gpo_dn =
talloc_strdup(gplink_list[num_enabled], dn);
if (gplink_list[num_enabled]->gpo_dn == NULL) {
ret = ENOMEM;
goto done;
}
if (gplink_number == 0) {
gplink_list[num_enabled]->enforced = 0;
num_enabled++;
} else if (gplink_number == 2) {
gplink_list[num_enabled]->enforced = 1;
num_enabled++;
} else {
ret = EINVAL;
goto done;
}
ptr = last;
- }
[...]
+ad_gpo_site_dn_retrieval_done(struct tevent_req *subreq) +{
- struct tevent_req *req;
- struct ad_gpo_process_som_state *state;
- int ret;
- int dp_error;
- int i = 0;
- size_t reply_count;
- struct sysdb_attrs **reply;
- const char *configNC;
- req = tevent_req_callback_data(subreq, struct tevent_req);
- state = tevent_req_data(req, struct ad_gpo_process_som_state);
Can you add a comment that would mention that this attribute should be read from the rootdse in the future and maybe even link to https://fedorahosted.org/sssd/ticket/2276 ?
Yes.
- ret = sdap_get_generic_recv(subreq, state,
&reply_count, &reply);
- talloc_zfree(subreq);
- if (ret != EOK) {
ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
/* TBD: handle (dp_error == DP_ERR_OFFLINE) case */
DEBUG(SSSDBG_OP_FAILURE,
"Unable to get configNC: [%d](%s)\n", ret,
strerror(ret));
ret = ENOENT;
goto done;
- }
[...]
- /* note that space was allocated for site_dn when allocating
som_list */
- state->som_list[state->num_soms] =
talloc_zero(state->som_list, struct gp_som);
- if (state->som_list[state->num_soms] == NULL) {
ret = ENOMEM;
goto done;
- }
- state->som_list[state->num_soms]->som_dn =
talloc_strdup(state->som_list[state->num_soms],
state->site_dn);
I think talloc_steal can be used safely here, no need to strdup.
- if (state->som_list[state->num_soms]->som_dn == NULL) {
ret = ENOMEM;
goto done;
- }
- state->num_soms++;
- state->som_list[state->num_soms] = NULL;
- i = 0;
- while (state->som_list[i]) {
DEBUG(SSSDBG_TRACE_FUNC, "som_list[%d]->som_dn is %s\n", i,
state->som_list[i]->som_dn);
i++;
- }
- ret = ad_gpo_get_som_attrs_step(req);
- done:
- if (ret == EOK) {
tevent_req_done(req);
Can we even have EOK here? Looks like the functions either return EAGAIN or outright failure..
ad_gpo_get_som_attrs_step() returns EOK after all SOMs have been processed. There is a comment to that effect in the code.
- } else if (ret != EAGAIN) {
tevent_req_error(req, ret);
- }
+} +static errno_t +ad_gpo_get_som_attrs_step(struct tevent_req *req) +{
- const char *attrs[] = {AD_AT_GPLINK, AD_AT_GPOPTIONS, NULL};
- struct tevent_req *subreq;
- struct ad_gpo_process_som_state *state;
- state = tevent_req_data(req, struct ad_gpo_process_som_state);
- state->som_index++;
- struct gp_som *gp_som = state->som_list[state->som_index];
- /* gp_som is NULL only after all SOMs have been processed */
- if (gp_som == NULL) return EOK;
- char *som_dn = gp_som->som_dn;
- subreq = sdap_get_generic_send(state, state->ev, state->opts,
sdap_id_op_handle(state->sdap_op),
som_dn, LDAP_SCOPE_BASE,
"(objectclass=*)", attrs, NULL,
0,
state->timeout,
false);
- if (subreq == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send failed.\n");
return ENOMEM;
- }
- tevent_req_set_callback(subreq, ad_gpo_get_som_attrs_done, req);
- return EAGAIN;
+}
[...]
diff --git a/src/util/sss_ldap.h b/src/util/sss_ldap.h index f298b2fbb30cf1532f8e94504ffb83ef73880b81..083c89141987f38313906d7d7a9a003a74c10cef 100644 --- a/src/util/sss_ldap.h +++ b/src/util/sss_ldap.h @@ -55,6 +55,15 @@ int sss_ldap_get_diagnostic_msg(TALLOC_CTX *mem_ctx, #define LDAP_SERVER_ASQ_OID "1.2.840.113556.1.4.1504" #endif /* LDAP_SERVER_ASQ_OID */
+#ifndef LDAP_SERVER_SD_OID +#define LDAP_SERVER_SD_OID "1.2.840.113556.1.4.801" +#endif /* LDAP_SERVER_SD_OID */
+#define SECINFO_OWNER ( 0x00000001 ) +#define SECINFO_GROUP ( 0x00000002 ) +#define SECINFO_DACL ( 0x00000004 ) +#define SECINFO_SACL ( 0x00000008 )
Of all these constants, only SECINFO_DACL is used, is the rest included for posterity? Do these magic values come from some technet article or MSDN documentation? If so, it might be nice to add a link or a comment.
These four flags indicate which security descriptor parts to retrieve during a search. They are defined at http://msdn.microsoft.com/en-us/library/aa366987(v=vs.85).aspx. While we only use a single flag (SECINFO_DACL), I thought I would include all four flags for completeness. Should I remove the other three flags that are not being used?
int sss_ldap_control_create(const char *oid, int iscritical, struct berval *value, int dupval, LDAPControl **ctrlp); -- 1.8.5.3
In general, it's really nice work, thank you! _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
I will now work on a new patch to address the comments you have raised.
Regards, Yassir. _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
Hi Jakub,
I have attached a revised cumulative patch that aims to address the review comments.
Additionally, I made the following changes en masse:
- changed talloc_strdup() to talloc_steal() when applicable (i.e. when
dealing with a talloc context)
- changed all occurrences of strerror() to sss_strerror() in order to use
sssd-specific error strings
Thanks, Yassir.
Hi,
I have some more comments, sorry I haven't caught them earlier. I ran the patch through Coverity and it uncovered several warnings, see below:
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function 'ad_gpo_target_dn_retrieval_done' sssd-1.11.90/src/providers/ad/ad_gpo.c:711:22: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # state->target_dn = talloc_steal(state, target_dn); # ^
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function 'ad_gpo_parent_dn' sssd-1.11.90/src/providers/ad/ad_gpo.c:902:17: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # *_parent_dn = talloc_steal(mem_ctx, p); # ^
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1053: cond_notnull: Condition "ptr = __coverity_strchr(ptr, 93)", taking true branch. Now the value of "ptr" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: dead_error_line: Execution cannot reach this statement "break;".
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1768: cond_notnull: Condition "ptr = __coverity_strchr(ptr, 92)", taking true branch. Now the value of "ptr" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: dead_error_line: Execution cannot reach this statement "break;".
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1812: cond_notnull: Condition "raw_machine_ext_names_value == NULL", taking false branch. Now the value of "raw_machine_ext_names_value" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1824: assignment: Assigning: "ptr" = "raw_machine_ext_names_value". sssd-1.11.90/src/providers/ad/ad_gpo.c:1825: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1825: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1826: dead_error_begin: Execution cannot reach this statement "ret = 12;".
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1830: cond_notnull: Condition "ptr = __coverity_strchr(ptr, 93)", taking true branch. Now the value of "ptr" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: dead_error_line: Execution cannot reach this statement "break;".
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function 'ad_gpo_get_gpo_attrs_done' sssd-1.11.90/src/providers/ad/ad_gpo.c:2091:22: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # gp_gpo->gpo_guid = talloc_steal(gp_gpo, gpo_guid); # ^
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c:2110:30: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # gp_gpo->gpo_display_name = talloc_steal(gp_gpo, gpo_display_name); # ^
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c:2131:19: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # file_sys_path = talloc_steal(gp_gpo, raw_file_sys_path); #
I have fixed the DEADCODE conditions detected by coverity and attached a revised cumulative patch. With regard to the COMPILER_WARNING, Lukas had already commented on these previously, and I had already fixed them in the most recent patch.
Also, when I set ad_gpo_access_control=permissive and tried to log in as Administrator, I was kicked out. I thought that permissive setting would ignore the errors, but maybe I stumbled upon something unexpected?
This is expected behavior because I have not implemented "permissive" yet. For now, "permissive" and "enforcing" are identical. I was not planning on implementing "permissive" until after the code is able to retrieve policy over smb and make an enforcement decision. I did implement "disabled", since it allows users to simply turn off gpo checking altogether.
The current code in ad_gpo_access.c simply does the following:
switch (state->ctx->gpo_access_control_mode) { case GPO_ACCESS_CONTROL_DISABLED: /* do not evaluate gpos; mark request done */ tevent_req_done(req); return; case GPO_ACCESS_CONTROL_PERMISSIVE: case GPO_ACCESS_CONTROL_ENFORCING: /* continue on to evaluate gpos */ break; default: tevent_req_error(req, EINVAL); return; }
Regards, Yassir.
On (06/05/14 12:42), Yassir Elley wrote:
----- Original Message -----
On Tue, Apr 29, 2014 at 07:22:37PM -0400, Yassir Elley wrote:
----- Original Message -----
----- Original Message -----
On Fri, Apr 18, 2014 at 12:56:27PM -0400, Yassir Elley wrote:
Hi Jakub,
I have attached a revised cumulative patch that aims to address the review comments.
Additionally, I made the following changes en masse:
- changed talloc_strdup() to talloc_steal() when applicable (i.e. when
dealing with a talloc context)
- changed all occurrences of strerror() to sss_strerror() in order to use
sssd-specific error strings
Thanks, Yassir.
Hi,
I have some more comments, sorry I haven't caught them earlier. I ran the patch through Coverity and it uncovered several warnings, see below:
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function 'ad_gpo_target_dn_retrieval_done' sssd-1.11.90/src/providers/ad/ad_gpo.c:711:22: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # state->target_dn = talloc_steal(state, target_dn); # ^
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function 'ad_gpo_parent_dn' sssd-1.11.90/src/providers/ad/ad_gpo.c:902:17: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # *_parent_dn = talloc_steal(mem_ctx, p); # ^
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1053: cond_notnull: Condition "ptr = __coverity_strchr(ptr, 93)", taking true branch. Now the value of "ptr" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: dead_error_line: Execution cannot reach this statement "break;".
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1768: cond_notnull: Condition "ptr = __coverity_strchr(ptr, 92)", taking true branch. Now the value of "ptr" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: dead_error_line: Execution cannot reach this statement "break;".
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1812: cond_notnull: Condition "raw_machine_ext_names_value == NULL", taking false branch. Now the value of "raw_machine_ext_names_value" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1824: assignment: Assigning: "ptr" = "raw_machine_ext_names_value". sssd-1.11.90/src/providers/ad/ad_gpo.c:1825: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1825: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1826: dead_error_begin: Execution cannot reach this statement "ret = 12;".
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1830: cond_notnull: Condition "ptr = __coverity_strchr(ptr, 93)", taking true branch. Now the value of "ptr" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: dead_error_line: Execution cannot reach this statement "break;".
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function 'ad_gpo_get_gpo_attrs_done' sssd-1.11.90/src/providers/ad/ad_gpo.c:2091:22: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # gp_gpo->gpo_guid = talloc_steal(gp_gpo, gpo_guid); # ^
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c:2110:30: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # gp_gpo->gpo_display_name = talloc_steal(gp_gpo, gpo_display_name); # ^
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c:2131:19: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # file_sys_path = talloc_steal(gp_gpo, raw_file_sys_path); #
I have fixed the DEADCODE conditions detected by coverity and attached a revised cumulative patch. With regard to the COMPILER_WARNING, Lukas had already commented on these previously, and I had already fixed them in the most recent patch.
Yes warnings were fixed in previous version and I could not see any problems with DEADCODE.
Also, when I set ad_gpo_access_control=permissive and tried to log in as Administrator, I was kicked out. I thought that permissive setting would ignore the errors, but maybe I stumbled upon something unexpected?
This is expected behavior because I have not implemented "permissive" yet. For now, "permissive" and "enforcing" are identical. I was not planning on implementing "permissive" until after the code is able to retrieve policy over smb and make an enforcement decision. I did implement "disabled", since it allows users to simply turn off gpo checking altogether.
The current code in ad_gpo_access.c simply does the following:
switch (state->ctx->gpo_access_control_mode) { case GPO_ACCESS_CONTROL_DISABLED: /* do not evaluate gpos; mark request done */ tevent_req_done(req); return; case GPO_ACCESS_CONTROL_PERMISSIVE: case GPO_ACCESS_CONTROL_ENFORCING: /* continue on to evaluate gpos */ break; default: tevent_req_error(req, EINVAL); return; }
Regards, Yassir.
I saw another warnigs from static analysers.
Error: CLANG_WARNING: [#def1] sssd-1.11.90/src/providers/ad/ad_gpo.c:777:14: warning: Function call argument is an uninitialized value # subreq = ad_gpo_process_gpo_send(state, # ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This one is false possitive.
Error: CLANG_WARNING: [#def2] sssd-1.11.90/src/providers/ad/ad_gpo.c:850:15: warning: Access to field 'gpo_guid' results in a dereference of a null pointer # state->dacl_filtered_gpos[i]->gpo_guid); # ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ sssd-1.11.90/src/util/util.h:127:28: note: expanded from macro 'DEBUG' # format, ##__VA_ARGS__); \ # ^ This can be a probelm. I am attaching html report with code flow.
Error: CLANG_WARNING: [#def3] sssd-1.11.90/src/providers/ad/ad_gpo.c:2212:5: warning: Value stored to 'ret' is never read # ret = ad_gpo_parse_sd(gp_gpo, el[0].values[0].data, el[0].values[0].length, # ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This can be solved with adding debug message.
2212 ret = ad_gpo_parse_sd(gp_gpo, el[0].values[0].data, el[0].values[0].length, 2213 &gp_gpo->gpo_sd); 2214
Value stored to 'ret' is never read 2215 ret = ad_gpo_get_gpo_attrs_step(req);
Thank your patience.
LS
----- Original Message -----
On (06/05/14 12:42), Yassir Elley wrote:
----- Original Message -----
On Tue, Apr 29, 2014 at 07:22:37PM -0400, Yassir Elley wrote:
----- Original Message -----
----- Original Message -----
On Fri, Apr 18, 2014 at 12:56:27PM -0400, Yassir Elley wrote:
Hi Jakub,
I have attached a revised cumulative patch that aims to address the review comments.
Additionally, I made the following changes en masse:
- changed talloc_strdup() to talloc_steal() when applicable (i.e. when
dealing with a talloc context)
- changed all occurrences of strerror() to sss_strerror() in order to
use sssd-specific error strings
Thanks, Yassir.
Hi,
I have some more comments, sorry I haven't caught them earlier. I ran the patch through Coverity and it uncovered several warnings, see below:
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function 'ad_gpo_target_dn_retrieval_done' sssd-1.11.90/src/providers/ad/ad_gpo.c:711:22: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # state->target_dn = talloc_steal(state, target_dn); # ^
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function 'ad_gpo_parent_dn' sssd-1.11.90/src/providers/ad/ad_gpo.c:902:17: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # *_parent_dn = talloc_steal(mem_ctx, p); # ^
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1053: cond_notnull: Condition "ptr = __coverity_strchr(ptr, 93)", taking true branch. Now the value of "ptr" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: dead_error_line: Execution cannot reach this statement "break;".
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1768: cond_notnull: Condition "ptr = __coverity_strchr(ptr, 92)", taking true branch. Now the value of "ptr" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: dead_error_line: Execution cannot reach this statement "break;".
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1812: cond_notnull: Condition "raw_machine_ext_names_value == NULL", taking false branch. Now the value of "raw_machine_ext_names_value" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1824: assignment: Assigning: "ptr" = "raw_machine_ext_names_value". sssd-1.11.90/src/providers/ad/ad_gpo.c:1825: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1825: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1826: dead_error_begin: Execution cannot reach this statement "ret = 12;".
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1830: cond_notnull: Condition "ptr = __coverity_strchr(ptr, 93)", taking true branch. Now the value of "ptr" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: dead_error_line: Execution cannot reach this statement "break;".
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function 'ad_gpo_get_gpo_attrs_done' sssd-1.11.90/src/providers/ad/ad_gpo.c:2091:22: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # gp_gpo->gpo_guid = talloc_steal(gp_gpo, gpo_guid); # ^
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c:2110:30: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # gp_gpo->gpo_display_name = talloc_steal(gp_gpo, gpo_display_name); # ^
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c:2131:19: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # file_sys_path = talloc_steal(gp_gpo, raw_file_sys_path); #
I have fixed the DEADCODE conditions detected by coverity and attached a revised cumulative patch. With regard to the COMPILER_WARNING, Lukas had already commented on these previously, and I had already fixed them in the most recent patch.
Yes warnings were fixed in previous version and I could not see any problems with DEADCODE.
Also, when I set ad_gpo_access_control=permissive and tried to log in as Administrator, I was kicked out. I thought that permissive setting would ignore the errors, but maybe I stumbled upon something unexpected?
This is expected behavior because I have not implemented "permissive" yet. For now, "permissive" and "enforcing" are identical. I was not planning on implementing "permissive" until after the code is able to retrieve policy over smb and make an enforcement decision. I did implement "disabled", since it allows users to simply turn off gpo checking altogether.
The current code in ad_gpo_access.c simply does the following:
switch (state->ctx->gpo_access_control_mode) { case GPO_ACCESS_CONTROL_DISABLED: /* do not evaluate gpos; mark request done */ tevent_req_done(req); return; case GPO_ACCESS_CONTROL_PERMISSIVE: case GPO_ACCESS_CONTROL_ENFORCING: /* continue on to evaluate gpos */ break; default: tevent_req_error(req, EINVAL); return; }
Regards, Yassir.
I saw another warnigs from static analysers.
Error: CLANG_WARNING: [#def1] sssd-1.11.90/src/providers/ad/ad_gpo.c:777:14: warning: Function call argument is an uninitialized value # subreq = ad_gpo_process_gpo_send(state, # ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This one is false possitive.
Error: CLANG_WARNING: [#def2] sssd-1.11.90/src/providers/ad/ad_gpo.c:850:15: warning: Access to field 'gpo_guid' results in a dereference of a null pointer # state->dacl_filtered_gpos[i]->gpo_guid); # ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ sssd-1.11.90/src/util/util.h:127:28: note: expanded from macro 'DEBUG' # format, ##__VA_ARGS__); \ # ^ This can be a probelm. I am attaching html report with code flow.
Good catch. I added the following to fix this:
if (state->dacl_filtered_gpos[0] == NULL) { /* since no applicable gpos were found, there is nothing to enforce */ DEBUG(SSSDBG_TRACE_FUNC, "no applicable gpos found after dacl filtering\n"); ret = EOK; goto done; }
Error: CLANG_WARNING: [#def3] sssd-1.11.90/src/providers/ad/ad_gpo.c:2212:5: warning: Value stored to 'ret' is never read # ret = ad_gpo_parse_sd(gp_gpo, el[0].values[0].data, el[0].values[0].length, # ^
This can be solved with adding debug message. 2212 ret = ad_gpo_parse_sd(gp_gpo, el[0].values[0].data, el[0].values[0].length, 2213 &gp_gpo->gpo_sd); 2214 Value stored to 'ret' is never read 2215 ret = ad_gpo_get_gpo_attrs_step(req);
Good catch. I added the following to fix this:
if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "ad_gpo_parse_sd() failed\n"); goto done; }
Thank your patience.
LS
Revised patch is attached.
Regards, Yassir.
On (07/05/14 08:47), Yassir Elley wrote:
----- Original Message -----
On (06/05/14 12:42), Yassir Elley wrote:
----- Original Message -----
On Tue, Apr 29, 2014 at 07:22:37PM -0400, Yassir Elley wrote:
----- Original Message -----
----- Original Message ----- > On Fri, Apr 18, 2014 at 12:56:27PM -0400, Yassir Elley wrote:
Hi Jakub,
I have attached a revised cumulative patch that aims to address the review comments.
Additionally, I made the following changes en masse:
- changed talloc_strdup() to talloc_steal() when applicable (i.e. when
dealing with a talloc context)
- changed all occurrences of strerror() to sss_strerror() in order to
use sssd-specific error strings
Thanks, Yassir.
Hi,
I have some more comments, sorry I haven't caught them earlier. I ran the patch through Coverity and it uncovered several warnings, see below:
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function 'ad_gpo_target_dn_retrieval_done' sssd-1.11.90/src/providers/ad/ad_gpo.c:711:22: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # state->target_dn = talloc_steal(state, target_dn); # ^
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function 'ad_gpo_parent_dn' sssd-1.11.90/src/providers/ad/ad_gpo.c:902:17: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # *_parent_dn = talloc_steal(mem_ctx, p); # ^
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1053: cond_notnull: Condition "ptr = __coverity_strchr(ptr, 93)", taking true branch. Now the value of "ptr" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: dead_error_line: Execution cannot reach this statement "break;".
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1768: cond_notnull: Condition "ptr = __coverity_strchr(ptr, 92)", taking true branch. Now the value of "ptr" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: dead_error_line: Execution cannot reach this statement "break;".
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1812: cond_notnull: Condition "raw_machine_ext_names_value == NULL", taking false branch. Now the value of "raw_machine_ext_names_value" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1824: assignment: Assigning: "ptr" = "raw_machine_ext_names_value". sssd-1.11.90/src/providers/ad/ad_gpo.c:1825: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1825: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1826: dead_error_begin: Execution cannot reach this statement "ret = 12;".
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1830: cond_notnull: Condition "ptr = __coverity_strchr(ptr, 93)", taking true branch. Now the value of "ptr" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: dead_error_line: Execution cannot reach this statement "break;".
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function 'ad_gpo_get_gpo_attrs_done' sssd-1.11.90/src/providers/ad/ad_gpo.c:2091:22: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # gp_gpo->gpo_guid = talloc_steal(gp_gpo, gpo_guid); # ^
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c:2110:30: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # gp_gpo->gpo_display_name = talloc_steal(gp_gpo, gpo_display_name); # ^
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c:2131:19: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # file_sys_path = talloc_steal(gp_gpo, raw_file_sys_path); #
I have fixed the DEADCODE conditions detected by coverity and attached a revised cumulative patch. With regard to the COMPILER_WARNING, Lukas had already commented on these previously, and I had already fixed them in the most recent patch.
Yes warnings were fixed in previous version and I could not see any problems with DEADCODE.
Also, when I set ad_gpo_access_control=permissive and tried to log in as Administrator, I was kicked out. I thought that permissive setting would ignore the errors, but maybe I stumbled upon something unexpected?
This is expected behavior because I have not implemented "permissive" yet. For now, "permissive" and "enforcing" are identical. I was not planning on implementing "permissive" until after the code is able to retrieve policy over smb and make an enforcement decision. I did implement "disabled", since it allows users to simply turn off gpo checking altogether.
The current code in ad_gpo_access.c simply does the following:
switch (state->ctx->gpo_access_control_mode) { case GPO_ACCESS_CONTROL_DISABLED: /* do not evaluate gpos; mark request done */ tevent_req_done(req); return; case GPO_ACCESS_CONTROL_PERMISSIVE: case GPO_ACCESS_CONTROL_ENFORCING: /* continue on to evaluate gpos */ break; default: tevent_req_error(req, EINVAL); return; }
Regards, Yassir.
I saw another warnigs from static analysers.
Error: CLANG_WARNING: [#def1] sssd-1.11.90/src/providers/ad/ad_gpo.c:777:14: warning: Function call argument is an uninitialized value # subreq = ad_gpo_process_gpo_send(state, # ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This one is false possitive.
Error: CLANG_WARNING: [#def2] sssd-1.11.90/src/providers/ad/ad_gpo.c:850:15: warning: Access to field 'gpo_guid' results in a dereference of a null pointer # state->dacl_filtered_gpos[i]->gpo_guid); # ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ sssd-1.11.90/src/util/util.h:127:28: note: expanded from macro 'DEBUG' # format, ##__VA_ARGS__); \ # ^ This can be a probelm. I am attaching html report with code flow.
Good catch. I added the following to fix this:
if (state->dacl_filtered_gpos[0] == NULL) { /* since no applicable gpos were found, there is nothing to enforce */ DEBUG(SSSDBG_TRACE_FUNC, "no applicable gpos found after dacl filtering\n"); ret = EOK; goto done; }
Error: CLANG_WARNING: [#def3] sssd-1.11.90/src/providers/ad/ad_gpo.c:2212:5: warning: Value stored to 'ret' is never read # ret = ad_gpo_parse_sd(gp_gpo, el[0].values[0].data, el[0].values[0].length, # ^
This can be solved with adding debug message. 2212 ret = ad_gpo_parse_sd(gp_gpo, el[0].values[0].data, el[0].values[0].length, 2213 &gp_gpo->gpo_sd); 2214 Value stored to 'ret' is never read 2215 ret = ad_gpo_get_gpo_attrs_step(req);
Good catch. I added the following to fix this:
if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "ad_gpo_parse_sd() failed\n"); goto done; }
Thank your patience.
LS
Revised patch is attached.
Regards, Yassir.
From ab9b95c39d7bfc95b3073e87661ecbdabfa8a8de Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Mon, 20 Jan 2014 11:17:06 -0500 Subject: [PATCH] Implemented LDAP component of GPO-based access control
Yassir, thank you. I can confirm that warnings from static analysers and compilers are gone.
LS
On Wed, May 07, 2014 at 04:46:14PM +0200, Lukas Slebodnik wrote:
On (07/05/14 08:47), Yassir Elley wrote:
----- Original Message -----
On (06/05/14 12:42), Yassir Elley wrote:
----- Original Message -----
On Tue, Apr 29, 2014 at 07:22:37PM -0400, Yassir Elley wrote:
----- Original Message ----- > ----- Original Message ----- > > On Fri, Apr 18, 2014 at 12:56:27PM -0400, Yassir Elley wrote:
Hi Jakub,
I have attached a revised cumulative patch that aims to address the review comments.
Additionally, I made the following changes en masse:
- changed talloc_strdup() to talloc_steal() when applicable (i.e. when
dealing with a talloc context)
- changed all occurrences of strerror() to sss_strerror() in order to
use sssd-specific error strings
Thanks, Yassir.
Hi,
I have some more comments, sorry I haven't caught them earlier. I ran the patch through Coverity and it uncovered several warnings, see below:
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function 'ad_gpo_target_dn_retrieval_done' sssd-1.11.90/src/providers/ad/ad_gpo.c:711:22: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # state->target_dn = talloc_steal(state, target_dn); # ^
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function 'ad_gpo_parent_dn' sssd-1.11.90/src/providers/ad/ad_gpo.c:902:17: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # *_parent_dn = talloc_steal(mem_ctx, p); # ^
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1053: cond_notnull: Condition "ptr = __coverity_strchr(ptr, 93)", taking true branch. Now the value of "ptr" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: dead_error_line: Execution cannot reach this statement "break;".
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1768: cond_notnull: Condition "ptr = __coverity_strchr(ptr, 92)", taking true branch. Now the value of "ptr" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: dead_error_line: Execution cannot reach this statement "break;".
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1812: cond_notnull: Condition "raw_machine_ext_names_value == NULL", taking false branch. Now the value of "raw_machine_ext_names_value" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1824: assignment: Assigning: "ptr" = "raw_machine_ext_names_value". sssd-1.11.90/src/providers/ad/ad_gpo.c:1825: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1825: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1826: dead_error_begin: Execution cannot reach this statement "ret = 12;".
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1830: cond_notnull: Condition "ptr = __coverity_strchr(ptr, 93)", taking true branch. Now the value of "ptr" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: dead_error_line: Execution cannot reach this statement "break;".
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function 'ad_gpo_get_gpo_attrs_done' sssd-1.11.90/src/providers/ad/ad_gpo.c:2091:22: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # gp_gpo->gpo_guid = talloc_steal(gp_gpo, gpo_guid); # ^
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c:2110:30: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # gp_gpo->gpo_display_name = talloc_steal(gp_gpo, gpo_display_name); # ^
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c:2131:19: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # file_sys_path = talloc_steal(gp_gpo, raw_file_sys_path); #
I have fixed the DEADCODE conditions detected by coverity and attached a revised cumulative patch. With regard to the COMPILER_WARNING, Lukas had already commented on these previously, and I had already fixed them in the most recent patch.
Yes warnings were fixed in previous version and I could not see any problems with DEADCODE.
Also, when I set ad_gpo_access_control=permissive and tried to log in as Administrator, I was kicked out. I thought that permissive setting would ignore the errors, but maybe I stumbled upon something unexpected?
This is expected behavior because I have not implemented "permissive" yet. For now, "permissive" and "enforcing" are identical. I was not planning on implementing "permissive" until after the code is able to retrieve policy over smb and make an enforcement decision. I did implement "disabled", since it allows users to simply turn off gpo checking altogether.
The current code in ad_gpo_access.c simply does the following:
switch (state->ctx->gpo_access_control_mode) { case GPO_ACCESS_CONTROL_DISABLED: /* do not evaluate gpos; mark request done */ tevent_req_done(req); return; case GPO_ACCESS_CONTROL_PERMISSIVE: case GPO_ACCESS_CONTROL_ENFORCING: /* continue on to evaluate gpos */ break; default: tevent_req_error(req, EINVAL); return; }
Regards, Yassir.
I saw another warnigs from static analysers.
Error: CLANG_WARNING: [#def1] sssd-1.11.90/src/providers/ad/ad_gpo.c:777:14: warning: Function call argument is an uninitialized value # subreq = ad_gpo_process_gpo_send(state, # ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This one is false possitive.
Error: CLANG_WARNING: [#def2] sssd-1.11.90/src/providers/ad/ad_gpo.c:850:15: warning: Access to field 'gpo_guid' results in a dereference of a null pointer # state->dacl_filtered_gpos[i]->gpo_guid); # ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ sssd-1.11.90/src/util/util.h:127:28: note: expanded from macro 'DEBUG' # format, ##__VA_ARGS__); \ # ^ This can be a probelm. I am attaching html report with code flow.
Good catch. I added the following to fix this:
if (state->dacl_filtered_gpos[0] == NULL) { /* since no applicable gpos were found, there is nothing to enforce */ DEBUG(SSSDBG_TRACE_FUNC, "no applicable gpos found after dacl filtering\n"); ret = EOK; goto done; }
Error: CLANG_WARNING: [#def3] sssd-1.11.90/src/providers/ad/ad_gpo.c:2212:5: warning: Value stored to 'ret' is never read # ret = ad_gpo_parse_sd(gp_gpo, el[0].values[0].data, el[0].values[0].length, # ^
This can be solved with adding debug message. 2212 ret = ad_gpo_parse_sd(gp_gpo, el[0].values[0].data, el[0].values[0].length, 2213 &gp_gpo->gpo_sd); 2214 Value stored to 'ret' is never read 2215 ret = ad_gpo_get_gpo_attrs_step(req);
Good catch. I added the following to fix this:
if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "ad_gpo_parse_sd() failed\n"); goto done; }
Thank your patience.
LS
Revised patch is attached.
Regards, Yassir.
From ab9b95c39d7bfc95b3073e87661ecbdabfa8a8de Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Mon, 20 Jan 2014 11:17:06 -0500 Subject: [PATCH] Implemented LDAP component of GPO-based access control
Yassir, thank you. I can confirm that warnings from static analysers and compilers are gone.
The latest version still works well in my tests. Only the dates in the commit header are looking a bit odd.
bye, Sumit
LS _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
----- Original Message -----
On Wed, May 07, 2014 at 04:46:14PM +0200, Lukas Slebodnik wrote:
On (07/05/14 08:47), Yassir Elley wrote:
----- Original Message -----
On (06/05/14 12:42), Yassir Elley wrote:
----- Original Message -----
On Tue, Apr 29, 2014 at 07:22:37PM -0400, Yassir Elley wrote: > ----- Original Message ----- > > ----- Original Message ----- > > > On Fri, Apr 18, 2014 at 12:56:27PM -0400, Yassir Elley wrote: > > Hi Jakub, > > I have attached a revised cumulative patch that aims to address the > review > comments. > > Additionally, I made the following changes en masse: > * changed talloc_strdup() to talloc_steal() when applicable (i.e. > when > dealing with a talloc context) > * changed all occurrences of strerror() to sss_strerror() in order > to > use > sssd-specific error strings > > Thanks, > Yassir.
Hi,
I have some more comments, sorry I haven't caught them earlier. I ran the patch through Coverity and it uncovered several warnings, see below:
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function 'ad_gpo_target_dn_retrieval_done' sssd-1.11.90/src/providers/ad/ad_gpo.c:711:22: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # state->target_dn = talloc_steal(state, target_dn); # ^
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function 'ad_gpo_parent_dn' sssd-1.11.90/src/providers/ad/ad_gpo.c:902:17: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # *_parent_dn = talloc_steal(mem_ctx, p); # ^
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1053: cond_notnull: Condition "ptr = __coverity_strchr(ptr, 93)", taking true branch. Now the value of "ptr" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: dead_error_line: Execution cannot reach this statement "break;".
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1768: cond_notnull: Condition "ptr = __coverity_strchr(ptr, 92)", taking true branch. Now the value of "ptr" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: dead_error_line: Execution cannot reach this statement "break;".
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1812: cond_notnull: Condition "raw_machine_ext_names_value == NULL", taking false branch. Now the value of "raw_machine_ext_names_value" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1824: assignment: Assigning: "ptr" = "raw_machine_ext_names_value". sssd-1.11.90/src/providers/ad/ad_gpo.c:1825: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1825: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1826: dead_error_begin: Execution cannot reach this statement "ret = 12;".
Error: DEADCODE (CWE-561): sssd-1.11.90/src/providers/ad/ad_gpo.c:1830: cond_notnull: Condition "ptr = __coverity_strchr(ptr, 93)", taking true branch. Now the value of "ptr" is not NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: notnull: At condition "ptr == NULL", the value of "ptr" cannot be NULL. sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: dead_error_condition: The condition "ptr == NULL" cannot be true. sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: dead_error_line: Execution cannot reach this statement "break;".
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function 'ad_gpo_get_gpo_attrs_done' sssd-1.11.90/src/providers/ad/ad_gpo.c:2091:22: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # gp_gpo->gpo_guid = talloc_steal(gp_gpo, gpo_guid); # ^
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c:2110:30: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # gp_gpo->gpo_display_name = talloc_steal(gp_gpo, gpo_display_name); # ^
Error: COMPILER_WARNING: sssd-1.11.90/src/providers/ad/ad_gpo.c:2131:19: warning: assignment discards 'const' qualifier from pointer target type [enabled by default] # file_sys_path = talloc_steal(gp_gpo, raw_file_sys_path); #
I have fixed the DEADCODE conditions detected by coverity and attached a revised cumulative patch. With regard to the COMPILER_WARNING, Lukas had already commented on these previously, and I had already fixed them in the most recent patch.
Yes warnings were fixed in previous version and I could not see any problems with DEADCODE.
Also, when I set ad_gpo_access_control=permissive and tried to log in as Administrator, I was kicked out. I thought that permissive setting would ignore the errors, but maybe I stumbled upon something unexpected?
This is expected behavior because I have not implemented "permissive" yet. For now, "permissive" and "enforcing" are identical. I was not planning on implementing "permissive" until after the code is able to retrieve policy over smb and make an enforcement decision. I did implement "disabled", since it allows users to simply turn off gpo checking altogether.
The current code in ad_gpo_access.c simply does the following:
switch (state->ctx->gpo_access_control_mode) { case GPO_ACCESS_CONTROL_DISABLED: /* do not evaluate gpos; mark request done */ tevent_req_done(req); return; case GPO_ACCESS_CONTROL_PERMISSIVE: case GPO_ACCESS_CONTROL_ENFORCING: /* continue on to evaluate gpos */ break; default: tevent_req_error(req, EINVAL); return; }
Regards, Yassir.
I saw another warnigs from static analysers.
Error: CLANG_WARNING: [#def1] sssd-1.11.90/src/providers/ad/ad_gpo.c:777:14: warning: Function call argument is an uninitialized value # subreq = ad_gpo_process_gpo_send(state, # ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This one is false possitive.
Error: CLANG_WARNING: [#def2] sssd-1.11.90/src/providers/ad/ad_gpo.c:850:15: warning: Access to field 'gpo_guid' results in a dereference of a null pointer # state->dacl_filtered_gpos[i]->gpo_guid); # ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ sssd-1.11.90/src/util/util.h:127:28: note: expanded from macro 'DEBUG' # format, ##__VA_ARGS__); \ # ^ This can be a probelm. I am attaching html report with code flow.
Good catch. I added the following to fix this:
if (state->dacl_filtered_gpos[0] == NULL) { /* since no applicable gpos were found, there is nothing to enforce */ DEBUG(SSSDBG_TRACE_FUNC, "no applicable gpos found after dacl filtering\n"); ret = EOK; goto done; }
Error: CLANG_WARNING: [#def3] sssd-1.11.90/src/providers/ad/ad_gpo.c:2212:5: warning: Value stored to 'ret' is never read # ret = ad_gpo_parse_sd(gp_gpo, el[0].values[0].data, el[0].values[0].length, # ^
This can be solved with adding debug message. 2212 ret = ad_gpo_parse_sd(gp_gpo, el[0].values[0].data, el[0].values[0].length, 2213 &gp_gpo->gpo_sd); 2214 Value stored to 'ret' is never read 2215 ret = ad_gpo_get_gpo_attrs_step(req);
Good catch. I added the following to fix this:
if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "ad_gpo_parse_sd() failed\n"); goto done; }
Thank your patience.
LS
Revised patch is attached.
Regards, Yassir.
From ab9b95c39d7bfc95b3073e87661ecbdabfa8a8de Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Mon, 20 Jan 2014 11:17:06 -0500 Subject: [PATCH] Implemented LDAP component of GPO-based access control
Yassir, thank you. I can confirm that warnings from static analysers and compilers are gone.
The latest version still works well in my tests. Only the dates in the commit header are looking a bit odd.
bye, Sumit
I think that the commit header shows the "magic date" of September 17, 2001 and the author date of January 20, 2014. I made my initial commit on January 20, and then made additional commits, but always squashed them into the initial commit using "git rebase -i". Since the author date does not change with rebases (although the committer date does), the author date has remained unchanged since the initial commit.
Regards, Yassir.
On Tue, May 13, 2014 at 11:57:54AM -0400, Yassir Elley wrote:
----- Original Message -----
On Wed, May 07, 2014 at 04:46:14PM +0200, Lukas Slebodnik wrote:
On (07/05/14 08:47), Yassir Elley wrote:
----- Original Message -----
On (06/05/14 12:42), Yassir Elley wrote:
----- Original Message ----- > On Tue, Apr 29, 2014 at 07:22:37PM -0400, Yassir Elley wrote: > > ----- Original Message ----- > > > ----- Original Message ----- > > > > On Fri, Apr 18, 2014 at 12:56:27PM -0400, Yassir Elley wrote: > > > > Hi Jakub, > > > > I have attached a revised cumulative patch that aims to address the > > review > > comments. > > > > Additionally, I made the following changes en masse: > > * changed talloc_strdup() to talloc_steal() when applicable (i.e. > > when > > dealing with a talloc context) > > * changed all occurrences of strerror() to sss_strerror() in order > > to > > use > > sssd-specific error strings > > > > Thanks, > > Yassir. > > Hi, > > I have some more comments, sorry I haven't caught them earlier. I ran > the > patch through Coverity and it uncovered several warnings, see below: > > Error: COMPILER_WARNING: > sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function > 'ad_gpo_target_dn_retrieval_done' > sssd-1.11.90/src/providers/ad/ad_gpo.c:711:22: warning: assignment > discards > 'const' qualifier from pointer target type [enabled by default] > # state->target_dn = talloc_steal(state, target_dn); > # ^ > > Error: COMPILER_WARNING: > sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function > 'ad_gpo_parent_dn' > sssd-1.11.90/src/providers/ad/ad_gpo.c:902:17: warning: assignment > discards > 'const' qualifier from pointer target type [enabled by default] > # *_parent_dn = talloc_steal(mem_ctx, p); > # ^ > > Error: DEADCODE (CWE-561): > sssd-1.11.90/src/providers/ad/ad_gpo.c:1053: cond_notnull: Condition > "ptr > = > __coverity_strchr(ptr, 93)", taking true branch. Now the value of > "ptr" is > not NULL. > sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: notnull: At condition > "ptr == > NULL", the value of "ptr" cannot be NULL. > sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: dead_error_condition: > The > condition "ptr == NULL" cannot be true. > sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: dead_error_line: > Execution > cannot reach this statement "break;". > > Error: DEADCODE (CWE-561): > sssd-1.11.90/src/providers/ad/ad_gpo.c:1768: cond_notnull: Condition > "ptr > = > __coverity_strchr(ptr, 92)", taking true branch. Now the value of > "ptr" is > not NULL. > sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: notnull: At condition > "ptr == > NULL", the value of "ptr" cannot be NULL. > sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: dead_error_condition: > The > condition "ptr == NULL" cannot be true. > sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: dead_error_line: > Execution > cannot reach this statement "break;". > > Error: DEADCODE (CWE-561): > sssd-1.11.90/src/providers/ad/ad_gpo.c:1812: cond_notnull: Condition > "raw_machine_ext_names_value == NULL", taking false branch. Now the > value > of > "raw_machine_ext_names_value" is not NULL. > sssd-1.11.90/src/providers/ad/ad_gpo.c:1824: assignment: Assigning: > "ptr" > = > "raw_machine_ext_names_value". > sssd-1.11.90/src/providers/ad/ad_gpo.c:1825: notnull: At condition > "ptr == > NULL", the value of "ptr" cannot be NULL. > sssd-1.11.90/src/providers/ad/ad_gpo.c:1825: dead_error_condition: > The > condition "ptr == NULL" cannot be true. > sssd-1.11.90/src/providers/ad/ad_gpo.c:1826: dead_error_begin: > Execution > cannot reach this statement "ret = 12;". > > Error: DEADCODE (CWE-561): > sssd-1.11.90/src/providers/ad/ad_gpo.c:1830: cond_notnull: Condition > "ptr > = > __coverity_strchr(ptr, 93)", taking true branch. Now the value of > "ptr" is > not NULL. > sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: notnull: At condition > "ptr == > NULL", the value of "ptr" cannot be NULL. > sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: dead_error_condition: > The > condition "ptr == NULL" cannot be true. > sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: dead_error_line: > Execution > cannot reach this statement "break;". > > Error: COMPILER_WARNING: > sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function > 'ad_gpo_get_gpo_attrs_done' > sssd-1.11.90/src/providers/ad/ad_gpo.c:2091:22: warning: assignment > discards > 'const' qualifier from pointer target type [enabled by default] > # gp_gpo->gpo_guid = talloc_steal(gp_gpo, gpo_guid); > # ^ > > Error: COMPILER_WARNING: > sssd-1.11.90/src/providers/ad/ad_gpo.c:2110:30: warning: assignment > discards > 'const' qualifier from pointer target type [enabled by default] > # gp_gpo->gpo_display_name = talloc_steal(gp_gpo, > gpo_display_name); > # ^ > > Error: COMPILER_WARNING: > sssd-1.11.90/src/providers/ad/ad_gpo.c:2131:19: warning: assignment > discards > 'const' qualifier from pointer target type [enabled by default] > # file_sys_path = talloc_steal(gp_gpo, raw_file_sys_path); > #
I have fixed the DEADCODE conditions detected by coverity and attached a revised cumulative patch. With regard to the COMPILER_WARNING, Lukas had already commented on these previously, and I had already fixed them in the most recent patch.
Yes warnings were fixed in previous version and I could not see any problems with DEADCODE.
> > Also, when I set ad_gpo_access_control=permissive and tried to log in > as > Administrator, I was kicked out. I thought that permissive > setting would ignore the errors, but maybe I stumbled upon something > unexpected?
This is expected behavior because I have not implemented "permissive" yet. For now, "permissive" and "enforcing" are identical. I was not planning on implementing "permissive" until after the code is able to retrieve policy over smb and make an enforcement decision. I did implement "disabled", since it allows users to simply turn off gpo checking altogether.
The current code in ad_gpo_access.c simply does the following:
switch (state->ctx->gpo_access_control_mode) { case GPO_ACCESS_CONTROL_DISABLED: /* do not evaluate gpos; mark request done */ tevent_req_done(req); return; case GPO_ACCESS_CONTROL_PERMISSIVE: case GPO_ACCESS_CONTROL_ENFORCING: /* continue on to evaluate gpos */ break; default: tevent_req_error(req, EINVAL); return; }
Regards, Yassir.
I saw another warnigs from static analysers.
Error: CLANG_WARNING: [#def1] sssd-1.11.90/src/providers/ad/ad_gpo.c:777:14: warning: Function call argument is an uninitialized value # subreq = ad_gpo_process_gpo_send(state, # ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This one is false possitive.
Error: CLANG_WARNING: [#def2] sssd-1.11.90/src/providers/ad/ad_gpo.c:850:15: warning: Access to field 'gpo_guid' results in a dereference of a null pointer # state->dacl_filtered_gpos[i]->gpo_guid); # ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ sssd-1.11.90/src/util/util.h:127:28: note: expanded from macro 'DEBUG' # format, ##__VA_ARGS__); \ # ^ This can be a probelm. I am attaching html report with code flow.
Good catch. I added the following to fix this:
if (state->dacl_filtered_gpos[0] == NULL) { /* since no applicable gpos were found, there is nothing to enforce */ DEBUG(SSSDBG_TRACE_FUNC, "no applicable gpos found after dacl filtering\n"); ret = EOK; goto done; }
Error: CLANG_WARNING: [#def3] sssd-1.11.90/src/providers/ad/ad_gpo.c:2212:5: warning: Value stored to 'ret' is never read # ret = ad_gpo_parse_sd(gp_gpo, el[0].values[0].data, el[0].values[0].length, # ^
This can be solved with adding debug message. 2212 ret = ad_gpo_parse_sd(gp_gpo, el[0].values[0].data, el[0].values[0].length, 2213 &gp_gpo->gpo_sd); 2214 Value stored to 'ret' is never read 2215 ret = ad_gpo_get_gpo_attrs_step(req);
Good catch. I added the following to fix this:
if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "ad_gpo_parse_sd() failed\n"); goto done; }
Thank your patience.
LS
Revised patch is attached.
Regards, Yassir.
From ab9b95c39d7bfc95b3073e87661ecbdabfa8a8de Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Mon, 20 Jan 2014 11:17:06 -0500 Subject: [PATCH] Implemented LDAP component of GPO-based access control
Yassir, thank you. I can confirm that warnings from static analysers and compilers are gone.
The latest version still works well in my tests. Only the dates in the commit header are looking a bit odd.
bye, Sumit
I think that the commit header shows the "magic date" of September 17, 2001 and the author date of January 20, 2014. I made my initial commit on January 20, and then made additional commits, but always squashed them into the initial commit using "git rebase -i". Since the author date does not change with rebases (although the committer date does), the author date has remained unchanged since the initial commit.
Regards, Yassir.
I can fix that before pushing..
On Tue, May 13, 2014 at 06:10:06PM +0200, Jakub Hrozek wrote:
On Tue, May 13, 2014 at 11:57:54AM -0400, Yassir Elley wrote:
----- Original Message -----
On Wed, May 07, 2014 at 04:46:14PM +0200, Lukas Slebodnik wrote:
On (07/05/14 08:47), Yassir Elley wrote:
----- Original Message -----
On (06/05/14 12:42), Yassir Elley wrote: >----- Original Message ----- >> On Tue, Apr 29, 2014 at 07:22:37PM -0400, Yassir Elley wrote: >> > ----- Original Message ----- >> > > ----- Original Message ----- >> > > > On Fri, Apr 18, 2014 at 12:56:27PM -0400, Yassir Elley wrote: >> > >> > Hi Jakub, >> > >> > I have attached a revised cumulative patch that aims to address the >> > review >> > comments. >> > >> > Additionally, I made the following changes en masse: >> > * changed talloc_strdup() to talloc_steal() when applicable (i.e. >> > when >> > dealing with a talloc context) >> > * changed all occurrences of strerror() to sss_strerror() in order >> > to >> > use >> > sssd-specific error strings >> > >> > Thanks, >> > Yassir. >> >> Hi, >> >> I have some more comments, sorry I haven't caught them earlier. I ran >> the >> patch through Coverity and it uncovered several warnings, see below: >> >> Error: COMPILER_WARNING: >> sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function >> 'ad_gpo_target_dn_retrieval_done' >> sssd-1.11.90/src/providers/ad/ad_gpo.c:711:22: warning: assignment >> discards >> 'const' qualifier from pointer target type [enabled by default] >> # state->target_dn = talloc_steal(state, target_dn); >> # ^ >> >> Error: COMPILER_WARNING: >> sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function >> 'ad_gpo_parent_dn' >> sssd-1.11.90/src/providers/ad/ad_gpo.c:902:17: warning: assignment >> discards >> 'const' qualifier from pointer target type [enabled by default] >> # *_parent_dn = talloc_steal(mem_ctx, p); >> # ^ >> >> Error: DEADCODE (CWE-561): >> sssd-1.11.90/src/providers/ad/ad_gpo.c:1053: cond_notnull: Condition >> "ptr >> = >> __coverity_strchr(ptr, 93)", taking true branch. Now the value of >> "ptr" is >> not NULL. >> sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: notnull: At condition >> "ptr == >> NULL", the value of "ptr" cannot be NULL. >> sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: dead_error_condition: >> The >> condition "ptr == NULL" cannot be true. >> sssd-1.11.90/src/providers/ad/ad_gpo.c:1054: dead_error_line: >> Execution >> cannot reach this statement "break;". >> >> Error: DEADCODE (CWE-561): >> sssd-1.11.90/src/providers/ad/ad_gpo.c:1768: cond_notnull: Condition >> "ptr >> = >> __coverity_strchr(ptr, 92)", taking true branch. Now the value of >> "ptr" is >> not NULL. >> sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: notnull: At condition >> "ptr == >> NULL", the value of "ptr" cannot be NULL. >> sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: dead_error_condition: >> The >> condition "ptr == NULL" cannot be true. >> sssd-1.11.90/src/providers/ad/ad_gpo.c:1769: dead_error_line: >> Execution >> cannot reach this statement "break;". >> >> Error: DEADCODE (CWE-561): >> sssd-1.11.90/src/providers/ad/ad_gpo.c:1812: cond_notnull: Condition >> "raw_machine_ext_names_value == NULL", taking false branch. Now the >> value >> of >> "raw_machine_ext_names_value" is not NULL. >> sssd-1.11.90/src/providers/ad/ad_gpo.c:1824: assignment: Assigning: >> "ptr" >> = >> "raw_machine_ext_names_value". >> sssd-1.11.90/src/providers/ad/ad_gpo.c:1825: notnull: At condition >> "ptr == >> NULL", the value of "ptr" cannot be NULL. >> sssd-1.11.90/src/providers/ad/ad_gpo.c:1825: dead_error_condition: >> The >> condition "ptr == NULL" cannot be true. >> sssd-1.11.90/src/providers/ad/ad_gpo.c:1826: dead_error_begin: >> Execution >> cannot reach this statement "ret = 12;". >> >> Error: DEADCODE (CWE-561): >> sssd-1.11.90/src/providers/ad/ad_gpo.c:1830: cond_notnull: Condition >> "ptr >> = >> __coverity_strchr(ptr, 93)", taking true branch. Now the value of >> "ptr" is >> not NULL. >> sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: notnull: At condition >> "ptr == >> NULL", the value of "ptr" cannot be NULL. >> sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: dead_error_condition: >> The >> condition "ptr == NULL" cannot be true. >> sssd-1.11.90/src/providers/ad/ad_gpo.c:1831: dead_error_line: >> Execution >> cannot reach this statement "break;". >> >> Error: COMPILER_WARNING: >> sssd-1.11.90/src/providers/ad/ad_gpo.c: scope_hint: In function >> 'ad_gpo_get_gpo_attrs_done' >> sssd-1.11.90/src/providers/ad/ad_gpo.c:2091:22: warning: assignment >> discards >> 'const' qualifier from pointer target type [enabled by default] >> # gp_gpo->gpo_guid = talloc_steal(gp_gpo, gpo_guid); >> # ^ >> >> Error: COMPILER_WARNING: >> sssd-1.11.90/src/providers/ad/ad_gpo.c:2110:30: warning: assignment >> discards >> 'const' qualifier from pointer target type [enabled by default] >> # gp_gpo->gpo_display_name = talloc_steal(gp_gpo, >> gpo_display_name); >> # ^ >> >> Error: COMPILER_WARNING: >> sssd-1.11.90/src/providers/ad/ad_gpo.c:2131:19: warning: assignment >> discards >> 'const' qualifier from pointer target type [enabled by default] >> # file_sys_path = talloc_steal(gp_gpo, raw_file_sys_path); >> # > > I have fixed the DEADCODE conditions detected by coverity and attached > a revised cumulative patch. With regard to the COMPILER_WARNING, Lukas > had > already commented on these previously, and I had already fixed them > in the most recent patch. > Yes warnings were fixed in previous version and I could not see any problems with DEADCODE.
>> >> Also, when I set ad_gpo_access_control=permissive and tried to log in >> as >> Administrator, I was kicked out. I thought that permissive >> setting would ignore the errors, but maybe I stumbled upon something >> unexpected? > > This is expected behavior because I have not implemented "permissive" > yet. > For now, "permissive" and "enforcing" are identical. I was not > planning > on implementing "permissive" until after the code is able to retrieve > policy > over smb and make an enforcement decision. I did implement "disabled", > since > it allows users to simply turn off gpo checking altogether. > >The current code in ad_gpo_access.c simply does the following: > > switch (state->ctx->gpo_access_control_mode) { > case GPO_ACCESS_CONTROL_DISABLED: > /* do not evaluate gpos; mark request done */ > tevent_req_done(req); > return; > case GPO_ACCESS_CONTROL_PERMISSIVE: > case GPO_ACCESS_CONTROL_ENFORCING: > /* continue on to evaluate gpos */ > break; > default: > tevent_req_error(req, EINVAL); > return; > } > >Regards, >Yassir.
I saw another warnigs from static analysers.
Error: CLANG_WARNING: [#def1] sssd-1.11.90/src/providers/ad/ad_gpo.c:777:14: warning: Function call argument is an uninitialized value # subreq = ad_gpo_process_gpo_send(state, # ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This one is false possitive.
Error: CLANG_WARNING: [#def2] sssd-1.11.90/src/providers/ad/ad_gpo.c:850:15: warning: Access to field 'gpo_guid' results in a dereference of a null pointer # state->dacl_filtered_gpos[i]->gpo_guid); # ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ sssd-1.11.90/src/util/util.h:127:28: note: expanded from macro 'DEBUG' # format, ##__VA_ARGS__); \ # ^ This can be a probelm. I am attaching html report with code flow.
Good catch. I added the following to fix this:
if (state->dacl_filtered_gpos[0] == NULL) { /* since no applicable gpos were found, there is nothing to enforce */ DEBUG(SSSDBG_TRACE_FUNC, "no applicable gpos found after dacl filtering\n"); ret = EOK; goto done; }
Error: CLANG_WARNING: [#def3] sssd-1.11.90/src/providers/ad/ad_gpo.c:2212:5: warning: Value stored to 'ret' is never read # ret = ad_gpo_parse_sd(gp_gpo, el[0].values[0].data, el[0].values[0].length, # ^
This can be solved with adding debug message. 2212 ret = ad_gpo_parse_sd(gp_gpo, el[0].values[0].data, el[0].values[0].length, 2213 &gp_gpo->gpo_sd); 2214 Value stored to 'ret' is never read 2215 ret = ad_gpo_get_gpo_attrs_step(req);
Good catch. I added the following to fix this:
if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "ad_gpo_parse_sd() failed\n"); goto done; }
Thank your patience.
LS
Revised patch is attached.
Regards, Yassir.
From ab9b95c39d7bfc95b3073e87661ecbdabfa8a8de Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Mon, 20 Jan 2014 11:17:06 -0500 Subject: [PATCH] Implemented LDAP component of GPO-based access control
Yassir, thank you. I can confirm that warnings from static analysers and compilers are gone.
The latest version still works well in my tests. Only the dates in the commit header are looking a bit odd.
bye, Sumit
I think that the commit header shows the "magic date" of September 17, 2001 and the author date of January 20, 2014. I made my initial commit on January 20, and then made additional commits, but always squashed them into the initial commit using "git rebase -i". Since the author date does not change with rebases (although the committer date does), the author date has remained unchanged since the initial commit.
Regards, Yassir.
I can fix that before pushing..
Hm, actually, both AuthorDate and CommitDate show up as 2014, so I left the date as Yassir sent the patch. I hope that's fine.
btw, the patch also has an ACK from me.
Pushed to master in 60cab26b12df9a2153823972cde0c38ca86e01b9
Great work!
sssd-devel@lists.fedorahosted.org