Hi,
The attached patch adds support for gpo version checking and for conditional downloading of policy files. In order to get that to work, this patch also replaces the deprecated smb functions (e.g. smbc_open, smbc_read, etc) I had been using (not knowing they were deprecated) with the newer smb functions, such as "smbc_getFunctionOpen(smbc_ctx)(smbc_ctx, smb_uri, O_RDONLY, 0755);".
Before this patch, the policy files were being unconditionally downloaded every time. After this patch, the backend sends the cached_gpt_version (from a previous transaction, if any) to the gpo_child. The gpo_child downloads the per-GPO GPT.INI file, from which it extracts the sysvol_gpt_version. Only if the sysvol_gpt_version is greater than the cached_gpt_version (or if there is no cached_gpt_version) does the gpo_child download the policy files. The gpo_child returns the sysvol_gpt_version to the backend, which stores it in the cache.
Note that, while a GPO has both a SYSVOL-stored Group Policy Template (gpt) version, as well as an LDAP-stored Group Policy Container (gpc) version (which should be equal), it is best practice to use the gpt version for version checking b/c SYSVOL replication is typically much faster than AD replication. In other words, the gpt version is quickly up-to-date on all DCs for a given domain, while the gpc version may be stale for some time after a change is made.
In order to keep the gpo_child as simple as possible, the gpo_child does not interact with the sysdb cache at all (only the backend does). However, the gpo_child does need to parse the GPT.INI file in order to determine whether to download the policy files. As such, the gpo_child includes GPT.INI file parsing functionality (which is per-GPO, and not CSE-specific).
Note that this patch does not add support for offline mode, which will be implemented in a subsequent patch.
Regards, Yassir.
On Sun, Jul 20, 2014 at 05:48:45PM -0400, Yassir Elley wrote:
Hi,
The attached patch adds support for gpo version checking and for conditional downloading of policy files. In order to get that to work, this patch also replaces the deprecated smb functions (e.g. smbc_open, smbc_read, etc) I had been using (not knowing they were deprecated) with the newer smb functions, such as "smbc_getFunctionOpen(smbc_ctx)(smbc_ctx, smb_uri, O_RDONLY, 0755);".
Before this patch, the policy files were being unconditionally downloaded every time. After this patch, the backend sends the cached_gpt_version (from a previous transaction, if any) to the gpo_child. The gpo_child downloads the per-GPO GPT.INI file, from which it extracts the sysvol_gpt_version. Only if the sysvol_gpt_version is greater than the cached_gpt_version (or if there is no cached_gpt_version) does the gpo_child download the policy files. The gpo_child returns the sysvol_gpt_version to the backend, which stores it in the cache.
Note that, while a GPO has both a SYSVOL-stored Group Policy Template (gpt) version, as well as an LDAP-stored Group Policy Container (gpc) version (which should be equal), it is best practice to use the gpt version for version checking b/c SYSVOL replication is typically much faster than AD replication. In other words, the gpt version is quickly up-to-date on all DCs for a given domain, while the gpc version may be stale for some time after a change is made.
In order to keep the gpo_child as simple as possible, the gpo_child does not interact with the sysdb cache at all (only the backend does). However, the gpo_child does need to parse the GPT.INI file in order to determine whether to download the policy files. As such, the gpo_child includes GPT.INI file parsing functionality (which is per-GPO, and not CSE-specific).
Note that this patch does not add support for offline mode, which will be implemented in a subsequent patch.
Regards, Yassir.
From cabea44761b61e75fbd7355ab9ed8a346815e57d Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Mon, 7 Jul 2014 08:32:25 -0400 Subject: [PATCH] AD-GPO: Cache gpo version; only download policy files if version changes.
In order to speed up the review process, here are my notes based on reading the code. I haven't done any testing yet. See my comments inline:
Makefile.am | 1 + src/db/sysdb.h | 29 +++ src/db/sysdb_gpo.c | 270 +++++++++++++++++++++++++++
I think it would be better to have two patches -- one with the sysdb API and one with the AD changes.
src/providers/ad/ad_gpo.c | 307 +++++++++++++++++++----------- src/providers/ad/ad_gpo_child.c | 404 ++++++++++++++++++++++++++++++++-------- 5 files changed, 818 insertions(+), 193 deletions(-) create mode 100644 src/db/sysdb_gpo.c
diff --git a/Makefile.am b/Makefile.am index e3592868ce29b569a71f6ad74bc24cc617301b34..d80da00a47fc118b19aee4ff6b40de5fdd0ce792 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2297,6 +2297,7 @@ libsss_ad_la_SOURCES = \ src/providers/ad/ad_gpo.c \ src/providers/ad/ad_gpo.h \ src/providers/ad/ad_gpo_ndr.c \
- src/db/sysdb_gpo.c \
I would prefer if sysdb_gpo was part of libsss_util, similar to how the other sysdb modules are handled. If you only want to have this code as part of the AD provider to now grow libsss_util any more, we can alternatively rename the file to src/providers/ad/ad_gpo_util.c or similar, but I think a separate module is OK and more reusable in the future.
src/providers/ad/ad_opts.h \ src/providers/ad/ad_srv.c \ src/providers/ad/ad_subdomains.c \diff --git a/src/db/sysdb.h b/src/db/sysdb.h index 17cd5110c9bdafd8d7b18e621188523ed41e5c8a..1bc0ec48e06954892f96f0b513e6225eb65dd483 100644
[....]
diff --git a/src/db/sysdb_gpo.c b/src/db/sysdb_gpo.c new file mode 100644 index 0000000000000000000000000000000000000000..86907733a0908ec85e7a9069d37864a41b1b63cd --- /dev/null +++ b/src/db/sysdb_gpo.c @@ -0,0 +1,270 @@ +/*
- SSSD
- Authors:
Yassir Elley <yelley@redhat.com>- Copyright (C) 2014 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/.
+*/
+#include "db/sysdb.h" +#include "db/sysdb_private.h"
+static struct ldb_dn * +sysdb_gpo_dn(TALLOC_CTX *mem_ctx, struct sss_domain_info *domain,
const char *gpo_guid)+{
- errno_t ret;
- char *clean_gpo_guid;
- struct ldb_dn *dn;
- ret = sysdb_dn_sanitize(NULL, gpo_guid, &clean_gpo_guid);
- if (ret != EOK) {
return NULL;- }
- DEBUG(SSSDBG_TRACE_FUNC, SYSDB_TMPL_GPO"\n", clean_gpo_guid, domain->name);
- dn = ldb_dn_new_fmt(mem_ctx, domain->sysdb->ldb, SYSDB_TMPL_GPO,
clean_gpo_guid, domain->name);- talloc_free(clean_gpo_guid);
- return dn;
+}
+errno_t +sysdb_gpo_store_gpo(struct sss_domain_info *domain,
const char *dom_name,
Why do you have a separate dom_name parameter alongside domain? Can you use domain->name in the function instead?
const char *gpo_guid,int gpo_version)+{
- errno_t ret, sret;
- int lret;
- struct ldb_message *update_msg;
- struct ldb_message **msgs;
- struct ldb_dn *dn;
- static const char *attrs[] = SYSDB_GPO_ATTRS;
- size_t count;
- bool in_transaction = false;
- TALLOC_CTX *tmp_ctx;
- tmp_ctx = talloc_new(NULL);
- if (!tmp_ctx) return ENOMEM;
Please put a blank line here for better readability.
- dn = sysdb_gpo_dn(tmp_ctx, domain, gpo_guid);
- if (!dn) {
ret = ENOMEM;goto done;- }
- update_msg = ldb_msg_new(tmp_ctx);
- if (!update_msg) {
ret = ENOMEM;goto done;- }
- update_msg->dn = dn;
I think the memory hierarchy would be cleaner if you did: update_msg = ldb_msg_new(tmp_ctx); update_msg->dn = sysdb_gpo_dn(update_msg, domain, gpo_guid);
This was update_msg->dn is not only part of update_msg structure logically, but also as far as talloc hierarchy goes. It's not really important in this function, but it's a good practice.
- ret = sysdb_transaction_start(domain->sysdb);
- if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");goto done;- }
- in_transaction = true;
- dn = update_msg->dn;
- tmp_ctx = talloc_new(NULL);
- if (!tmp_ctx) return ENOMEM;
You overwrite both dn and tmp_ctx above.
- /* Check for an existing gpo_guid entry */
- ret = sysdb_search_entry(tmp_ctx, domain->sysdb, dn, LDB_SCOPE_BASE,
NULL, attrs, &count, &msgs);- if (ret != EOK && ret != ENOENT) goto done;
I realize that here the error code can be either EOK or ENOENT and nothing else, but to me it's more readable to write:
if (ret == ENOENT) { /* new gpo */ } else if (ret == EOK) { /* update existing gpo */ } else { /* error! */ }
But this is more of a suggestion, feel free to keep the code the way it is if you disagree.
- if (ret == EOK && count != 1) {
/* More than one reply for a base search? */ret = EIO;goto done;- } else if (ret == ENOENT) {
/* Create new GPO */DEBUG(SSSDBG_TRACE_FUNC,"Adding new GPO [gpo_guid:%s][gpo_version:%d]\n",gpo_guid, gpo_version);/* Add the objectClass */lret = ldb_msg_add_empty(update_msg, SYSDB_OBJECTCLASS,LDB_FLAG_MOD_ADD,NULL);if (lret != LDB_SUCCESS) {ret = sysdb_error_to_errno(lret);goto done;}lret = ldb_msg_add_string(update_msg, SYSDB_OBJECTCLASS,SYSDB_GPO_OC);if (lret != LDB_SUCCESS) {ret = sysdb_error_to_errno(lret);goto done;}/* Add the GPO GUID */lret = ldb_msg_add_empty(update_msg, SYSDB_GPO_GUID_ATTR,LDB_FLAG_MOD_ADD,NULL);if (lret != LDB_SUCCESS) {ret = sysdb_error_to_errno(lret);goto done;}lret = ldb_msg_add_string(update_msg, SYSDB_GPO_GUID_ATTR, gpo_guid);if (lret != LDB_SUCCESS) {ret = sysdb_error_to_errno(lret);goto done;}/* Add the Version */lret = ldb_msg_add_empty(update_msg, SYSDB_GPO_VERSION_ATTR,LDB_FLAG_MOD_ADD,NULL);if (lret != LDB_SUCCESS) {ret = sysdb_error_to_errno(lret);goto done;}lret = ldb_msg_add_fmt(update_msg, SYSDB_GPO_VERSION_ATTR,"%d", gpo_version);if (lret != LDB_SUCCESS) {ret = sysdb_error_to_errno(lret);goto done;}lret = ldb_add(domain->sysdb->ldb, update_msg);if (lret != LDB_SUCCESS) {DEBUG(SSSDBG_MINOR_FAILURE,"Failed to add GPO: [%s]\n",ldb_strerror(lret));ret = sysdb_error_to_errno(lret);goto done;}- } else {
/* Update the existing GPO */DEBUG(SSSDBG_TRACE_FUNC,"Updating new GPO [%s][%s]\n", dom_name, gpo_guid);/* Add the Version */lret = ldb_msg_add_empty(update_msg, SYSDB_GPO_VERSION_ATTR,LDB_FLAG_MOD_ADD,
Are you sure you want to add another version string and make it multivalued as opposed to replacing a version string? Please note this is a genuine question, but if you do intend to make the attribute multivalued maybe it's worth adding acomment.
NULL);if (lret != LDB_SUCCESS) {ret = sysdb_error_to_errno(lret);goto done;}lret = ldb_msg_add_fmt(update_msg, SYSDB_GPO_VERSION_ATTR,"%d", gpo_version);if (lret != LDB_SUCCESS) {ret = sysdb_error_to_errno(lret);goto done;}lret = sss_ldb_modify_permissive(domain->sysdb->ldb, update_msg);if (lret != LDB_SUCCESS) {DEBUG(SSSDBG_MINOR_FAILURE,"Failed to modify GPO: [%s]\n", ldb_strerror(lret));ret = sysdb_error_to_errno(lret);goto done;}- }
- ret = sysdb_transaction_commit(domain->sysdb);
- if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,"Could not commit transaction: [%s]\n", strerror(ret));goto done;- }
- in_transaction = false;
+done:
- if (in_transaction) {
sret = sysdb_transaction_cancel(domain->sysdb);if (sret != EOK) {DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n");}- }
- talloc_free(tmp_ctx);
- return ret;
+}
+errno_t +sysdb_gpo_get_gpo(TALLOC_CTX *mem_ctx,
struct sss_domain_info *domain,const char *gpo_guid,struct ldb_result **_result)+{
- errno_t ret;
- int lret;
- struct ldb_dn *base_dn;
- TALLOC_CTX *tmp_ctx;
- struct ldb_result *res;
- const char *attrs[] = SYSDB_GPO_ATTRS;
- tmp_ctx = talloc_new(NULL);
- if (!tmp_ctx) return ENOMEM;
- DEBUG(SSSDBG_TRACE_FUNC, SYSDB_TMPL_GPO_BASE"\n", domain->name);
- base_dn = ldb_dn_new_fmt(tmp_ctx, domain->sysdb->ldb,
SYSDB_TMPL_GPO_BASE,domain->name);- if (!base_dn) {
ret = ENOMEM;goto done;- }
Nitpick - while you're changing the module anyway, please put a blank line here, too.
- lret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, base_dn,
LDB_SCOPE_SUBTREE, attrs, SYSDB_GPO_FILTER, gpo_guid);- if (lret) {
DEBUG(SSSDBG_MINOR_FAILURE,"Could not locate GPO: [%s]\n",ldb_strerror(lret));ret = sysdb_error_to_errno(lret);goto done;- }
- if (res->count > 1) {
DEBUG(SSSDBG_CRIT_FAILURE, "Search for GUID [%s] returned more than " \"one object.\n", gpo_guid);ret = EINVAL;goto done;- } else if (res->count == 0) {
ret = ENOENT;goto done;- }
- *_result = talloc_steal(mem_ctx, res);
- ret = EOK;
+done:
- if (ret == ENOENT) {
DEBUG(SSSDBG_TRACE_ALL, "No such entry.\n");- } else if (ret) {
DEBUG(SSSDBG_OP_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));- }
- talloc_free(tmp_ctx);
- return ret;
+} diff --git a/src/providers/ad/ad_gpo.c b/src/providers/ad/ad_gpo.c index e33ea72e9fdfb053655a5ab7c63de735da0f6954..470a6fcfcc45c5c66186c6406eb2966a0a3fd8bd 100644 --- a/src/providers/ad/ad_gpo.c +++ b/src/providers/ad/ad_gpo.c @@ -86,7 +86,7 @@ #define DENY_LOGON_LOCALLY "SeDenyInteractiveLogonRight"
#define GP_EXT_GUID_SECURITY "{827D319E-6EAC-11D2-A4EA-00C04F79F83A}" -#define GP_EXT_GUID_SECURITY_SUFFIX "/Microsoft/Windows NT/SecEdit/GptTmpl.inf" +#define GP_EXT_GUID_SECURITY_SUFFIX "/Machine/Microsoft/Windows NT/SecEdit/GptTmpl.inf"
#ifndef SSSD_LIBEXEC_PATH #error "SSSD_LIBEXEC_PATH not defined" @@ -115,9 +115,10 @@ struct gp_gpo { const char *gpo_dn; const char *gpo_guid; const char *gpo_display_name;
- const char *gpo_file_sys_path;
- const char *gpo_unix_path;
- uint32_t gpo_container_version;
- const char *smb_server;
- const char *smb_share;
- const char *smb_path;
- uint32_t gpc_version; const char **gpo_cse_guids; int num_gpo_cse_guids; int gpo_func_version;
@@ -156,10 +157,15 @@ int ad_gpo_process_gpo_recv(struct tevent_req *req, int *num_candidate_gpos); struct tevent_req *ad_gpo_process_cse_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
char *cse_smb_uri,char *cse_unix_path);
struct sss_domain_info *domain,const char *smb_server,const char *smb_share,const char *smb_path,const char *smb_cse_suffix,const char *gpo_guid);int ad_gpo_process_cse_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
int *_sysvol_gpt_version, int *_allowed_size, char ***_allowed_sids, int *_denied_size,@@ -1298,8 +1304,6 @@ ad_gpo_cse_step(struct tevent_req *req) { struct tevent_req *subreq; struct ad_gpo_access_state *state;
char *cse_smb_uri;
char *cse_unix_path; int i = 0;
state = tevent_req_data(req, struct ad_gpo_access_state);
@@ -1319,15 +1323,19 @@ ad_gpo_cse_step(struct tevent_req *req) state->cse_gpo_index, i, cse_filtered_gpo->gpo_cse_guids[i]); }
- cse_smb_uri = talloc_asprintf(state, "%s%s",
cse_filtered_gpo->gpo_file_sys_path,GP_EXT_GUID_SECURITY_SUFFIX);
- DEBUG(SSSDBG_TRACE_FUNC, "smb_server: %s\n", cse_filtered_gpo->smb_server);
- DEBUG(SSSDBG_TRACE_FUNC, "smb_share: %s\n", cse_filtered_gpo->smb_share);
- DEBUG(SSSDBG_TRACE_FUNC, "smb_path: %s\n", cse_filtered_gpo->smb_path);
- DEBUG(SSSDBG_TRACE_FUNC, "gpo_guid: %s\n", cse_filtered_gpo->gpo_guid);
- cse_unix_path = talloc_asprintf(state, "%s%s",
cse_filtered_gpo->gpo_unix_path,GP_EXT_GUID_SECURITY_SUFFIX);- subreq = ad_gpo_process_cse_send(state, state->ev, cse_smb_uri, cse_unix_path);
subreq = ad_gpo_process_cse_send(state,
state->ev,state->domain,cse_filtered_gpo->smb_server,cse_filtered_gpo->smb_share,cse_filtered_gpo->smb_path,GP_EXT_GUID_SECURITY_SUFFIX,cse_filtered_gpo->gpo_guid);tevent_req_set_callback(subreq, ad_gpo_cse_done, req); return EAGAIN;
@@ -1348,6 +1356,7 @@ ad_gpo_cse_done(struct tevent_req *subreq) struct tevent_req *req; struct ad_gpo_access_state *state; int ret;
- int sysvol_gpt_version; char **allowed_sids; int allowed_size; char **denied_sids;
@@ -1356,11 +1365,24 @@ ad_gpo_cse_done(struct tevent_req *subreq) req = tevent_req_callback_data(subreq, struct tevent_req); state = tevent_req_data(req, struct ad_gpo_access_state);
- ret = ad_gpo_process_cse_recv(subreq, state, &allowed_size,
struct gp_gpo *cse_filtered_gpo =
state->cse_filtered_gpos[state->cse_gpo_index];const char *gpo_guid = cse_filtered_gpo->gpo_guid;
DEBUG(SSSDBG_TRACE_FUNC, "gpo_guid: %s\n", gpo_guid);
ret = ad_gpo_process_cse_recv(subreq, state, &sysvol_gpt_version, &allowed_size, &allowed_sids, &denied_size, &denied_sids);
talloc_zfree(subreq);
DEBUG(SSSDBG_TRACE_FUNC, "sysvol_gpt_version: %d\n", sysvol_gpt_version);
ret = sysdb_gpo_store_gpo(state->domain, state->domain->name, gpo_guid,
sysvol_gpt_version);if (ret != EOK) { /* TBD: handle ret error */ goto done;
@@ -2274,48 +2296,46 @@ ad_gpo_populate_candidate_gpos(TALLOC_CTX *mem_ctx, }
/*
- This function converts the input_path to an smb uri and a unix_path, which
- are used to populate the _smb_uri and _unix_path output parameters,
- respectively.
- This function parses the input_path into its components, replaces each
- forward slash ('') with a back slash ('/'), and populates the output params.
I think the forward slash ans backslash are the other way around :-)
I'll continue with the review tomorrow, sorry, but I'm running out of time today, I just wanted to send out what I was able to review so far in case you wanted to do some fixes in tree. Feel free to send updated patches if you agree with some of the points I raised (or not if you prefer to send the update in a single large batch).
Thank you for the patch!
To be continued :-)
----- Original Message -----
On Sun, Jul 20, 2014 at 05:48:45PM -0400, Yassir Elley wrote:
Hi,
The attached patch adds support for gpo version checking and for conditional downloading of policy files. In order to get that to work, this patch also replaces the deprecated smb functions (e.g. smbc_open, smbc_read, etc) I had been using (not knowing they were deprecated) with the newer smb functions, such as "smbc_getFunctionOpen(smbc_ctx)(smbc_ctx, smb_uri, O_RDONLY, 0755);".
Before this patch, the policy files were being unconditionally downloaded every time. After this patch, the backend sends the cached_gpt_version (from a previous transaction, if any) to the gpo_child. The gpo_child downloads the per-GPO GPT.INI file, from which it extracts the sysvol_gpt_version. Only if the sysvol_gpt_version is greater than the cached_gpt_version (or if there is no cached_gpt_version) does the gpo_child download the policy files. The gpo_child returns the sysvol_gpt_version to the backend, which stores it in the cache.
Note that, while a GPO has both a SYSVOL-stored Group Policy Template (gpt) version, as well as an LDAP-stored Group Policy Container (gpc) version (which should be equal), it is best practice to use the gpt version for version checking b/c SYSVOL replication is typically much faster than AD replication. In other words, the gpt version is quickly up-to-date on all DCs for a given domain, while the gpc version may be stale for some time after a change is made.
In order to keep the gpo_child as simple as possible, the gpo_child does not interact with the sysdb cache at all (only the backend does). However, the gpo_child does need to parse the GPT.INI file in order to determine whether to download the policy files. As such, the gpo_child includes GPT.INI file parsing functionality (which is per-GPO, and not CSE-specific).
Note that this patch does not add support for offline mode, which will be implemented in a subsequent patch.
Regards, Yassir.
From cabea44761b61e75fbd7355ab9ed8a346815e57d Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Mon, 7 Jul 2014 08:32:25 -0400 Subject: [PATCH] AD-GPO: Cache gpo version; only download policy files if version changes.
In order to speed up the review process, here are my notes based on reading the code. I haven't done any testing yet. See my comments inline:
Makefile.am | 1 + src/db/sysdb.h | 29 +++ src/db/sysdb_gpo.c | 270 +++++++++++++++++++++++++++
I think it would be better to have two patches -- one with the sysdb API and one with the AD changes.
src/providers/ad/ad_gpo.c | 307 +++++++++++++++++++----------- src/providers/ad/ad_gpo_child.c | 404 ++++++++++++++++++++++++++++++++-------- 5 files changed, 818 insertions(+), 193 deletions(-) create mode 100644 src/db/sysdb_gpo.c
diff --git a/Makefile.am b/Makefile.am index e3592868ce29b569a71f6ad74bc24cc617301b34..d80da00a47fc118b19aee4ff6b40de5fdd0ce792 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2297,6 +2297,7 @@ libsss_ad_la_SOURCES = \ src/providers/ad/ad_gpo.c \ src/providers/ad/ad_gpo.h \ src/providers/ad/ad_gpo_ndr.c \
- src/db/sysdb_gpo.c \
I would prefer if sysdb_gpo was part of libsss_util, similar to how the other sysdb modules are handled. If you only want to have this code as part of the AD provider to now grow libsss_util any more, we can alternatively rename the file to src/providers/ad/ad_gpo_util.c or similar, but I think a separate module is OK and more reusable in the future.
src/providers/ad/ad_opts.h \ src/providers/ad/ad_srv.c \ src/providers/ad/ad_subdomains.c \diff --git a/src/db/sysdb.h b/src/db/sysdb.h index 17cd5110c9bdafd8d7b18e621188523ed41e5c8a..1bc0ec48e06954892f96f0b513e6225eb65dd483 100644
[....]
diff --git a/src/db/sysdb_gpo.c b/src/db/sysdb_gpo.c new file mode 100644 index 0000000000000000000000000000000000000000..86907733a0908ec85e7a9069d37864a41b1b63cd --- /dev/null +++ b/src/db/sysdb_gpo.c @@ -0,0 +1,270 @@ +/*
- SSSD
- Authors:
Yassir Elley <yelley@redhat.com>- Copyright (C) 2014 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/.
+*/
+#include "db/sysdb.h" +#include "db/sysdb_private.h"
+static struct ldb_dn * +sysdb_gpo_dn(TALLOC_CTX *mem_ctx, struct sss_domain_info *domain,
const char *gpo_guid)+{
- errno_t ret;
- char *clean_gpo_guid;
- struct ldb_dn *dn;
- ret = sysdb_dn_sanitize(NULL, gpo_guid, &clean_gpo_guid);
- if (ret != EOK) {
return NULL;- }
- DEBUG(SSSDBG_TRACE_FUNC, SYSDB_TMPL_GPO"\n", clean_gpo_guid,
domain->name);
- dn = ldb_dn_new_fmt(mem_ctx, domain->sysdb->ldb, SYSDB_TMPL_GPO,
clean_gpo_guid, domain->name);- talloc_free(clean_gpo_guid);
- return dn;
+}
+errno_t +sysdb_gpo_store_gpo(struct sss_domain_info *domain,
const char *dom_name,Why do you have a separate dom_name parameter alongside domain? Can you use domain->name in the function instead?
const char *gpo_guid,int gpo_version)+{
- errno_t ret, sret;
- int lret;
- struct ldb_message *update_msg;
- struct ldb_message **msgs;
- struct ldb_dn *dn;
- static const char *attrs[] = SYSDB_GPO_ATTRS;
- size_t count;
- bool in_transaction = false;
- TALLOC_CTX *tmp_ctx;
- tmp_ctx = talloc_new(NULL);
- if (!tmp_ctx) return ENOMEM;
Please put a blank line here for better readability.
- dn = sysdb_gpo_dn(tmp_ctx, domain, gpo_guid);
- if (!dn) {
ret = ENOMEM;goto done;- }
- update_msg = ldb_msg_new(tmp_ctx);
- if (!update_msg) {
ret = ENOMEM;goto done;- }
- update_msg->dn = dn;
I think the memory hierarchy would be cleaner if you did: update_msg = ldb_msg_new(tmp_ctx); update_msg->dn = sysdb_gpo_dn(update_msg, domain, gpo_guid);
This was update_msg->dn is not only part of update_msg structure logically, but also as far as talloc hierarchy goes. It's not really important in this function, but it's a good practice.
- ret = sysdb_transaction_start(domain->sysdb);
- if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");goto done;- }
- in_transaction = true;
- dn = update_msg->dn;
- tmp_ctx = talloc_new(NULL);
- if (!tmp_ctx) return ENOMEM;
You overwrite both dn and tmp_ctx above.
- /* Check for an existing gpo_guid entry */
- ret = sysdb_search_entry(tmp_ctx, domain->sysdb, dn, LDB_SCOPE_BASE,
NULL, attrs, &count, &msgs);- if (ret != EOK && ret != ENOENT) goto done;
I realize that here the error code can be either EOK or ENOENT and nothing else, but to me it's more readable to write:
if (ret == ENOENT) { /* new gpo */ } else if (ret == EOK) { /* update existing gpo */ } else { /* error! */ }But this is more of a suggestion, feel free to keep the code the way it is if you disagree.
- if (ret == EOK && count != 1) {
/* More than one reply for a base search? */ret = EIO;goto done;- } else if (ret == ENOENT) {
/* Create new GPO */DEBUG(SSSDBG_TRACE_FUNC,"Adding new GPO [gpo_guid:%s][gpo_version:%d]\n",gpo_guid, gpo_version);/* Add the objectClass */lret = ldb_msg_add_empty(update_msg, SYSDB_OBJECTCLASS,LDB_FLAG_MOD_ADD,NULL);if (lret != LDB_SUCCESS) {ret = sysdb_error_to_errno(lret);goto done;}lret = ldb_msg_add_string(update_msg, SYSDB_OBJECTCLASS,SYSDB_GPO_OC);if (lret != LDB_SUCCESS) {ret = sysdb_error_to_errno(lret);goto done;}/* Add the GPO GUID */lret = ldb_msg_add_empty(update_msg, SYSDB_GPO_GUID_ATTR,LDB_FLAG_MOD_ADD,NULL);if (lret != LDB_SUCCESS) {ret = sysdb_error_to_errno(lret);goto done;}lret = ldb_msg_add_string(update_msg, SYSDB_GPO_GUID_ATTR,gpo_guid);
if (lret != LDB_SUCCESS) {ret = sysdb_error_to_errno(lret);goto done;}/* Add the Version */lret = ldb_msg_add_empty(update_msg, SYSDB_GPO_VERSION_ATTR,LDB_FLAG_MOD_ADD,NULL);if (lret != LDB_SUCCESS) {ret = sysdb_error_to_errno(lret);goto done;}lret = ldb_msg_add_fmt(update_msg, SYSDB_GPO_VERSION_ATTR,"%d", gpo_version);if (lret != LDB_SUCCESS) {ret = sysdb_error_to_errno(lret);goto done;}lret = ldb_add(domain->sysdb->ldb, update_msg);if (lret != LDB_SUCCESS) {DEBUG(SSSDBG_MINOR_FAILURE,"Failed to add GPO: [%s]\n",ldb_strerror(lret));ret = sysdb_error_to_errno(lret);goto done;}- } else {
/* Update the existing GPO */DEBUG(SSSDBG_TRACE_FUNC,"Updating new GPO [%s][%s]\n", dom_name, gpo_guid);/* Add the Version */lret = ldb_msg_add_empty(update_msg, SYSDB_GPO_VERSION_ATTR,LDB_FLAG_MOD_ADD,Are you sure you want to add another version string and make it multivalued as opposed to replacing a version string? Please note this is a genuine question, but if you do intend to make the attribute multivalued maybe it's worth adding acomment.
NULL);if (lret != LDB_SUCCESS) {ret = sysdb_error_to_errno(lret);goto done;}lret = ldb_msg_add_fmt(update_msg, SYSDB_GPO_VERSION_ATTR,"%d", gpo_version);if (lret != LDB_SUCCESS) {ret = sysdb_error_to_errno(lret);goto done;}lret = sss_ldb_modify_permissive(domain->sysdb->ldb, update_msg);if (lret != LDB_SUCCESS) {DEBUG(SSSDBG_MINOR_FAILURE,"Failed to modify GPO: [%s]\n", ldb_strerror(lret));ret = sysdb_error_to_errno(lret);goto done;}- }
- ret = sysdb_transaction_commit(domain->sysdb);
- if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,"Could not commit transaction: [%s]\n", strerror(ret));goto done;- }
- in_transaction = false;
+done:
- if (in_transaction) {
sret = sysdb_transaction_cancel(domain->sysdb);if (sret != EOK) {DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n");}- }
- talloc_free(tmp_ctx);
- return ret;
+}
+errno_t +sysdb_gpo_get_gpo(TALLOC_CTX *mem_ctx,
struct sss_domain_info *domain,const char *gpo_guid,struct ldb_result **_result)+{
- errno_t ret;
- int lret;
- struct ldb_dn *base_dn;
- TALLOC_CTX *tmp_ctx;
- struct ldb_result *res;
- const char *attrs[] = SYSDB_GPO_ATTRS;
- tmp_ctx = talloc_new(NULL);
- if (!tmp_ctx) return ENOMEM;
- DEBUG(SSSDBG_TRACE_FUNC, SYSDB_TMPL_GPO_BASE"\n", domain->name);
- base_dn = ldb_dn_new_fmt(tmp_ctx, domain->sysdb->ldb,
SYSDB_TMPL_GPO_BASE,domain->name);- if (!base_dn) {
ret = ENOMEM;goto done;- }
Nitpick - while you're changing the module anyway, please put a blank line here, too.
- lret = ldb_search(domain->sysdb->ldb, tmp_ctx, &res, base_dn,
LDB_SCOPE_SUBTREE, attrs, SYSDB_GPO_FILTER,gpo_guid);
- if (lret) {
DEBUG(SSSDBG_MINOR_FAILURE,"Could not locate GPO: [%s]\n",ldb_strerror(lret));ret = sysdb_error_to_errno(lret);goto done;- }
- if (res->count > 1) {
DEBUG(SSSDBG_CRIT_FAILURE, "Search for GUID [%s] returned morethan " \
"one object.\n", gpo_guid);ret = EINVAL;goto done;- } else if (res->count == 0) {
ret = ENOENT;goto done;- }
- *_result = talloc_steal(mem_ctx, res);
- ret = EOK;
+done:
- if (ret == ENOENT) {
DEBUG(SSSDBG_TRACE_ALL, "No such entry.\n");- } else if (ret) {
DEBUG(SSSDBG_OP_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));- }
- talloc_free(tmp_ctx);
- return ret;
+} diff --git a/src/providers/ad/ad_gpo.c b/src/providers/ad/ad_gpo.c index e33ea72e9fdfb053655a5ab7c63de735da0f6954..470a6fcfcc45c5c66186c6406eb2966a0a3fd8bd 100644 --- a/src/providers/ad/ad_gpo.c +++ b/src/providers/ad/ad_gpo.c @@ -86,7 +86,7 @@ #define DENY_LOGON_LOCALLY "SeDenyInteractiveLogonRight"
#define GP_EXT_GUID_SECURITY "{827D319E-6EAC-11D2-A4EA-00C04F79F83A}" -#define GP_EXT_GUID_SECURITY_SUFFIX "/Microsoft/Windows NT/SecEdit/GptTmpl.inf" +#define GP_EXT_GUID_SECURITY_SUFFIX "/Machine/Microsoft/Windows NT/SecEdit/GptTmpl.inf"
#ifndef SSSD_LIBEXEC_PATH #error "SSSD_LIBEXEC_PATH not defined" @@ -115,9 +115,10 @@ struct gp_gpo { const char *gpo_dn; const char *gpo_guid; const char *gpo_display_name;
- const char *gpo_file_sys_path;
- const char *gpo_unix_path;
- uint32_t gpo_container_version;
- const char *smb_server;
- const char *smb_share;
- const char *smb_path;
- uint32_t gpc_version; const char **gpo_cse_guids; int num_gpo_cse_guids; int gpo_func_version;
@@ -156,10 +157,15 @@ int ad_gpo_process_gpo_recv(struct tevent_req *req, int *num_candidate_gpos); struct tevent_req *ad_gpo_process_cse_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
char *cse_smb_uri,char *cse_unix_path);
struct sss_domain_info *domain,const char *smb_server,const char *smb_share,const char *smb_path,const char *smb_cse_suffix,const char *gpo_guid);int ad_gpo_process_cse_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
int *_sysvol_gpt_version, int *_allowed_size, char ***_allowed_sids, int *_denied_size,@@ -1298,8 +1304,6 @@ ad_gpo_cse_step(struct tevent_req *req) { struct tevent_req *subreq; struct ad_gpo_access_state *state;
char *cse_smb_uri;
char *cse_unix_path; int i = 0;
state = tevent_req_data(req, struct ad_gpo_access_state);
@@ -1319,15 +1323,19 @@ ad_gpo_cse_step(struct tevent_req *req) state->cse_gpo_index, i, cse_filtered_gpo->gpo_cse_guids[i]); }
- cse_smb_uri = talloc_asprintf(state, "%s%s",
cse_filtered_gpo->gpo_file_sys_path,GP_EXT_GUID_SECURITY_SUFFIX);
- DEBUG(SSSDBG_TRACE_FUNC, "smb_server: %s\n",
cse_filtered_gpo->smb_server);
- DEBUG(SSSDBG_TRACE_FUNC, "smb_share: %s\n",
cse_filtered_gpo->smb_share);
- DEBUG(SSSDBG_TRACE_FUNC, "smb_path: %s\n",
cse_filtered_gpo->smb_path);
- DEBUG(SSSDBG_TRACE_FUNC, "gpo_guid: %s\n",
cse_filtered_gpo->gpo_guid);
- cse_unix_path = talloc_asprintf(state, "%s%s",
cse_filtered_gpo->gpo_unix_path,GP_EXT_GUID_SECURITY_SUFFIX);- subreq = ad_gpo_process_cse_send(state, state->ev, cse_smb_uri,
cse_unix_path);
subreq = ad_gpo_process_cse_send(state,
state->ev,state->domain,cse_filtered_gpo->smb_server,cse_filtered_gpo->smb_share,cse_filtered_gpo->smb_path,GP_EXT_GUID_SECURITY_SUFFIX,cse_filtered_gpo->gpo_guid);tevent_req_set_callback(subreq, ad_gpo_cse_done, req); return EAGAIN;
@@ -1348,6 +1356,7 @@ ad_gpo_cse_done(struct tevent_req *subreq) struct tevent_req *req; struct ad_gpo_access_state *state; int ret;
- int sysvol_gpt_version; char **allowed_sids; int allowed_size; char **denied_sids;
@@ -1356,11 +1365,24 @@ ad_gpo_cse_done(struct tevent_req *subreq) req = tevent_req_callback_data(subreq, struct tevent_req); state = tevent_req_data(req, struct ad_gpo_access_state);
- ret = ad_gpo_process_cse_recv(subreq, state, &allowed_size,
- struct gp_gpo *cse_filtered_gpo =
state->cse_filtered_gpos[state->cse_gpo_index];- const char *gpo_guid = cse_filtered_gpo->gpo_guid;
- DEBUG(SSSDBG_TRACE_FUNC, "gpo_guid: %s\n", gpo_guid);
- ret = ad_gpo_process_cse_recv(subreq, state, &sysvol_gpt_version,
&allowed_size, &allowed_sids, &denied_size, &denied_sids);
talloc_zfree(subreq);
- DEBUG(SSSDBG_TRACE_FUNC, "sysvol_gpt_version: %d\n",
sysvol_gpt_version);
- ret = sysdb_gpo_store_gpo(state->domain, state->domain->name,
gpo_guid,
sysvol_gpt_version);- if (ret != EOK) { /* TBD: handle ret error */ goto done;
@@ -2274,48 +2296,46 @@ ad_gpo_populate_candidate_gpos(TALLOC_CTX *mem_ctx, }
/*
- This function converts the input_path to an smb uri and a unix_path,
which
- are used to populate the _smb_uri and _unix_path output parameters,
- respectively.
- This function parses the input_path into its components, replaces each
- forward slash ('') with a back slash ('/'), and populates the output
params.
I think the forward slash ans backslash are the other way around :-)
I'll continue with the review tomorrow, sorry, but I'm running out of time today, I just wanted to send out what I was able to review so far in case you wanted to do some fixes in tree. Feel free to send updated patches if you agree with some of the points I raised (or not if you prefer to send the update in a single large batch).
Thank you for the patch!
To be continued :-)
Hi Jakub,
I agree with all of your comments so far and have attached revised patches to this email. I split the original patch into two patches, as you suggested, and I addressed your review comments in the revised patches. Thanks for the review! Keep the comments coming! :)
Regards, Yassir.
On Tue, Jul 22, 2014 at 03:13:58AM -0400, Yassir Elley wrote:
Hi Jakub,
I agree with all of your comments so far and have attached revised patches to this email. I split the original patch into two patches, as you suggested, and I addressed your review comments in the revised patches. Thanks for the review! Keep the comments coming! :)
Regards, Yassir.
From bb7d2289f7a4f6afbd38501dd33b606cc3942ee4 Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Tue, 22 Jul 2014 03:02:30 -0400 Subject: [PATCH 1/2] AD-GPO: add sysdb_gpo support for caching gpo version
ACK. I wrote two simple unit tests which I'll send to a separate thread..
From f3f00a78102ad5e389266b28c43d0396caf57592 Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Tue, 22 Jul 2014 03:03:03 -0400 Subject: [PATCH 2/2] AD-GPO: only download policy files if gpo version changes
Under review...
On Tue, Jul 22, 2014 at 03:13:58AM -0400, Yassir Elley wrote:
See some comments about ad_gpo.c inline, I'm still reading ad_gpo_child.c
From f3f00a78102ad5e389266b28c43d0396caf57592 Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Tue, 22 Jul 2014 03:03:03 -0400 Subject: [PATCH 2/2] AD-GPO: only download policy files if gpo version changes
[...]
@@ -1298,8 +1304,6 @@ ad_gpo_cse_step(struct tevent_req *req) { struct tevent_req *subreq; struct ad_gpo_access_state *state;
char *cse_smb_uri;
char *cse_unix_path; int i = 0;
state = tevent_req_data(req, struct ad_gpo_access_state);
It's not really visible from the diff, but ad_gpo_cse_step increments the index as the first thing it does and only then assigns to cse_filtered_gpo. This reads to me as if you were skipping the first entry...shouldn't the code read the other way around, first assign the GPO and then increment the index?
@@ -1319,15 +1323,19 @@ ad_gpo_cse_step(struct tevent_req *req) state->cse_gpo_index, i, cse_filtered_gpo->gpo_cse_guids[i]); }
- cse_smb_uri = talloc_asprintf(state, "%s%s",
cse_filtered_gpo->gpo_file_sys_path,GP_EXT_GUID_SECURITY_SUFFIX);
- DEBUG(SSSDBG_TRACE_FUNC, "smb_server: %s\n", cse_filtered_gpo->smb_server);
- DEBUG(SSSDBG_TRACE_FUNC, "smb_share: %s\n", cse_filtered_gpo->smb_share);
- DEBUG(SSSDBG_TRACE_FUNC, "smb_path: %s\n", cse_filtered_gpo->smb_path);
- DEBUG(SSSDBG_TRACE_FUNC, "gpo_guid: %s\n", cse_filtered_gpo->gpo_guid);
- cse_unix_path = talloc_asprintf(state, "%s%s",
cse_filtered_gpo->gpo_unix_path,GP_EXT_GUID_SECURITY_SUFFIX);- subreq = ad_gpo_process_cse_send(state, state->ev, cse_smb_uri, cse_unix_path);
subreq = ad_gpo_process_cse_send(state,
state->ev,state->domain,cse_filtered_gpo->smb_server,cse_filtered_gpo->smb_share,cse_filtered_gpo->smb_path,GP_EXT_GUID_SECURITY_SUFFIX,cse_filtered_gpo->gpo_guid);tevent_req_set_callback(subreq, ad_gpo_cse_done, req); return EAGAIN;
@@ -1348,6 +1356,7 @@ ad_gpo_cse_done(struct tevent_req *subreq) struct tevent_req *req; struct ad_gpo_access_state *state; int ret;
- int sysvol_gpt_version; char **allowed_sids; int allowed_size; char **denied_sids;
@@ -1356,11 +1365,23 @@ ad_gpo_cse_done(struct tevent_req *subreq) req = tevent_req_callback_data(subreq, struct tevent_req); state = tevent_req_data(req, struct ad_gpo_access_state);
- ret = ad_gpo_process_cse_recv(subreq, state, &allowed_size,
- struct gp_gpo *cse_filtered_gpo =
state->cse_filtered_gpos[state->cse_gpo_index];- const char *gpo_guid = cse_filtered_gpo->gpo_guid;
- DEBUG(SSSDBG_TRACE_FUNC, "gpo_guid: %s\n", gpo_guid);
- ret = ad_gpo_process_cse_recv(subreq, state, &sysvol_gpt_version, &allowed_size, &allowed_sids, &denied_size, &denied_sids);
Here you read the return code of ad_gpo_process_cse_recv() but don't check it.
talloc_zfree(subreq);
- DEBUG(SSSDBG_TRACE_FUNC, "sysvol_gpt_version: %d\n", sysvol_gpt_version);
- ret = sysdb_gpo_store_gpo(state->domain, gpo_guid, sysvol_gpt_version);
- if (ret != EOK) { /* TBD: handle ret error */ goto done;
@@ -2274,48 +2295,46 @@ ad_gpo_populate_candidate_gpos(TALLOC_CTX *mem_ctx,
[...]
@@ -2709,32 +2734,23 @@ ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq) }
file_sys_path = talloc_strdup(gp_gpo, raw_file_sys_path);
ad_gpo_convert_to_smb_uri(state, state->server_hostname, file_sys_path,
&smb_uri, &unix_path);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;}
- ad_gpo_extract_smb_components(state, state->server_hostname,
file_sys_path, &smb_server,&smb_share, &smb_path);
There is no return code check here.
- DEBUG(SSSDBG_TRACE_FUNC, "gpo_file_sys_path: %s\n",
gp_gpo->gpo_file_sys_path);
- gp_gpo->smb_server = talloc_strdup(gp_gpo, smb_server);
- DEBUG(SSSDBG_TRACE_ALL, "smb_server: %s\n", gp_gpo->smb_server);
- gp_gpo->gpo_unix_path = talloc_asprintf(gp_gpo, "%s/Machine",
unix_path);- if (gp_gpo->gpo_unix_path == NULL) {
ret = ENOMEM;goto done;- }
- gp_gpo->smb_share = talloc_strdup(gp_gpo, smb_share);
- DEBUG(SSSDBG_TRACE_ALL, "smb_share: %s\n", gp_gpo->smb_share);
- DEBUG(SSSDBG_TRACE_FUNC, "gpo_unix_path: %s\n",
gp_gpo->gpo_unix_path);
- gp_gpo->smb_path = talloc_strdup(gp_gpo, smb_path);
- DEBUG(SSSDBG_TRACE_ALL, "smb_path: %s\n", gp_gpo->smb_path);
I wonder if you could call either talloc_steal to move the variables like smb_path, smb_share and smb_server to the gp_gpo context or assign to members of gp_gpo right away when calling ad_gpo_extract_smb_components() instead of allocating the result on state and assigning to local variables. Not a big deal though.
/* 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",&gp_gpo->gpc_version);@@ -2742,8 +2758,7 @@ ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq) goto done; }
- DEBUG(SSSDBG_TRACE_ALL, "gpo_container_version: %d\n",
gp_gpo->gpo_container_version);
DEBUG(SSSDBG_TRACE_ALL, "gpc_version: %d\n", gp_gpo->gpc_version);
/* retrieve AD_AT_MACHINE_EXT_NAMES */ ret = sysdb_attrs_get_el(results[0], AD_AT_MACHINE_EXT_NAMES, &el);
@@ -2843,20 +2858,26 @@ ad_gpo_process_gpo_recv(struct tevent_req *req, }
/* == ad_gpo_process_cse_send/recv helpers ================================= */
static errno_t create_cse_send_buffer(TALLOC_CTX *mem_ctx,
char *cse_smb_uri,char *cse_unix_path,
const char *smb_server,const char *smb_share,const char *smb_path,const char *smb_cse_suffix,int cached_gpt_version, struct io_buffer **io_buf){ struct io_buffer *buf; size_t rp;
- int cse_smb_uri_length;
- int cse_unix_path_length;
- int smb_server_length;
- int smb_share_length;
- int smb_path_length;
- int smb_cse_suffix_length;
- cse_smb_uri_length = strlen(cse_smb_uri);
- cse_unix_path_length = strlen(cse_unix_path);
smb_server_length = strlen(smb_server);
smb_share_length = strlen(smb_share);
smb_path_length = strlen(smb_path);
smb_cse_suffix_length = strlen(smb_cse_suffix);
buf = talloc(mem_ctx, struct io_buffer); if (buf == NULL) {
@@ -2864,8 +2885,9 @@ create_cse_send_buffer(TALLOC_CTX *mem_ctx, return ENOMEM; }
- buf->size = 2 * sizeof(uint32_t);
- buf->size += cse_smb_uri_length + cse_unix_path_length;
buf->size = 5 * sizeof(uint32_t);
buf->size += smb_server_length + smb_share_length + smb_path_length +
smb_cse_suffix_length;DEBUG(SSSDBG_TRACE_ALL, "buffer size: %zu\n", buf->size);
@@ -2877,13 +2899,24 @@ create_cse_send_buffer(TALLOC_CTX *mem_ctx, }
rp = 0;
- /* cse_smb_uri */
- SAFEALIGN_SET_UINT32(&buf->data[rp], cse_smb_uri_length, &rp);
- safealign_memcpy(&buf->data[rp], cse_smb_uri, cse_smb_uri_length, &rp);
- /* cached_gpt_version */
- SAFEALIGN_SET_UINT32(&buf->data[rp], cached_gpt_version, &rp);
- /* cse_unix_path */
- SAFEALIGN_SET_UINT32(&buf->data[rp], cse_unix_path_length, &rp);
- safealign_memcpy(&buf->data[rp], cse_unix_path, cse_unix_path_length, &rp);
/* smb_server */
SAFEALIGN_SET_UINT32(&buf->data[rp], smb_server_length, &rp);
safealign_memcpy(&buf->data[rp], smb_server, smb_server_length, &rp);
/* smb_share */
SAFEALIGN_SET_UINT32(&buf->data[rp], smb_share_length, &rp);
safealign_memcpy(&buf->data[rp], smb_share, smb_share_length, &rp);
/* smb_path */
SAFEALIGN_SET_UINT32(&buf->data[rp], smb_path_length, &rp);
safealign_memcpy(&buf->data[rp], smb_path, smb_path_length, &rp);
/* smb_cse_suffix */
SAFEALIGN_SET_UINT32(&buf->data[rp], smb_cse_suffix_length, &rp);
safealign_memcpy(&buf->data[rp], smb_cse_suffix, smb_cse_suffix_length, &rp);
*io_buf = buf; return EOK;
@@ -2934,7 +2967,8 @@ parse_logon_right_with_libini(TALLOC_CTX *mem_ctx,
if (ret != 0) { DEBUG(SSSDBG_CRIT_FAILURE,
"ini_get_string_config_array failed [%d][%s]\n", ret, strerror(ret));
"ini_get_string_config_array failed [%d][%s]\n", }ret, strerror(ret)); goto done;@@ -2977,12 +3011,12 @@ parse_logon_right_with_libini(TALLOC_CTX *mem_ctx,
- allowed_sids and denied_sids
*/ static errno_t -ad_gpo_parse_security_cse_buffer(TALLOC_CTX *mem_ctx,
const char *filename,char ***allowed_sids,int *allowed_size,char ***denied_sids,int *denied_size)+ad_gpo_parse_policy_file(TALLOC_CTX *mem_ctx,
const char *filename,char ***allowed_sids,int *allowed_size,char ***denied_sids,int *denied_size){ struct ini_cfgfile *file_ctx = NULL; struct ini_cfgobj *ini_config = NULL; @@ -3066,17 +3100,26 @@ ad_gpo_parse_security_cse_buffer(TALLOC_CTX *mem_ctx,
static errno_t ad_gpo_parse_gpo_child_response(TALLOC_CTX *mem_ctx,
uint8_t *buf, ssize_t size, uint32_t *_result)
uint8_t *buf,ssize_t size,uint32_t *_sysvol_gpt_version,uint32_t *_result){
int ret; size_t p = 0;
uint32_t sysvol_gpt_version; uint32_t result;
/* sysvol_gpt_version */
SAFEALIGN_COPY_UINT32_CHECK(&sysvol_gpt_version, buf + p, size, &p);
/* operation result code */ SAFEALIGN_COPY_UINT32_CHECK(&result, buf + p, size, &p);
*_sysvol_gpt_version = sysvol_gpt_version; *_result = result;
ret = EOK; return ret;
} @@ -3085,7 +3128,8 @@ ad_gpo_parse_gpo_child_response(TALLOC_CTX *mem_ctx,
struct ad_gpo_process_cse_state { struct tevent_context *ev;
- const char *cse_unix_path;
- const char *smb_path;
- const char *smb_cse_suffix; pid_t child_pid; uint8_t *buf; ssize_t len;
@@ -3141,13 +3185,19 @@ static void gpo_cse_done(struct tevent_req *subreq); struct tevent_req * ad_gpo_process_cse_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
char *cse_smb_uri,char *cse_unix_path)
struct sss_domain_info *domain,const char *smb_server,const char *smb_share,const char *smb_path,const char *smb_cse_suffix,const char *gpo_guid){ struct tevent_req *req; struct tevent_req *subreq; struct ad_gpo_process_cse_state *state;
struct ldb_result *res; struct io_buffer *buf = NULL;
int cached_gpt_version = 0; errno_t ret;
req = tevent_req_create(mem_ctx, &state, struct ad_gpo_process_cse_state);
@@ -3157,10 +3207,10 @@ ad_gpo_process_cse_send(TALLOC_CTX *mem_ctx, }
state->ev = ev;
- state->cse_unix_path = cse_unix_path; state->buf = NULL; state->len = 0;
- state->smb_path = smb_path;
- state->smb_cse_suffix = smb_cse_suffix; state->io = talloc(state, struct io); if (state->io == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n");
@@ -3172,8 +3222,33 @@ ad_gpo_process_cse_send(TALLOC_CTX *mem_ctx, state->io->read_from_child_fd = -1; talloc_set_destructor((void *) state->io, gpo_child_io_destructor);
- /* retrieve cached gpt version (or set it to -1 if unavailable) */
- DEBUG(SSSDBG_TRACE_FUNC, "retrieving GPO from cache [%s]\n", gpo_guid);
- ret = sysdb_gpo_get_gpo(state, domain, gpo_guid, &res);
- if (ret != EOK) {
switch (ret) {case ENOENT:DEBUG(SSSDBG_TRACE_FUNC, "ENOENT\n");cached_gpt_version = -1;break;default:DEBUG(SSSDBG_FATAL_FAILURE, "Could not read GPO from cache: [%s]\n",strerror(ret));goto fail;
This is more of a question, but would it make sense to read the GPOs from the server and attempt a recovery instead of failing?
The rest of the changes in this file look good to me, now on to gpo_child.c...
----- Original Message -----
On Tue, Jul 22, 2014 at 03:13:58AM -0400, Yassir Elley wrote:
See some comments about ad_gpo.c inline, I'm still reading ad_gpo_child.c
From f3f00a78102ad5e389266b28c43d0396caf57592 Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Tue, 22 Jul 2014 03:03:03 -0400 Subject: [PATCH 2/2] AD-GPO: only download policy files if gpo version changes
[...]
@@ -1298,8 +1304,6 @@ ad_gpo_cse_step(struct tevent_req *req) { struct tevent_req *subreq; struct ad_gpo_access_state *state;
char *cse_smb_uri;
char *cse_unix_path; int i = 0;
state = tevent_req_data(req, struct ad_gpo_access_state);
It's not really visible from the diff, but ad_gpo_cse_step increments the index as the first thing it does and only then assigns to cse_filtered_gpo. This reads to me as if you were skipping the first entry...shouldn't the code read the other way around, first assign the GPO and then increment the index?
I ran into problems when using an index initialized to 0. Although I don't remember what those problems were anymore, I was able to solve them by using an index initialized to -1, which is what I am using now, and which is why the current code works correctly (and doesn't skip the first entry).
@@ -1319,15 +1323,19 @@ ad_gpo_cse_step(struct tevent_req *req) state->cse_gpo_index, i, cse_filtered_gpo->gpo_cse_guids[i]); }
- cse_smb_uri = talloc_asprintf(state, "%s%s",
cse_filtered_gpo->gpo_file_sys_path,GP_EXT_GUID_SECURITY_SUFFIX);
- DEBUG(SSSDBG_TRACE_FUNC, "smb_server: %s\n",
cse_filtered_gpo->smb_server);
- DEBUG(SSSDBG_TRACE_FUNC, "smb_share: %s\n",
cse_filtered_gpo->smb_share);
- DEBUG(SSSDBG_TRACE_FUNC, "smb_path: %s\n",
cse_filtered_gpo->smb_path);
- DEBUG(SSSDBG_TRACE_FUNC, "gpo_guid: %s\n",
cse_filtered_gpo->gpo_guid);
- cse_unix_path = talloc_asprintf(state, "%s%s",
cse_filtered_gpo->gpo_unix_path,GP_EXT_GUID_SECURITY_SUFFIX);- subreq = ad_gpo_process_cse_send(state, state->ev, cse_smb_uri,
cse_unix_path);
subreq = ad_gpo_process_cse_send(state,
state->ev,state->domain,cse_filtered_gpo->smb_server,cse_filtered_gpo->smb_share,cse_filtered_gpo->smb_path,GP_EXT_GUID_SECURITY_SUFFIX,cse_filtered_gpo->gpo_guid);tevent_req_set_callback(subreq, ad_gpo_cse_done, req); return EAGAIN;
@@ -1348,6 +1356,7 @@ ad_gpo_cse_done(struct tevent_req *subreq) struct tevent_req *req; struct ad_gpo_access_state *state; int ret;
- int sysvol_gpt_version; char **allowed_sids; int allowed_size; char **denied_sids;
@@ -1356,11 +1365,23 @@ ad_gpo_cse_done(struct tevent_req *subreq) req = tevent_req_callback_data(subreq, struct tevent_req); state = tevent_req_data(req, struct ad_gpo_access_state);
- ret = ad_gpo_process_cse_recv(subreq, state, &allowed_size,
- struct gp_gpo *cse_filtered_gpo =
state->cse_filtered_gpos[state->cse_gpo_index];- const char *gpo_guid = cse_filtered_gpo->gpo_guid;
- DEBUG(SSSDBG_TRACE_FUNC, "gpo_guid: %s\n", gpo_guid);
- ret = ad_gpo_process_cse_recv(subreq, state, &sysvol_gpt_version,
&allowed_size, &allowed_sids, &denied_size, &denied_sids);
Here you read the return code of ad_gpo_process_cse_recv() but don't check it.
OK.
talloc_zfree(subreq);
- DEBUG(SSSDBG_TRACE_FUNC, "sysvol_gpt_version: %d\n",
sysvol_gpt_version);
- ret = sysdb_gpo_store_gpo(state->domain, gpo_guid,
sysvol_gpt_version);
- if (ret != EOK) { /* TBD: handle ret error */ goto done;
@@ -2274,48 +2295,46 @@ ad_gpo_populate_candidate_gpos(TALLOC_CTX *mem_ctx,
[...]
@@ -2709,32 +2734,23 @@ ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq) }
file_sys_path = talloc_strdup(gp_gpo, raw_file_sys_path);
- ad_gpo_convert_to_smb_uri(state, state->server_hostname,
file_sys_path,
&smb_uri, &unix_path);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;}
- ad_gpo_extract_smb_components(state, state->server_hostname,
file_sys_path, &smb_server,&smb_share, &smb_path);There is no return code check here.
OK.
- DEBUG(SSSDBG_TRACE_FUNC, "gpo_file_sys_path: %s\n",
gp_gpo->gpo_file_sys_path);
- gp_gpo->smb_server = talloc_strdup(gp_gpo, smb_server);
- DEBUG(SSSDBG_TRACE_ALL, "smb_server: %s\n", gp_gpo->smb_server);
- gp_gpo->gpo_unix_path = talloc_asprintf(gp_gpo, "%s/Machine",
unix_path);- if (gp_gpo->gpo_unix_path == NULL) {
ret = ENOMEM;goto done;- }
- gp_gpo->smb_share = talloc_strdup(gp_gpo, smb_share);
- DEBUG(SSSDBG_TRACE_ALL, "smb_share: %s\n", gp_gpo->smb_share);
- DEBUG(SSSDBG_TRACE_FUNC, "gpo_unix_path: %s\n",
gp_gpo->gpo_unix_path);
- gp_gpo->smb_path = talloc_strdup(gp_gpo, smb_path);
- DEBUG(SSSDBG_TRACE_ALL, "smb_path: %s\n", gp_gpo->smb_path);
I wonder if you could call either talloc_steal to move the variables like smb_path, smb_share and smb_server to the gp_gpo context or assign to members of gp_gpo right away when calling ad_gpo_extract_smb_components() instead of allocating the result on state and assigning to local variables. Not a big deal though.
If I store directly to the gp_gpo member variables, they are initially stored correctly. However, later on (in ad_gpo_cse_step), the member variables have corrupted values. Not sure why. As such, I have left this part of the code unchanged.
/* 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",&gp_gpo->gpc_version);@@ -2742,8 +2758,7 @@ ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq) goto done; }
- DEBUG(SSSDBG_TRACE_ALL, "gpo_container_version: %d\n",
gp_gpo->gpo_container_version);
DEBUG(SSSDBG_TRACE_ALL, "gpc_version: %d\n", gp_gpo->gpc_version);
/* retrieve AD_AT_MACHINE_EXT_NAMES */ ret = sysdb_attrs_get_el(results[0], AD_AT_MACHINE_EXT_NAMES, &el);
@@ -2843,20 +2858,26 @@ ad_gpo_process_gpo_recv(struct tevent_req *req, }
/* == ad_gpo_process_cse_send/recv helpers ================================= */
static errno_t create_cse_send_buffer(TALLOC_CTX *mem_ctx,
char *cse_smb_uri,char *cse_unix_path,
const char *smb_server,const char *smb_share,const char *smb_path,const char *smb_cse_suffix,int cached_gpt_version, struct io_buffer **io_buf){ struct io_buffer *buf; size_t rp;
- int cse_smb_uri_length;
- int cse_unix_path_length;
- int smb_server_length;
- int smb_share_length;
- int smb_path_length;
- int smb_cse_suffix_length;
- cse_smb_uri_length = strlen(cse_smb_uri);
- cse_unix_path_length = strlen(cse_unix_path);
smb_server_length = strlen(smb_server);
smb_share_length = strlen(smb_share);
smb_path_length = strlen(smb_path);
smb_cse_suffix_length = strlen(smb_cse_suffix);
buf = talloc(mem_ctx, struct io_buffer); if (buf == NULL) {
@@ -2864,8 +2885,9 @@ create_cse_send_buffer(TALLOC_CTX *mem_ctx, return ENOMEM; }
- buf->size = 2 * sizeof(uint32_t);
- buf->size += cse_smb_uri_length + cse_unix_path_length;
buf->size = 5 * sizeof(uint32_t);
buf->size += smb_server_length + smb_share_length + smb_path_length +
smb_cse_suffix_length;DEBUG(SSSDBG_TRACE_ALL, "buffer size: %zu\n", buf->size);
@@ -2877,13 +2899,24 @@ create_cse_send_buffer(TALLOC_CTX *mem_ctx, }
rp = 0;
- /* cse_smb_uri */
- SAFEALIGN_SET_UINT32(&buf->data[rp], cse_smb_uri_length, &rp);
- safealign_memcpy(&buf->data[rp], cse_smb_uri, cse_smb_uri_length,
&rp);
- /* cached_gpt_version */
- SAFEALIGN_SET_UINT32(&buf->data[rp], cached_gpt_version, &rp);
- /* cse_unix_path */
- SAFEALIGN_SET_UINT32(&buf->data[rp], cse_unix_path_length, &rp);
- safealign_memcpy(&buf->data[rp], cse_unix_path, cse_unix_path_length,
&rp);
- /* smb_server */
- SAFEALIGN_SET_UINT32(&buf->data[rp], smb_server_length, &rp);
- safealign_memcpy(&buf->data[rp], smb_server, smb_server_length, &rp);
- /* smb_share */
- SAFEALIGN_SET_UINT32(&buf->data[rp], smb_share_length, &rp);
- safealign_memcpy(&buf->data[rp], smb_share, smb_share_length, &rp);
- /* smb_path */
- SAFEALIGN_SET_UINT32(&buf->data[rp], smb_path_length, &rp);
- safealign_memcpy(&buf->data[rp], smb_path, smb_path_length, &rp);
- /* smb_cse_suffix */
- SAFEALIGN_SET_UINT32(&buf->data[rp], smb_cse_suffix_length, &rp);
- safealign_memcpy(&buf->data[rp], smb_cse_suffix,
smb_cse_suffix_length, &rp);
*io_buf = buf; return EOK;@@ -2934,7 +2967,8 @@ parse_logon_right_with_libini(TALLOC_CTX *mem_ctx,
if (ret != 0) { DEBUG(SSSDBG_CRIT_FAILURE,
"ini_get_string_config_array failed [%d][%s]\n", ret,strerror(ret));
"ini_get_string_config_array failed [%d][%s]\n", }ret, strerror(ret)); goto done;@@ -2977,12 +3011,12 @@ parse_logon_right_with_libini(TALLOC_CTX *mem_ctx,
- allowed_sids and denied_sids
*/ static errno_t -ad_gpo_parse_security_cse_buffer(TALLOC_CTX *mem_ctx,
const char *filename,char ***allowed_sids,int *allowed_size,char ***denied_sids,int *denied_size)+ad_gpo_parse_policy_file(TALLOC_CTX *mem_ctx,
const char *filename,char ***allowed_sids,int *allowed_size,char ***denied_sids,int *denied_size){ struct ini_cfgfile *file_ctx = NULL; struct ini_cfgobj *ini_config = NULL; @@ -3066,17 +3100,26 @@ ad_gpo_parse_security_cse_buffer(TALLOC_CTX *mem_ctx,
static errno_t ad_gpo_parse_gpo_child_response(TALLOC_CTX *mem_ctx,
uint8_t *buf, ssize_t size, uint32_t *_result)
uint8_t *buf,ssize_t size,uint32_t *_sysvol_gpt_version,uint32_t *_result){
int ret; size_t p = 0;
uint32_t sysvol_gpt_version; uint32_t result;
/* sysvol_gpt_version */
SAFEALIGN_COPY_UINT32_CHECK(&sysvol_gpt_version, buf + p, size, &p);
/* operation result code */ SAFEALIGN_COPY_UINT32_CHECK(&result, buf + p, size, &p);
*_sysvol_gpt_version = sysvol_gpt_version; *_result = result;
ret = EOK; return ret;
} @@ -3085,7 +3128,8 @@ ad_gpo_parse_gpo_child_response(TALLOC_CTX *mem_ctx,
struct ad_gpo_process_cse_state { struct tevent_context *ev;
- const char *cse_unix_path;
- const char *smb_path;
- const char *smb_cse_suffix; pid_t child_pid; uint8_t *buf; ssize_t len;
@@ -3141,13 +3185,19 @@ static void gpo_cse_done(struct tevent_req *subreq); struct tevent_req * ad_gpo_process_cse_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
char *cse_smb_uri,char *cse_unix_path)
struct sss_domain_info *domain,const char *smb_server,const char *smb_share,const char *smb_path,const char *smb_cse_suffix,const char *gpo_guid){ struct tevent_req *req; struct tevent_req *subreq; struct ad_gpo_process_cse_state *state;
struct ldb_result *res; struct io_buffer *buf = NULL;
int cached_gpt_version = 0; errno_t ret;
req = tevent_req_create(mem_ctx, &state, struct ad_gpo_process_cse_state);
@@ -3157,10 +3207,10 @@ ad_gpo_process_cse_send(TALLOC_CTX *mem_ctx, }
state->ev = ev;
- state->cse_unix_path = cse_unix_path; state->buf = NULL; state->len = 0;
- state->smb_path = smb_path;
- state->smb_cse_suffix = smb_cse_suffix; state->io = talloc(state, struct io); if (state->io == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n");
@@ -3172,8 +3222,33 @@ ad_gpo_process_cse_send(TALLOC_CTX *mem_ctx, state->io->read_from_child_fd = -1; talloc_set_destructor((void *) state->io, gpo_child_io_destructor);
- /* retrieve cached gpt version (or set it to -1 if unavailable) */
- DEBUG(SSSDBG_TRACE_FUNC, "retrieving GPO from cache [%s]\n",
gpo_guid);
- ret = sysdb_gpo_get_gpo(state, domain, gpo_guid, &res);
- if (ret != EOK) {
switch (ret) {case ENOENT:DEBUG(SSSDBG_TRACE_FUNC, "ENOENT\n");cached_gpt_version = -1;break;default:DEBUG(SSSDBG_FATAL_FAILURE, "Could not read GPO from cache:[%s]\n",
strerror(ret));goto fail;This is more of a question, but would it make sense to read the GPOs from the server and attempt a recovery instead of failing?
I think it could make sense to attempt a recovery. However, I think it would be better to fail quickly, so that the actual problem can be fixed, rather than attempting a recovery which might mask the problem. For example, if the sysdb_gpo code was very buggy and failed for some reason (every time!), but we kept reading the GPOs from the server anyway, then the user would have a false sense of security that the cache was working and that the gpo's would work fine in offline mode (which they wouldn't).
The rest of the changes in this file look good to me, now on to gpo_child.c... _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
I'll send a revised patch later.
Thanks, Yassir.
On (23/07/14 03:03), Yassir Elley wrote:
----- Original Message -----
On Tue, Jul 22, 2014 at 03:13:58AM -0400, Yassir Elley wrote:
See some comments about ad_gpo.c inline, I'm still reading ad_gpo_child.c
From f3f00a78102ad5e389266b28c43d0396caf57592 Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Tue, 22 Jul 2014 03:03:03 -0400 Subject: [PATCH 2/2] AD-GPO: only download policy files if gpo version changes
[...]
@@ -1298,8 +1304,6 @@ ad_gpo_cse_step(struct tevent_req *req) { struct tevent_req *subreq; struct ad_gpo_access_state *state;
char *cse_smb_uri;
char *cse_unix_path; int i = 0;
state = tevent_req_data(req, struct ad_gpo_access_state);
It's not really visible from the diff, but ad_gpo_cse_step increments the index as the first thing it does and only then assigns to cse_filtered_gpo. This reads to me as if you were skipping the first entry...shouldn't the code read the other way around, first assign the GPO and then increment the index?
I ran into problems when using an index initialized to 0. Although I don't remember what those problems were anymore, I was able to solve them by using an index initialized to -1, which is what I am using now, and which is why the current code works correctly (and doesn't skip the first entry).
@@ -1319,15 +1323,19 @@ ad_gpo_cse_step(struct tevent_req *req) state->cse_gpo_index, i, cse_filtered_gpo->gpo_cse_guids[i]); }
- cse_smb_uri = talloc_asprintf(state, "%s%s",
cse_filtered_gpo->gpo_file_sys_path,GP_EXT_GUID_SECURITY_SUFFIX);
- DEBUG(SSSDBG_TRACE_FUNC, "smb_server: %s\n",
cse_filtered_gpo->smb_server);
- DEBUG(SSSDBG_TRACE_FUNC, "smb_share: %s\n",
cse_filtered_gpo->smb_share);
- DEBUG(SSSDBG_TRACE_FUNC, "smb_path: %s\n",
cse_filtered_gpo->smb_path);
- DEBUG(SSSDBG_TRACE_FUNC, "gpo_guid: %s\n",
cse_filtered_gpo->gpo_guid);
- cse_unix_path = talloc_asprintf(state, "%s%s",
cse_filtered_gpo->gpo_unix_path,GP_EXT_GUID_SECURITY_SUFFIX);- subreq = ad_gpo_process_cse_send(state, state->ev, cse_smb_uri,
cse_unix_path);
subreq = ad_gpo_process_cse_send(state,
state->ev,state->domain,cse_filtered_gpo->smb_server,cse_filtered_gpo->smb_share,cse_filtered_gpo->smb_path,GP_EXT_GUID_SECURITY_SUFFIX,cse_filtered_gpo->gpo_guid);tevent_req_set_callback(subreq, ad_gpo_cse_done, req); return EAGAIN;
@@ -1348,6 +1356,7 @@ ad_gpo_cse_done(struct tevent_req *subreq) struct tevent_req *req; struct ad_gpo_access_state *state; int ret;
- int sysvol_gpt_version; char **allowed_sids; int allowed_size; char **denied_sids;
@@ -1356,11 +1365,23 @@ ad_gpo_cse_done(struct tevent_req *subreq) req = tevent_req_callback_data(subreq, struct tevent_req); state = tevent_req_data(req, struct ad_gpo_access_state);
- ret = ad_gpo_process_cse_recv(subreq, state, &allowed_size,
- struct gp_gpo *cse_filtered_gpo =
state->cse_filtered_gpos[state->cse_gpo_index];- const char *gpo_guid = cse_filtered_gpo->gpo_guid;
- DEBUG(SSSDBG_TRACE_FUNC, "gpo_guid: %s\n", gpo_guid);
- ret = ad_gpo_process_cse_recv(subreq, state, &sysvol_gpt_version,
&allowed_size, &allowed_sids, &denied_size, &denied_sids);
Here you read the return code of ad_gpo_process_cse_recv() but don't check it.
OK.
talloc_zfree(subreq);
- DEBUG(SSSDBG_TRACE_FUNC, "sysvol_gpt_version: %d\n",
sysvol_gpt_version);
- ret = sysdb_gpo_store_gpo(state->domain, gpo_guid,
sysvol_gpt_version);
- if (ret != EOK) { /* TBD: handle ret error */ goto done;
@@ -2274,48 +2295,46 @@ ad_gpo_populate_candidate_gpos(TALLOC_CTX *mem_ctx,
[...]
@@ -2709,32 +2734,23 @@ ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq) }
file_sys_path = talloc_strdup(gp_gpo, raw_file_sys_path);
- ad_gpo_convert_to_smb_uri(state, state->server_hostname,
file_sys_path,
&smb_uri, &unix_path);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;}
- ad_gpo_extract_smb_components(state, state->server_hostname,
file_sys_path, &smb_server,&smb_share, &smb_path);There is no return code check here.
OK.
- DEBUG(SSSDBG_TRACE_FUNC, "gpo_file_sys_path: %s\n",
gp_gpo->gpo_file_sys_path);
- gp_gpo->smb_server = talloc_strdup(gp_gpo, smb_server);
- DEBUG(SSSDBG_TRACE_ALL, "smb_server: %s\n", gp_gpo->smb_server);
- gp_gpo->gpo_unix_path = talloc_asprintf(gp_gpo, "%s/Machine",
unix_path);- if (gp_gpo->gpo_unix_path == NULL) {
ret = ENOMEM;goto done;- }
- gp_gpo->smb_share = talloc_strdup(gp_gpo, smb_share);
- DEBUG(SSSDBG_TRACE_ALL, "smb_share: %s\n", gp_gpo->smb_share);
- DEBUG(SSSDBG_TRACE_FUNC, "gpo_unix_path: %s\n",
gp_gpo->gpo_unix_path);
- gp_gpo->smb_path = talloc_strdup(gp_gpo, smb_path);
- DEBUG(SSSDBG_TRACE_ALL, "smb_path: %s\n", gp_gpo->smb_path);
I wonder if you could call either talloc_steal to move the variables like smb_path, smb_share and smb_server to the gp_gpo context or assign to members of gp_gpo right away when calling ad_gpo_extract_smb_components() instead of allocating the result on state and assigning to local variables. Not a big deal though.
If I store directly to the gp_gpo member variables, they are initially stored correctly. However, later on (in ad_gpo_cse_step), the member variables have corrupted values. Not sure why. As such, I have left this part of the code unchanged.
It can be use after free problem or something smilar. It should be visible in valgrind log. It might help you.
You can add next line to the domain section command = valgrind -v --leak-check=full --show-reachable=yes --log-file=/var/log/sssd/valgrind_default.log /usr/libexec/sssd/sssd_be --domain default --debug-to-files
You would need to change arguments after sssd_be. pgrep can help you.
sh-4.2# pgrep -af sssd_be 981 /usr/libexec/sssd/sssd_be --domain default --debug-to-files
LS
----- Original Message -----
On (23/07/14 03:03), Yassir Elley wrote:
----- Original Message -----
On Tue, Jul 22, 2014 at 03:13:58AM -0400, Yassir Elley wrote:
See some comments about ad_gpo.c inline, I'm still reading ad_gpo_child.c
From f3f00a78102ad5e389266b28c43d0396caf57592 Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Tue, 22 Jul 2014 03:03:03 -0400 Subject: [PATCH 2/2] AD-GPO: only download policy files if gpo version changes
[...]
@@ -1298,8 +1304,6 @@ ad_gpo_cse_step(struct tevent_req *req) { struct tevent_req *subreq; struct ad_gpo_access_state *state;
char *cse_smb_uri;
char *cse_unix_path; int i = 0;
state = tevent_req_data(req, struct ad_gpo_access_state);
It's not really visible from the diff, but ad_gpo_cse_step increments the index as the first thing it does and only then assigns to cse_filtered_gpo. This reads to me as if you were skipping the first entry...shouldn't the code read the other way around, first assign the GPO and then increment the index?
I ran into problems when using an index initialized to 0. Although I don't remember what those problems were anymore, I was able to solve them by using an index initialized to -1, which is what I am using now, and which is why the current code works correctly (and doesn't skip the first entry).
@@ -1319,15 +1323,19 @@ ad_gpo_cse_step(struct tevent_req *req) state->cse_gpo_index, i, cse_filtered_gpo->gpo_cse_guids[i]); }
- cse_smb_uri = talloc_asprintf(state, "%s%s",
cse_filtered_gpo->gpo_file_sys_path,GP_EXT_GUID_SECURITY_SUFFIX);
- DEBUG(SSSDBG_TRACE_FUNC, "smb_server: %s\n",
cse_filtered_gpo->smb_server);
- DEBUG(SSSDBG_TRACE_FUNC, "smb_share: %s\n",
cse_filtered_gpo->smb_share);
- DEBUG(SSSDBG_TRACE_FUNC, "smb_path: %s\n",
cse_filtered_gpo->smb_path);
- DEBUG(SSSDBG_TRACE_FUNC, "gpo_guid: %s\n",
cse_filtered_gpo->gpo_guid);
- cse_unix_path = talloc_asprintf(state, "%s%s",
cse_filtered_gpo->gpo_unix_path,GP_EXT_GUID_SECURITY_SUFFIX);- subreq = ad_gpo_process_cse_send(state, state->ev, cse_smb_uri,
cse_unix_path);
subreq = ad_gpo_process_cse_send(state,
state->ev,state->domain,cse_filtered_gpo->smb_server,cse_filtered_gpo->smb_share,cse_filtered_gpo->smb_path,GP_EXT_GUID_SECURITY_SUFFIX,cse_filtered_gpo->gpo_guid);tevent_req_set_callback(subreq, ad_gpo_cse_done, req); return EAGAIN;
@@ -1348,6 +1356,7 @@ ad_gpo_cse_done(struct tevent_req *subreq) struct tevent_req *req; struct ad_gpo_access_state *state; int ret;
- int sysvol_gpt_version; char **allowed_sids; int allowed_size; char **denied_sids;
@@ -1356,11 +1365,23 @@ ad_gpo_cse_done(struct tevent_req *subreq) req = tevent_req_callback_data(subreq, struct tevent_req); state = tevent_req_data(req, struct ad_gpo_access_state);
- ret = ad_gpo_process_cse_recv(subreq, state, &allowed_size,
- struct gp_gpo *cse_filtered_gpo =
state->cse_filtered_gpos[state->cse_gpo_index];- const char *gpo_guid = cse_filtered_gpo->gpo_guid;
- DEBUG(SSSDBG_TRACE_FUNC, "gpo_guid: %s\n", gpo_guid);
- ret = ad_gpo_process_cse_recv(subreq, state, &sysvol_gpt_version,
&allowed_size, &allowed_sids, &denied_size, &denied_sids);
Here you read the return code of ad_gpo_process_cse_recv() but don't check it.
OK.
talloc_zfree(subreq);
- DEBUG(SSSDBG_TRACE_FUNC, "sysvol_gpt_version: %d\n",
sysvol_gpt_version);
- ret = sysdb_gpo_store_gpo(state->domain, gpo_guid,
sysvol_gpt_version);
- if (ret != EOK) { /* TBD: handle ret error */ goto done;
@@ -2274,48 +2295,46 @@ ad_gpo_populate_candidate_gpos(TALLOC_CTX *mem_ctx,
[...]
@@ -2709,32 +2734,23 @@ ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq) }
file_sys_path = talloc_strdup(gp_gpo, raw_file_sys_path);
- ad_gpo_convert_to_smb_uri(state, state->server_hostname,
file_sys_path,
&smb_uri, &unix_path);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;}
- ad_gpo_extract_smb_components(state, state->server_hostname,
file_sys_path, &smb_server,&smb_share, &smb_path);There is no return code check here.
OK.
- DEBUG(SSSDBG_TRACE_FUNC, "gpo_file_sys_path: %s\n",
gp_gpo->gpo_file_sys_path);
- gp_gpo->smb_server = talloc_strdup(gp_gpo, smb_server);
- DEBUG(SSSDBG_TRACE_ALL, "smb_server: %s\n", gp_gpo->smb_server);
- gp_gpo->gpo_unix_path = talloc_asprintf(gp_gpo, "%s/Machine",
unix_path);- if (gp_gpo->gpo_unix_path == NULL) {
ret = ENOMEM;goto done;- }
- gp_gpo->smb_share = talloc_strdup(gp_gpo, smb_share);
- DEBUG(SSSDBG_TRACE_ALL, "smb_share: %s\n", gp_gpo->smb_share);
- DEBUG(SSSDBG_TRACE_FUNC, "gpo_unix_path: %s\n",
gp_gpo->gpo_unix_path);
- gp_gpo->smb_path = talloc_strdup(gp_gpo, smb_path);
- DEBUG(SSSDBG_TRACE_ALL, "smb_path: %s\n", gp_gpo->smb_path);
I wonder if you could call either talloc_steal to move the variables like smb_path, smb_share and smb_server to the gp_gpo context or assign to members of gp_gpo right away when calling ad_gpo_extract_smb_components() instead of allocating the result on state and assigning to local variables. Not a big deal though.
If I store directly to the gp_gpo member variables, they are initially stored correctly. However, later on (in ad_gpo_cse_step), the member variables have corrupted values. Not sure why. As such, I have left this part of the code unchanged.
It can be use after free problem or something smilar. It should be visible in valgrind log. It might help you.
You can add next line to the domain section command = valgrind -v --leak-check=full --show-reachable=yes --log-file=/var/log/sssd/valgrind_default.log /usr/libexec/sssd/sssd_be --domain default --debug-to-files
You would need to change arguments after sssd_be. pgrep can help you.
sh-4.2# pgrep -af sssd_be 981 /usr/libexec/sssd/sssd_be --domain default --debug-to-files
LS
Thanks. Very useful.
Yassir.
On Wed, Jul 23, 2014 at 03:03:34AM -0400, Yassir Elley wrote:
----- Original Message -----
On Tue, Jul 22, 2014 at 03:13:58AM -0400, Yassir Elley wrote:
See some comments about ad_gpo.c inline, I'm still reading ad_gpo_child.c
From f3f00a78102ad5e389266b28c43d0396caf57592 Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Tue, 22 Jul 2014 03:03:03 -0400 Subject: [PATCH 2/2] AD-GPO: only download policy files if gpo version changes
[...]
@@ -1298,8 +1304,6 @@ ad_gpo_cse_step(struct tevent_req *req) { struct tevent_req *subreq; struct ad_gpo_access_state *state;
char *cse_smb_uri;
char *cse_unix_path; int i = 0;
state = tevent_req_data(req, struct ad_gpo_access_state);
It's not really visible from the diff, but ad_gpo_cse_step increments the index as the first thing it does and only then assigns to cse_filtered_gpo. This reads to me as if you were skipping the first entry...shouldn't the code read the other way around, first assign the GPO and then increment the index?
I ran into problems when using an index initialized to 0. Although I don't remember what those problems were anymore, I was able to solve them by using an index initialized to -1, which is what I am using now, and which is why the current code works correctly (and doesn't skip the first entry).
OK, thank you, I admit I haven't run any tests yet. Can you add a comment to that part of code, then? (You don't have to re-send the patches now)
I wonder if you could call either talloc_steal to move the variables like smb_path, smb_share and smb_server to the gp_gpo context or assign to members of gp_gpo right away when calling ad_gpo_extract_smb_components() instead of allocating the result on state and assigning to local variables. Not a big deal though.
If I store directly to the gp_gpo member variables, they are initially stored correctly. However, later on (in ad_gpo_cse_step), the member variables have corrupted values. Not sure why. As such, I have left this part of the code unchanged.
I'll try to play with this area of code during my testing. But the strdup shouldn't block the inclusion of the patches to master.
[...]
- ret = sysdb_gpo_get_gpo(state, domain, gpo_guid, &res);
- if (ret != EOK) {
switch (ret) {case ENOENT:DEBUG(SSSDBG_TRACE_FUNC, "ENOENT\n");cached_gpt_version = -1;break;default:DEBUG(SSSDBG_FATAL_FAILURE, "Could not read GPO from cache:[%s]\n",
strerror(ret));goto fail;This is more of a question, but would it make sense to read the GPOs from the server and attempt a recovery instead of failing?
I think it could make sense to attempt a recovery. However, I think it would be better to fail quickly, so that the actual problem can be fixed, rather than attempting a recovery which might mask the problem. For example, if the sysdb_gpo code was very buggy and failed for some reason (every time!), but we kept reading the GPOs from the server anyway, then the user would have a false sense of security that the cache was working and that the gpo's would work fine in offline mode (which they wouldn't).
OK, then the FATAL_FAILURE makes sense.
----- Original Message -----
On Wed, Jul 23, 2014 at 03:03:34AM -0400, Yassir Elley wrote:
----- Original Message -----
On Tue, Jul 22, 2014 at 03:13:58AM -0400, Yassir Elley wrote:
See some comments about ad_gpo.c inline, I'm still reading ad_gpo_child.c
From f3f00a78102ad5e389266b28c43d0396caf57592 Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Tue, 22 Jul 2014 03:03:03 -0400 Subject: [PATCH 2/2] AD-GPO: only download policy files if gpo version changes
[...]
@@ -1298,8 +1304,6 @@ ad_gpo_cse_step(struct tevent_req *req) { struct tevent_req *subreq; struct ad_gpo_access_state *state;
char *cse_smb_uri;
char *cse_unix_path; int i = 0;
state = tevent_req_data(req, struct ad_gpo_access_state);
It's not really visible from the diff, but ad_gpo_cse_step increments the index as the first thing it does and only then assigns to cse_filtered_gpo. This reads to me as if you were skipping the first entry...shouldn't the code read the other way around, first assign the GPO and then increment the index?
I ran into problems when using an index initialized to 0. Although I don't remember what those problems were anymore, I was able to solve them by using an index initialized to -1, which is what I am using now, and which is why the current code works correctly (and doesn't skip the first entry).
OK, thank you, I admit I haven't run any tests yet. Can you add a comment to that part of code, then? (You don't have to re-send the patches now)
Actually, I agree with you that the code is currently confusing, so I have changed it to use a cse_gpo_index initialized to 0. I have also made similar changes to som_index and gpo_index.
I wonder if you could call either talloc_steal to move the variables like smb_path, smb_share and smb_server to the gp_gpo context or assign to members of gp_gpo right away when calling ad_gpo_extract_smb_components() instead of allocating the result on state and assigning to local variables. Not a big deal though.
If I store directly to the gp_gpo member variables, they are initially stored correctly. However, later on (in ad_gpo_cse_step), the member variables have corrupted values. Not sure why. As such, I have left this part of the code unchanged.
I'll try to play with this area of code during my testing. But the strdup shouldn't block the inclusion of the patches to master.
Actually, I agree with you here as well, so I have changed the code to avoid the double-allocation (of allocating first to state and then strdup'ing to gp_gpo).
In a previous email, I mentioned that I tried to directly assign the output values to member variables of gp->gpo, and that this was leading to corruption. Thanks to Lukas's suggestion, I ran valgrind, which showed a "use-after-free" condition leading to corruption, which made my mistake evident, which was that I was still using state (rather than gp_gpo) as the TALLOC_CTX, thereby disrupting the logical memory hierarchy. In other words, I was doing:
ret = ad_gpo_extract_smb_components(state, state->server_hostname, file_sys_path, &gp_gpo->smb_server, &gp_gpo->smb_share, &gp_gpo->smb_path);
instead of the correct solution, which I am now doing, which is:
ret = ad_gpo_extract_smb_components(gp_gpo, state->server_hostname, file_sys_path, &gp_gpo->smb_server, &gp_gpo->smb_share, &gp_gpo->smb_path);
I have attached a revised patch that addresses these two issues. I have also attached the previous (unchanged) sysdb_gpo API patch for your convenience.
Thanks, Yassir.
On Wed, Jul 23, 2014 at 08:02:55PM -0400, Yassir Elley wrote:
----- Original Message -----
On Wed, Jul 23, 2014 at 03:03:34AM -0400, Yassir Elley wrote:
----- Original Message -----
On Tue, Jul 22, 2014 at 03:13:58AM -0400, Yassir Elley wrote:
See some comments about ad_gpo.c inline, I'm still reading ad_gpo_child.c
From f3f00a78102ad5e389266b28c43d0396caf57592 Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Tue, 22 Jul 2014 03:03:03 -0400 Subject: [PATCH 2/2] AD-GPO: only download policy files if gpo version changes
[...]
@@ -1298,8 +1304,6 @@ ad_gpo_cse_step(struct tevent_req *req) { struct tevent_req *subreq; struct ad_gpo_access_state *state;
char *cse_smb_uri;
char *cse_unix_path; int i = 0;
state = tevent_req_data(req, struct ad_gpo_access_state);
It's not really visible from the diff, but ad_gpo_cse_step increments the index as the first thing it does and only then assigns to cse_filtered_gpo. This reads to me as if you were skipping the first entry...shouldn't the code read the other way around, first assign the GPO and then increment the index?
I ran into problems when using an index initialized to 0. Although I don't remember what those problems were anymore, I was able to solve them by using an index initialized to -1, which is what I am using now, and which is why the current code works correctly (and doesn't skip the first entry).
OK, thank you, I admit I haven't run any tests yet. Can you add a comment to that part of code, then? (You don't have to re-send the patches now)
Actually, I agree with you that the code is currently confusing, so I have changed it to use a cse_gpo_index initialized to 0. I have also made similar changes to som_index and gpo_index.
I wonder if you could call either talloc_steal to move the variables like smb_path, smb_share and smb_server to the gp_gpo context or assign to members of gp_gpo right away when calling ad_gpo_extract_smb_components() instead of allocating the result on state and assigning to local variables. Not a big deal though.
If I store directly to the gp_gpo member variables, they are initially stored correctly. However, later on (in ad_gpo_cse_step), the member variables have corrupted values. Not sure why. As such, I have left this part of the code unchanged.
I'll try to play with this area of code during my testing. But the strdup shouldn't block the inclusion of the patches to master.
Actually, I agree with you here as well, so I have changed the code to avoid the double-allocation (of allocating first to state and then strdup'ing to gp_gpo).
In a previous email, I mentioned that I tried to directly assign the output values to member variables of gp->gpo, and that this was leading to corruption. Thanks to Lukas's suggestion, I ran valgrind, which showed a "use-after-free" condition leading to corruption, which made my mistake evident, which was that I was still using state (rather than gp_gpo) as the TALLOC_CTX, thereby disrupting the logical memory hierarchy. In other words, I was doing:
ret = ad_gpo_extract_smb_components(state, state->server_hostname, file_sys_path, &gp_gpo->smb_server, &gp_gpo->smb_share, &gp_gpo->smb_path);instead of the correct solution, which I am now doing, which is:
ret = ad_gpo_extract_smb_components(gp_gpo, state->server_hostname, file_sys_path, &gp_gpo->smb_server, &gp_gpo->smb_share, &gp_gpo->smb_path);I have attached a revised patch that addresses these two issues. I have also attached the previous (unchanged) sysdb_gpo API patch for your convenience.
Thanks, Yassir.
Hi,
after your helpful instructions I was able to set up the GPOs but I'm seeing an odd behaviour and I'm not sure which way to fix it..
So the thing I'm seeing is that the file we fetch from AD contains the user or group names already, but SSSD seems to be looking for SIDs in ad_gpo_access_check().
This is the contents of the file as downloaded on my machine. Note the garbage (?) at the beginning of the file:
# cat /var/lib/sss/gpo_cache/win.example.com/Policies/{94235CE6-B2F2-43C4-8C9F-CB24C0B61E0B}/Machine/Microsoft/Windows\ NT/SecEdit/GptTmpl.inf ��[Unicode] Unicode=yes [Version] signature="$CHICAGO$" Revision=1 [Privilege Rights] SeInteractiveLogonRight = tuser,tgroup1,*S-1-5-32-544,administrator
I'm using Windows server 2012 if that matters.
When I navigate to the SYSVOL folder on the server, I see the same contents, including user names (except for the extra bytes at the start).
So I'm pretty sure I must be missing the part that converts the names to SIDs or vice-versa..
On Mon, Jul 28, 2014 at 08:48:37PM +0200, Jakub Hrozek wrote:
This is the contents of the file as downloaded on my machine. Note the garbage (?) at the beginning of the file:
Replying to myself after I looked at the contents more closely -- the file just seems to begin with 0xFFFE which seems to indicate UTF-16, little endian. Sorry for the noise, I should have done my research sooner. But the question about SID <-> name resolution still stands.
----- Original Message -----
On Mon, Jul 28, 2014 at 08:48:37PM +0200, Jakub Hrozek wrote:
This is the contents of the file as downloaded on my machine. Note the garbage (?) at the beginning of the file:
Replying to myself after I looked at the contents more closely -- the file just seems to begin with 0xFFFE which seems to indicate UTF-16, little endian. Sorry for the noise, I should have done my research sooner. But the question about SID <-> name resolution still stands. _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
Hi Jakub,
Hmmm. I have not seen this before. The SYSVOL files I have generated have always exclusively contained user/group SIDs (not user/group names). However, you may have uncovered a bug. Please tell me how you generated the GptTmpl.inf file in the SYSVOL share. Did you use the Group Policy Object Editor?? Did you edit it manually?
Thanks, Yassir.
On Mon, Jul 28, 2014 at 10:19:38PM -0400, Yassir Elley wrote:
----- Original Message -----
On Mon, Jul 28, 2014 at 08:48:37PM +0200, Jakub Hrozek wrote:
This is the contents of the file as downloaded on my machine. Note the garbage (?) at the beginning of the file:
Replying to myself after I looked at the contents more closely -- the file just seems to begin with 0xFFFE which seems to indicate UTF-16, little endian. Sorry for the noise, I should have done my research sooner. But the question about SID <-> name resolution still stands. _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
Hi Jakub,
Hmmm. I have not seen this before. The SYSVOL files I have generated have always exclusively contained user/group SIDs (not user/group names). However, you may have uncovered a bug. Please tell me how you generated the GptTmpl.inf file in the SYSVOL share. Did you use the Group Policy Object Editor?? Did you edit it manually?
I never edited the file manually, I only used the Group Policy Management editor.
btw I don't want to stall these patches any longer, I will test the patches again using SIDs only in the GPO file and if they work, I will ack them and we can continue pursuing this issue separately.
On Tue, Jul 29, 2014 at 10:01:03AM +0200, Jakub Hrozek wrote:
On Mon, Jul 28, 2014 at 10:19:38PM -0400, Yassir Elley wrote:
----- Original Message -----
On Mon, Jul 28, 2014 at 08:48:37PM +0200, Jakub Hrozek wrote:
This is the contents of the file as downloaded on my machine. Note the garbage (?) at the beginning of the file:
Replying to myself after I looked at the contents more closely -- the file just seems to begin with 0xFFFE which seems to indicate UTF-16, little endian. Sorry for the noise, I should have done my research sooner. But the question about SID <-> name resolution still stands. _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
Hi Jakub,
Hmmm. I have not seen this before. The SYSVOL files I have generated have always exclusively contained user/group SIDs (not user/group names). However, you may have uncovered a bug. Please tell me how you generated the GptTmpl.inf file in the SYSVOL share. Did you use the Group Policy Object Editor?? Did you edit it manually?
I never edited the file manually, I only used the Group Policy Management editor.
btw I don't want to stall these patches any longer, I will test the patches again using SIDs only in the GPO file and if they work, I will ack them and we can continue pursuing this issue separately.
In case the above wasn't clear -- I did verify the file is not refreshed if the version stays the same and is correctly refreshed after modifying the GPO policy on the AD side.
So it's an ACK from me for these patches, let's investigate the SID issue separately.
----- Original Message -----
On Tue, Jul 29, 2014 at 10:01:03AM +0200, Jakub Hrozek wrote:
On Mon, Jul 28, 2014 at 10:19:38PM -0400, Yassir Elley wrote:
----- Original Message -----
On Mon, Jul 28, 2014 at 08:48:37PM +0200, Jakub Hrozek wrote:
This is the contents of the file as downloaded on my machine. Note the garbage (?) at the beginning of the file:
Replying to myself after I looked at the contents more closely -- the file just seems to begin with 0xFFFE which seems to indicate UTF-16, little endian. Sorry for the noise, I should have done my research sooner. But the question about SID <-> name resolution still stands. _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
Hi Jakub,
Hmmm. I have not seen this before. The SYSVOL files I have generated have always exclusively contained user/group SIDs (not user/group names). However, you may have uncovered a bug. Please tell me how you generated the GptTmpl.inf file in the SYSVOL share. Did you use the Group Policy Object Editor?? Did you edit it manually?
I never edited the file manually, I only used the Group Policy Management editor.
btw I don't want to stall these patches any longer, I will test the patches again using SIDs only in the GPO file and if they work, I will ack them and we can continue pursuing this issue separately.
In case the above wasn't clear -- I did verify the file is not refreshed if the version stays the same and is correctly refreshed after modifying the GPO policy on the AD side.
So it's an ACK from me for these patches, let's investigate the SID issue separately.
Agreed. Are you waiting for other ACKs before pushing this patch to master?
Regards, Yassir.
PS: I think a previous patch that was pushed to master ("AD: Increment som_index when advancing to the next GPO") actually relied on som_index changes in *this* patch. So it would be good if we could push this patch soon; otherwise, we should revert the previous patch.
On Tue, Jul 29, 2014 at 03:28:37PM -0400, Yassir Elley wrote:
----- Original Message -----
On Tue, Jul 29, 2014 at 10:01:03AM +0200, Jakub Hrozek wrote:
On Mon, Jul 28, 2014 at 10:19:38PM -0400, Yassir Elley wrote:
----- Original Message -----
On Mon, Jul 28, 2014 at 08:48:37PM +0200, Jakub Hrozek wrote:
This is the contents of the file as downloaded on my machine. Note the garbage (?) at the beginning of the file:
Replying to myself after I looked at the contents more closely -- the file just seems to begin with 0xFFFE which seems to indicate UTF-16, little endian. Sorry for the noise, I should have done my research sooner. But the question about SID <-> name resolution still stands. _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
Hi Jakub,
Hmmm. I have not seen this before. The SYSVOL files I have generated have always exclusively contained user/group SIDs (not user/group names). However, you may have uncovered a bug. Please tell me how you generated the GptTmpl.inf file in the SYSVOL share. Did you use the Group Policy Object Editor?? Did you edit it manually?
I never edited the file manually, I only used the Group Policy Management editor.
btw I don't want to stall these patches any longer, I will test the patches again using SIDs only in the GPO file and if they work, I will ack them and we can continue pursuing this issue separately.
In case the above wasn't clear -- I did verify the file is not refreshed if the version stays the same and is correctly refreshed after modifying the GPO policy on the AD side.
So it's an ACK from me for these patches, let's investigate the SID issue separately.
Agreed. Are you waiting for other ACKs before pushing this patch to master?
No, I wanted to find some time to re-test again with a fresh AD install, but didn't. Anyway, pushed to master now: b47449d8596d9573ff13e74b6984d080b414715a 64074e584a56611d7563667e0fcdadd215b0c922
Regards, Yassir.
PS: I think a previous patch that was pushed to master ("AD: Increment som_index when advancing to the next GPO") actually relied on som_index changes in *this* patch. So it would be good if we could push this patch soon; otherwise, we should revert the previous patch.
I thought so, too, but the patch applied OK even w/o these two on master..so I think we're fine. I also checked that the index is still incremented correctly after pushing all to master before all calls to ad_gpo_get_som_attrs_step()
----- Original Message -----
On Tue, Jul 29, 2014 at 03:28:37PM -0400, Yassir Elley wrote:
----- Original Message -----
On Tue, Jul 29, 2014 at 10:01:03AM +0200, Jakub Hrozek wrote:
On Mon, Jul 28, 2014 at 10:19:38PM -0400, Yassir Elley wrote:
----- Original Message -----
On Mon, Jul 28, 2014 at 08:48:37PM +0200, Jakub Hrozek wrote: > This is the contents of the file as downloaded on my machine. > Note > the > garbage (?) at the beginning of the file:
Replying to myself after I looked at the contents more closely -- the file just seems to begin with 0xFFFE which seems to indicate UTF-16, little endian. Sorry for the noise, I should have done my research sooner. But the question about SID <-> name resolution still stands. _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
Hi Jakub,
Hmmm. I have not seen this before. The SYSVOL files I have generated have always exclusively contained user/group SIDs (not user/group names). However, you may have uncovered a bug. Please tell me how you generated the GptTmpl.inf file in the SYSVOL share. Did you use the Group Policy Object Editor?? Did you edit it manually?
I never edited the file manually, I only used the Group Policy Management editor.
btw I don't want to stall these patches any longer, I will test the patches again using SIDs only in the GPO file and if they work, I will ack them and we can continue pursuing this issue separately.
In case the above wasn't clear -- I did verify the file is not refreshed if the version stays the same and is correctly refreshed after modifying the GPO policy on the AD side.
So it's an ACK from me for these patches, let's investigate the SID issue separately.
Agreed. Are you waiting for other ACKs before pushing this patch to master?
No, I wanted to find some time to re-test again with a fresh AD install, but didn't. Anyway, pushed to master now: b47449d8596d9573ff13e74b6984d080b414715a 64074e584a56611d7563667e0fcdadd215b0c922
Regards, Yassir.
PS: I think a previous patch that was pushed to master ("AD: Increment som_index when advancing to the next GPO") actually relied on som_index changes in *this* patch. So it would be good if we could push this patch soon; otherwise, we should revert the previous patch.
I thought so, too, but the patch applied OK even w/o these two on master..so I think we're fine. I also checked that the index is still incremented correctly after pushing all to master before all calls to ad_gpo_get_som_attrs_step()
There was a run-time dependency that would yield incorrect behavior in certain error cases, but no compile-time/patch-time dependency :)
Thanks for the review!
Regards, Yassir.
----- Original Message -----
On Wed, Jul 23, 2014 at 08:02:55PM -0400, Yassir Elley wrote:
----- Original Message -----
On Wed, Jul 23, 2014 at 03:03:34AM -0400, Yassir Elley wrote:
----- Original Message -----
On Tue, Jul 22, 2014 at 03:13:58AM -0400, Yassir Elley wrote:
See some comments about ad_gpo.c inline, I'm still reading ad_gpo_child.c
From f3f00a78102ad5e389266b28c43d0396caf57592 Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Tue, 22 Jul 2014 03:03:03 -0400 Subject: [PATCH 2/2] AD-GPO: only download policy files if gpo version changes
[...]
@@ -1298,8 +1304,6 @@ ad_gpo_cse_step(struct tevent_req *req) { struct tevent_req *subreq; struct ad_gpo_access_state *state;
char *cse_smb_uri;
char *cse_unix_path; int i = 0;
state = tevent_req_data(req, struct ad_gpo_access_state);
It's not really visible from the diff, but ad_gpo_cse_step increments the index as the first thing it does and only then assigns to cse_filtered_gpo. This reads to me as if you were skipping the first entry...shouldn't the code read the other way around, first assign the GPO and then increment the index?
I ran into problems when using an index initialized to 0. Although I don't remember what those problems were anymore, I was able to solve them by using an index initialized to -1, which is what I am using now, and which is why the current code works correctly (and doesn't skip the first entry).
OK, thank you, I admit I haven't run any tests yet. Can you add a comment to that part of code, then? (You don't have to re-send the patches now)
Actually, I agree with you that the code is currently confusing, so I have changed it to use a cse_gpo_index initialized to 0. I have also made similar changes to som_index and gpo_index.
I wonder if you could call either talloc_steal to move the variables like smb_path, smb_share and smb_server to the gp_gpo context or assign to members of gp_gpo right away when calling ad_gpo_extract_smb_components() instead of allocating the result on state and assigning to local variables. Not a big deal though.
If I store directly to the gp_gpo member variables, they are initially stored correctly. However, later on (in ad_gpo_cse_step), the member variables have corrupted values. Not sure why. As such, I have left this part of the code unchanged.
I'll try to play with this area of code during my testing. But the strdup shouldn't block the inclusion of the patches to master.
Actually, I agree with you here as well, so I have changed the code to avoid the double-allocation (of allocating first to state and then strdup'ing to gp_gpo).
In a previous email, I mentioned that I tried to directly assign the output values to member variables of gp->gpo, and that this was leading to corruption. Thanks to Lukas's suggestion, I ran valgrind, which showed a "use-after-free" condition leading to corruption, which made my mistake evident, which was that I was still using state (rather than gp_gpo) as the TALLOC_CTX, thereby disrupting the logical memory hierarchy. In other words, I was doing:
ret = ad_gpo_extract_smb_components(state, state->server_hostname, file_sys_path, &gp_gpo->smb_server, &gp_gpo->smb_share, &gp_gpo->smb_path);instead of the correct solution, which I am now doing, which is:
ret = ad_gpo_extract_smb_components(gp_gpo, state->server_hostname, file_sys_path, &gp_gpo->smb_server, &gp_gpo->smb_share, &gp_gpo->smb_path);I have attached a revised patch that addresses these two issues. I have also attached the previous (unchanged) sysdb_gpo API patch for your convenience.
Thanks, Yassir.
Hi,
after your helpful instructions I was able to set up the GPOs but I'm seeing an odd behaviour and I'm not sure which way to fix it..
So the thing I'm seeing is that the file we fetch from AD contains the user or group names already, but SSSD seems to be looking for SIDs in ad_gpo_access_check().
This is the contents of the file as downloaded on my machine. Note the garbage (?) at the beginning of the file:
# cat /var/lib/sss/gpo_cache/win.example.com/Policies/{94235CE6-B2F2-43C4-8C9F-CB24C0B61E0B}/Machine/Microsoft/Windows\ NT/SecEdit/GptTmpl.inf ��[Unicode] Unicode=yes [Version] signature="$CHICAGO$" Revision=1 [Privilege Rights] SeInteractiveLogonRight = tuser,tgroup1,*S-1-5-32-544,administrator
I'm using Windows server 2012 if that matters.
When I navigate to the SYSVOL folder on the server, I see the same contents, including user names (except for the extra bytes at the start).
So I'm pretty sure I must be missing the part that converts the names to SIDs or vice-versa..
The gpo code in sssd is indeed only looking for SIDs (not names) in the policy files it parses. In this particular case, it wouldn't be possible to convert the user/group name to a SID anyway, b/c the name is not fully-qualified (e.g. tuser).
After doing some investigation to figure out why I have never run into this issue, I found that this issue arises if one does not enter a fully-qualified name for the user/group (in the GPME GUI). If one specifies the user/group using a fully-qualified name (either by browsing for the name, or by manually entering a fully-qualified name in the text box provided by GPME), then the name is converted to a SID before being written to the GptTmpl.inf file (and the SID is prepended with an asterisk character). This is clearly the preferred (and most-used) approach. However, if one specifies the user/group by entering a non-fully-qualified name, then no SID conversion is done and the name itself (e.g. tuser, tgroup1) is written to the GptTmpl.inf file. For more details, see: http://morgansimonsen.wordpress.com/2008/05/26/working-with-group-policy-res...
I propose that we do *not* support this use case, since it seems like such a corner case (and I'm not sure how we would determine which domain the user/group belongs to). In other words, if someone manually enters a non-fully-qualified user/group name (or any other non-SID token), then the gpo code should simply continue to ignore that token. One could argue that the gpo code should throw a parsing error when it encounters a non-SID token (rather than ignoring it), but I think it is better to simply ignore it, b/c it is possible that policy files containing non-SID tokens exist in the wild (despite it being considered poor practice).
Regards, Yassir.
On Thu, Jul 31, 2014 at 10:45:57AM -0400, Yassir Elley wrote:
----- Original Message -----
On Wed, Jul 23, 2014 at 08:02:55PM -0400, Yassir Elley wrote:
----- Original Message -----
On Wed, Jul 23, 2014 at 03:03:34AM -0400, Yassir Elley wrote:
----- Original Message -----
On Tue, Jul 22, 2014 at 03:13:58AM -0400, Yassir Elley wrote:
See some comments about ad_gpo.c inline, I'm still reading ad_gpo_child.c
> From f3f00a78102ad5e389266b28c43d0396caf57592 Mon Sep 17 00:00:00 > 2001 > From: Yassir Elley yelley@redhat.com > Date: Tue, 22 Jul 2014 03:03:03 -0400 > Subject: [PATCH 2/2] AD-GPO: only download policy files if gpo > version > changes
[...]
> @@ -1298,8 +1304,6 @@ ad_gpo_cse_step(struct tevent_req *req) > { > struct tevent_req *subreq; > struct ad_gpo_access_state *state; > - char *cse_smb_uri; > - char *cse_unix_path; > int i = 0; > > state = tevent_req_data(req, struct ad_gpo_access_state);
It's not really visible from the diff, but ad_gpo_cse_step increments the index as the first thing it does and only then assigns to cse_filtered_gpo. This reads to me as if you were skipping the first entry...shouldn't the code read the other way around, first assign the GPO and then increment the index?
I ran into problems when using an index initialized to 0. Although I don't remember what those problems were anymore, I was able to solve them by using an index initialized to -1, which is what I am using now, and which is why the current code works correctly (and doesn't skip the first entry).
OK, thank you, I admit I haven't run any tests yet. Can you add a comment to that part of code, then? (You don't have to re-send the patches now)
Actually, I agree with you that the code is currently confusing, so I have changed it to use a cse_gpo_index initialized to 0. I have also made similar changes to som_index and gpo_index.
I wonder if you could call either talloc_steal to move the variables like smb_path, smb_share and smb_server to the gp_gpo context or assign to members of gp_gpo right away when calling ad_gpo_extract_smb_components() instead of allocating the result on state and assigning to local variables. Not a big deal though.
If I store directly to the gp_gpo member variables, they are initially stored correctly. However, later on (in ad_gpo_cse_step), the member variables have corrupted values. Not sure why. As such, I have left this part of the code unchanged.
I'll try to play with this area of code during my testing. But the strdup shouldn't block the inclusion of the patches to master.
Actually, I agree with you here as well, so I have changed the code to avoid the double-allocation (of allocating first to state and then strdup'ing to gp_gpo).
In a previous email, I mentioned that I tried to directly assign the output values to member variables of gp->gpo, and that this was leading to corruption. Thanks to Lukas's suggestion, I ran valgrind, which showed a "use-after-free" condition leading to corruption, which made my mistake evident, which was that I was still using state (rather than gp_gpo) as the TALLOC_CTX, thereby disrupting the logical memory hierarchy. In other words, I was doing:
ret = ad_gpo_extract_smb_components(state, state->server_hostname, file_sys_path, &gp_gpo->smb_server, &gp_gpo->smb_share, &gp_gpo->smb_path);instead of the correct solution, which I am now doing, which is:
ret = ad_gpo_extract_smb_components(gp_gpo, state->server_hostname, file_sys_path, &gp_gpo->smb_server, &gp_gpo->smb_share, &gp_gpo->smb_path);I have attached a revised patch that addresses these two issues. I have also attached the previous (unchanged) sysdb_gpo API patch for your convenience.
Thanks, Yassir.
Hi,
after your helpful instructions I was able to set up the GPOs but I'm seeing an odd behaviour and I'm not sure which way to fix it..
So the thing I'm seeing is that the file we fetch from AD contains the user or group names already, but SSSD seems to be looking for SIDs in ad_gpo_access_check().
This is the contents of the file as downloaded on my machine. Note the garbage (?) at the beginning of the file:
# cat /var/lib/sss/gpo_cache/win.example.com/Policies/{94235CE6-B2F2-43C4-8C9F-CB24C0B61E0B}/Machine/Microsoft/Windows\ NT/SecEdit/GptTmpl.inf ��[Unicode] Unicode=yes [Version] signature="$CHICAGO$" Revision=1 [Privilege Rights] SeInteractiveLogonRight = tuser,tgroup1,*S-1-5-32-544,administrator
I'm using Windows server 2012 if that matters.
When I navigate to the SYSVOL folder on the server, I see the same contents, including user names (except for the extra bytes at the start).
So I'm pretty sure I must be missing the part that converts the names to SIDs or vice-versa..
The gpo code in sssd is indeed only looking for SIDs (not names) in the policy files it parses. In this particular case, it wouldn't be possible to convert the user/group name to a SID anyway, b/c the name is not fully-qualified (e.g. tuser).
After doing some investigation to figure out why I have never run into this issue, I found that this issue arises if one does not enter a fully-qualified name for the user/group (in the GPME GUI). If one specifies the user/group using a fully-qualified name (either by browsing for the name, or by manually entering a fully-qualified name in the text box provided by GPME), then the name is converted to a SID before being written to the GptTmpl.inf file (and the SID is prepended with an asterisk character). This is clearly the preferred (and most-used) approach. However, if one specifies the user/group by entering a non-fully-qualified name, then no SID conversion is done and the name itself (e.g. tuser, tgroup1) is written to the GptTmpl.inf file. For more details, see: http://morgansimonsen.wordpress.com/2008/05/26/working-with-group-policy-res...
I propose that we do *not* support this use case, since it seems like such a corner case (and I'm not sure how we would determine which domain the user/group belongs to). In other words, if someone manually enters a non-fully-qualified user/group name (or any other non-SID token), then the gpo code should simply continue to ignore that token. One could argue that the gpo code should throw a parsing error when it encounters a non-SID token (rather than ignoring it), but I think it is better to simply ignore it, b/c it is possible that policy files containing non-SID tokens exist in the wild (despite it being considered poor practice).
Regards, Yassir.
I did some more testing and experiments and found out that the dialog to add names (as opposed to browse for names) even lets you add nonexistant users...that's quite bad..
So I propose we act differently with respect to allow and deny lists. If we encounter a non-SID on the allow list, we skip it. If we encounter a non-SID in the deny list, we deny access and log verbosely that the admin needs to go and change his policies.
This would be in line with how we proposed to handle typos in the simple allow list btw.
----- Original Message -----
On Thu, Jul 31, 2014 at 10:45:57AM -0400, Yassir Elley wrote:
----- Original Message -----
On Wed, Jul 23, 2014 at 08:02:55PM -0400, Yassir Elley wrote:
----- Original Message -----
On Wed, Jul 23, 2014 at 03:03:34AM -0400, Yassir Elley wrote:
----- Original Message ----- > On Tue, Jul 22, 2014 at 03:13:58AM -0400, Yassir Elley wrote: > > See some comments about ad_gpo.c inline, I'm still reading > ad_gpo_child.c > > > From f3f00a78102ad5e389266b28c43d0396caf57592 Mon Sep 17 > > 00:00:00 > > 2001 > > From: Yassir Elley yelley@redhat.com > > Date: Tue, 22 Jul 2014 03:03:03 -0400 > > Subject: [PATCH 2/2] AD-GPO: only download policy files if gpo > > version > > changes > > [...] > > > @@ -1298,8 +1304,6 @@ ad_gpo_cse_step(struct tevent_req *req) > > { > > struct tevent_req *subreq; > > struct ad_gpo_access_state *state; > > - char *cse_smb_uri; > > - char *cse_unix_path; > > int i = 0; > > > > state = tevent_req_data(req, struct ad_gpo_access_state); > > It's not really visible from the diff, but ad_gpo_cse_step > increments > the index as the first thing it does and only then assigns to > cse_filtered_gpo. This reads to me as if you were skipping the > first > entry...shouldn't the code read the other way around, first > assign > the > GPO and then increment the index?
I ran into problems when using an index initialized to 0. Although I don't remember what those problems were anymore, I was able to solve them by using an index initialized to -1, which is what I am using now, and which is why the current code works correctly (and doesn't skip the first entry).
OK, thank you, I admit I haven't run any tests yet. Can you add a comment to that part of code, then? (You don't have to re-send the patches now)
Actually, I agree with you that the code is currently confusing, so I have changed it to use a cse_gpo_index initialized to 0. I have also made similar changes to som_index and gpo_index.
> I wonder if you could call either talloc_steal to move the > variables > like smb_path, smb_share and smb_server to the gp_gpo context or > assign > to members of gp_gpo right away when calling > ad_gpo_extract_smb_components() instead of allocating the result > on > state and assigning to local variables. Not a big deal though.
If I store directly to the gp_gpo member variables, they are initially stored correctly. However, later on (in ad_gpo_cse_step), the member variables have corrupted values. Not sure why. As such, I have left this part of the code unchanged.
I'll try to play with this area of code during my testing. But the strdup shouldn't block the inclusion of the patches to master.
Actually, I agree with you here as well, so I have changed the code to avoid the double-allocation (of allocating first to state and then strdup'ing to gp_gpo).
In a previous email, I mentioned that I tried to directly assign the output values to member variables of gp->gpo, and that this was leading to corruption. Thanks to Lukas's suggestion, I ran valgrind, which showed a "use-after-free" condition leading to corruption, which made my mistake evident, which was that I was still using state (rather than gp_gpo) as the TALLOC_CTX, thereby disrupting the logical memory hierarchy. In other words, I was doing:
ret = ad_gpo_extract_smb_components(state, state->server_hostname, file_sys_path, &gp_gpo->smb_server, &gp_gpo->smb_share, &gp_gpo->smb_path);instead of the correct solution, which I am now doing, which is:
ret = ad_gpo_extract_smb_components(gp_gpo, state->server_hostname, file_sys_path, &gp_gpo->smb_server, &gp_gpo->smb_share, &gp_gpo->smb_path);I have attached a revised patch that addresses these two issues. I have also attached the previous (unchanged) sysdb_gpo API patch for your convenience.
Thanks, Yassir.
Hi,
after your helpful instructions I was able to set up the GPOs but I'm seeing an odd behaviour and I'm not sure which way to fix it..
So the thing I'm seeing is that the file we fetch from AD contains the user or group names already, but SSSD seems to be looking for SIDs in ad_gpo_access_check().
This is the contents of the file as downloaded on my machine. Note the garbage (?) at the beginning of the file:
# cat /var/lib/sss/gpo_cache/win.example.com/Policies/{94235CE6-B2F2-43C4-8C9F-CB24C0B61E0B}/Machine/Microsoft/Windows\ NT/SecEdit/GptTmpl.inf ��[Unicode] Unicode=yes [Version] signature="$CHICAGO$" Revision=1 [Privilege Rights] SeInteractiveLogonRight = tuser,tgroup1,*S-1-5-32-544,administrator
I'm using Windows server 2012 if that matters.
When I navigate to the SYSVOL folder on the server, I see the same contents, including user names (except for the extra bytes at the start).
So I'm pretty sure I must be missing the part that converts the names to SIDs or vice-versa..
The gpo code in sssd is indeed only looking for SIDs (not names) in the policy files it parses. In this particular case, it wouldn't be possible to convert the user/group name to a SID anyway, b/c the name is not fully-qualified (e.g. tuser).
After doing some investigation to figure out why I have never run into this issue, I found that this issue arises if one does not enter a fully-qualified name for the user/group (in the GPME GUI). If one specifies the user/group using a fully-qualified name (either by browsing for the name, or by manually entering a fully-qualified name in the text box provided by GPME), then the name is converted to a SID before being written to the GptTmpl.inf file (and the SID is prepended with an asterisk character). This is clearly the preferred (and most-used) approach. However, if one specifies the user/group by entering a non-fully-qualified name, then no SID conversion is done and the name itself (e.g. tuser, tgroup1) is written to the GptTmpl.inf file. For more details, see: http://morgansimonsen.wordpress.com/2008/05/26/working-with-group-policy-res...
I propose that we do *not* support this use case, since it seems like such a corner case (and I'm not sure how we would determine which domain the user/group belongs to). In other words, if someone manually enters a non-fully-qualified user/group name (or any other non-SID token), then the gpo code should simply continue to ignore that token. One could argue that the gpo code should throw a parsing error when it encounters a non-SID token (rather than ignoring it), but I think it is better to simply ignore it, b/c it is possible that policy files containing non-SID tokens exist in the wild (despite it being considered poor practice).
Regards, Yassir.
I did some more testing and experiments and found out that the dialog to add names (as opposed to browse for names) even lets you add nonexistant users...that's quite bad..
So I propose we act differently with respect to allow and deny lists. If we encounter a non-SID on the allow list, we skip it. If we encounter a non-SID in the deny list, we deny access and log verbosely that the admin needs to go and change his policies.
This would be in line with how we proposed to handle typos in the simple allow list btw.
I disagree.
My understanding is that, if an AD admin intentionally places a non-qualified name on either the allowed or denied list, then each domain member (running Windows) will either match that name, or not match that name (regardless of which list it's on). It is perfectly valid for some domain members to match, and others not to match. Considering that AD is a centralized system, this is very odd, but that is why it's considered such poor practice to use non-qualified names on these lists; this is a deprecated corner case. Since sssd can't possibly match a non-qualified name, I think we should simply never match (i.e. always skip).
Also, the simple access provider seems to consistently deny access if there are any typos on either of its allowed/denied lists (not just if there are typos on the denied list).
Regards, Yassir.
On Tue, Aug 05, 2014 at 06:30:26PM -0400, Yassir Elley wrote:
----- Original Message -----
On Thu, Jul 31, 2014 at 10:45:57AM -0400, Yassir Elley wrote:
----- Original Message -----
On Wed, Jul 23, 2014 at 08:02:55PM -0400, Yassir Elley wrote:
----- Original Message -----
On Wed, Jul 23, 2014 at 03:03:34AM -0400, Yassir Elley wrote: > > > ----- Original Message ----- > > On Tue, Jul 22, 2014 at 03:13:58AM -0400, Yassir Elley wrote: > > > > See some comments about ad_gpo.c inline, I'm still reading > > ad_gpo_child.c > > > > > From f3f00a78102ad5e389266b28c43d0396caf57592 Mon Sep 17 > > > 00:00:00 > > > 2001 > > > From: Yassir Elley yelley@redhat.com > > > Date: Tue, 22 Jul 2014 03:03:03 -0400 > > > Subject: [PATCH 2/2] AD-GPO: only download policy files if gpo > > > version > > > changes > > > > [...] > > > > > @@ -1298,8 +1304,6 @@ ad_gpo_cse_step(struct tevent_req *req) > > > { > > > struct tevent_req *subreq; > > > struct ad_gpo_access_state *state; > > > - char *cse_smb_uri; > > > - char *cse_unix_path; > > > int i = 0; > > > > > > state = tevent_req_data(req, struct ad_gpo_access_state); > > > > It's not really visible from the diff, but ad_gpo_cse_step > > increments > > the index as the first thing it does and only then assigns to > > cse_filtered_gpo. This reads to me as if you were skipping the > > first > > entry...shouldn't the code read the other way around, first > > assign > > the > > GPO and then increment the index? > > I ran into problems when using an index initialized to 0. Although > I > don't > remember what those problems were anymore, I was able to solve them > by > using an index initialized to -1, which is what I am using now, and > which > is why the current code works correctly (and doesn't skip the first > entry).
OK, thank you, I admit I haven't run any tests yet. Can you add a comment to that part of code, then? (You don't have to re-send the patches now)
Actually, I agree with you that the code is currently confusing, so I have changed it to use a cse_gpo_index initialized to 0. I have also made similar changes to som_index and gpo_index.
> > I wonder if you could call either talloc_steal to move the > > variables > > like smb_path, smb_share and smb_server to the gp_gpo context or > > assign > > to members of gp_gpo right away when calling > > ad_gpo_extract_smb_components() instead of allocating the result > > on > > state and assigning to local variables. Not a big deal though. > > If I store directly to the gp_gpo member variables, they are > initially > stored correctly. However, later on (in ad_gpo_cse_step), the > member > variables have corrupted values. Not sure why. As such, I have left > this > part of the code unchanged.
I'll try to play with this area of code during my testing. But the strdup shouldn't block the inclusion of the patches to master.
Actually, I agree with you here as well, so I have changed the code to avoid the double-allocation (of allocating first to state and then strdup'ing to gp_gpo).
In a previous email, I mentioned that I tried to directly assign the output values to member variables of gp->gpo, and that this was leading to corruption. Thanks to Lukas's suggestion, I ran valgrind, which showed a "use-after-free" condition leading to corruption, which made my mistake evident, which was that I was still using state (rather than gp_gpo) as the TALLOC_CTX, thereby disrupting the logical memory hierarchy. In other words, I was doing:
ret = ad_gpo_extract_smb_components(state, state->server_hostname, file_sys_path, &gp_gpo->smb_server, &gp_gpo->smb_share, &gp_gpo->smb_path);instead of the correct solution, which I am now doing, which is:
ret = ad_gpo_extract_smb_components(gp_gpo, state->server_hostname, file_sys_path, &gp_gpo->smb_server, &gp_gpo->smb_share, &gp_gpo->smb_path);I have attached a revised patch that addresses these two issues. I have also attached the previous (unchanged) sysdb_gpo API patch for your convenience.
Thanks, Yassir.
Hi,
after your helpful instructions I was able to set up the GPOs but I'm seeing an odd behaviour and I'm not sure which way to fix it..
So the thing I'm seeing is that the file we fetch from AD contains the user or group names already, but SSSD seems to be looking for SIDs in ad_gpo_access_check().
This is the contents of the file as downloaded on my machine. Note the garbage (?) at the beginning of the file:
# cat /var/lib/sss/gpo_cache/win.example.com/Policies/{94235CE6-B2F2-43C4-8C9F-CB24C0B61E0B}/Machine/Microsoft/Windows\ NT/SecEdit/GptTmpl.inf ��[Unicode] Unicode=yes [Version] signature="$CHICAGO$" Revision=1 [Privilege Rights] SeInteractiveLogonRight = tuser,tgroup1,*S-1-5-32-544,administrator
I'm using Windows server 2012 if that matters.
When I navigate to the SYSVOL folder on the server, I see the same contents, including user names (except for the extra bytes at the start).
So I'm pretty sure I must be missing the part that converts the names to SIDs or vice-versa..
The gpo code in sssd is indeed only looking for SIDs (not names) in the policy files it parses. In this particular case, it wouldn't be possible to convert the user/group name to a SID anyway, b/c the name is not fully-qualified (e.g. tuser).
After doing some investigation to figure out why I have never run into this issue, I found that this issue arises if one does not enter a fully-qualified name for the user/group (in the GPME GUI). If one specifies the user/group using a fully-qualified name (either by browsing for the name, or by manually entering a fully-qualified name in the text box provided by GPME), then the name is converted to a SID before being written to the GptTmpl.inf file (and the SID is prepended with an asterisk character). This is clearly the preferred (and most-used) approach. However, if one specifies the user/group by entering a non-fully-qualified name, then no SID conversion is done and the name itself (e.g. tuser, tgroup1) is written to the GptTmpl.inf file. For more details, see: http://morgansimonsen.wordpress.com/2008/05/26/working-with-group-policy-res...
I propose that we do *not* support this use case, since it seems like such a corner case (and I'm not sure how we would determine which domain the user/group belongs to). In other words, if someone manually enters a non-fully-qualified user/group name (or any other non-SID token), then the gpo code should simply continue to ignore that token. One could argue that the gpo code should throw a parsing error when it encounters a non-SID token (rather than ignoring it), but I think it is better to simply ignore it, b/c it is possible that policy files containing non-SID tokens exist in the wild (despite it being considered poor practice).
Regards, Yassir.
I did some more testing and experiments and found out that the dialog to add names (as opposed to browse for names) even lets you add nonexistant users...that's quite bad..
So I propose we act differently with respect to allow and deny lists. If we encounter a non-SID on the allow list, we skip it. If we encounter a non-SID in the deny list, we deny access and log verbosely that the admin needs to go and change his policies.
This would be in line with how we proposed to handle typos in the simple allow list btw.
I disagree.
My understanding is that, if an AD admin intentionally places a non-qualified name on either the allowed or denied list, then each domain member (running Windows) will either match that name, or not match that name (regardless of which list it's on). It is perfectly valid for some domain members to match, and others not to match. Considering that AD is a centralized system, this is very odd, but that is why it's considered such poor practice to use non-qualified names on these lists; this is a deprecated corner case. Since sssd can't possibly match a non-qualified name, I think we should simply never match (i.e. always skip).
Also, the simple access provider seems to consistently deny access if there are any typos on either of its allowed/denied lists (not just if there are typos on the denied list).
Regards, Yassir.
Do you know how exactly do windows client behave? I spoke to simo today on #sssd and he asked us to implement the same behaviour Windows clients implement:
18:55 < jhrozek> simo: do you have time to chime into a GPO discussion I was having with yelley ? 18:57 < simo> jhrozek: where ? 18:58 < jhrozek> simo: sssd-devel, the thread was called "[PATCH] AD: Cache gpo version; only download policy files if version changes" 18:59 < simo> jhrozek: ok what is the contentious point ? 19:00 < jhrozek> simo: what do do with entries that are represented as user names and not SIDs 19:00 < jhrozek> simo: especially in the deny case 19:01 < jhrozek> simo: AD does no validation so you can have completely nonexistant entries in the list and the names are not qualified, so ou have no idea which domain they apply to 19:01 < simo> resolve them > 19:01 < simo> ? 19:01 < jhrozek> to which domain? 19:01 < simo> jhrozek: what does AD do ? 19:02 < jhrozek> umm, I think yelley described that in the e-mail but I'm not 100% sure 19:03 < simo> jhrozek: it is unclear to me what windows does exactly 19:04 < jhrozek> simo: this is what yassir said and mind you, I haven't tested this myself: 19:04 < simo> does it deny if a name does not match ? 19:04 < jhrozek> then each domain member (running Windows) will either match that name, or not match that 19:04 < jhrozek> name (regardless of which list it's on). It is perfectly valid for some domain members to match, and others not to match. Considering that AD is a centralized system, this is very odd, but that is why it's 19:04 < jhrozek> considered such poor practice to use non-qualified names on these lists; this is a deprecated corner case. 19:04 < simo> I just read it 19:04 < simo> it is not clear 19:04 < jhrozek> ok, sorry 19:04 < simo> not sufficient to bnase a decision on it 19:04 < jhrozek> to me it reads like windows matches against its own domain only 19:04 < simo> if unqualified names are normally used in AD then we must deal with it 19:04 < jhrozek> which would be strange, though 19:04 < simo> and we need to deal with it the same way windows clients do 19:05 < simo> so testing is necessary 19:05 < jhrozek> ok
----- Original Message -----
On Tue, Aug 05, 2014 at 06:30:26PM -0400, Yassir Elley wrote:
----- Original Message -----
On Thu, Jul 31, 2014 at 10:45:57AM -0400, Yassir Elley wrote:
----- Original Message -----
On Wed, Jul 23, 2014 at 08:02:55PM -0400, Yassir Elley wrote:
----- Original Message ----- > On Wed, Jul 23, 2014 at 03:03:34AM -0400, Yassir Elley wrote: > > > > > > ----- Original Message ----- > > > On Tue, Jul 22, 2014 at 03:13:58AM -0400, Yassir Elley wrote: > > > > > > See some comments about ad_gpo.c inline, I'm still reading > > > ad_gpo_child.c > > > > > > > From f3f00a78102ad5e389266b28c43d0396caf57592 Mon Sep 17 > > > > 00:00:00 > > > > 2001 > > > > From: Yassir Elley yelley@redhat.com > > > > Date: Tue, 22 Jul 2014 03:03:03 -0400 > > > > Subject: [PATCH 2/2] AD-GPO: only download policy files if > > > > gpo > > > > version > > > > changes > > > > > > [...] > > > > > > > @@ -1298,8 +1304,6 @@ ad_gpo_cse_step(struct tevent_req > > > > *req) > > > > { > > > > struct tevent_req *subreq; > > > > struct ad_gpo_access_state *state; > > > > - char *cse_smb_uri; > > > > - char *cse_unix_path; > > > > int i = 0; > > > > > > > > state = tevent_req_data(req, struct > > > > ad_gpo_access_state); > > > > > > It's not really visible from the diff, but ad_gpo_cse_step > > > increments > > > the index as the first thing it does and only then assigns to > > > cse_filtered_gpo. This reads to me as if you were skipping > > > the > > > first > > > entry...shouldn't the code read the other way around, first > > > assign > > > the > > > GPO and then increment the index? > > > > I ran into problems when using an index initialized to 0. > > Although > > I > > don't > > remember what those problems were anymore, I was able to solve > > them > > by > > using an index initialized to -1, which is what I am using now, > > and > > which > > is why the current code works correctly (and doesn't skip the > > first > > entry). > > OK, thank you, I admit I haven't run any tests yet. Can you add a > comment to that part of code, then? (You don't have to re-send > the > patches now)
Actually, I agree with you that the code is currently confusing, so I have changed it to use a cse_gpo_index initialized to 0. I have also made similar changes to som_index and gpo_index.
> > > > I wonder if you could call either talloc_steal to move the > > > variables > > > like smb_path, smb_share and smb_server to the gp_gpo context > > > or > > > assign > > > to members of gp_gpo right away when calling > > > ad_gpo_extract_smb_components() instead of allocating the > > > result > > > on > > > state and assigning to local variables. Not a big deal > > > though. > > > > If I store directly to the gp_gpo member variables, they are > > initially > > stored correctly. However, later on (in ad_gpo_cse_step), the > > member > > variables have corrupted values. Not sure why. As such, I have > > left > > this > > part of the code unchanged. > > I'll try to play with this area of code during my testing. But > the > strdup shouldn't block the inclusion of the patches to master.
Actually, I agree with you here as well, so I have changed the code to avoid the double-allocation (of allocating first to state and then strdup'ing to gp_gpo).
In a previous email, I mentioned that I tried to directly assign the output values to member variables of gp->gpo, and that this was leading to corruption. Thanks to Lukas's suggestion, I ran valgrind, which showed a "use-after-free" condition leading to corruption, which made my mistake evident, which was that I was still using state (rather than gp_gpo) as the TALLOC_CTX, thereby disrupting the logical memory hierarchy. In other words, I was doing:
ret = ad_gpo_extract_smb_components(state, state->server_hostname, file_sys_path, &gp_gpo->smb_server, &gp_gpo->smb_share, &gp_gpo->smb_path);instead of the correct solution, which I am now doing, which is:
ret = ad_gpo_extract_smb_components(gp_gpo, state->server_hostname, file_sys_path, &gp_gpo->smb_server, &gp_gpo->smb_share, &gp_gpo->smb_path);I have attached a revised patch that addresses these two issues. I have also attached the previous (unchanged) sysdb_gpo API patch for your convenience.
Thanks, Yassir.
Hi,
after your helpful instructions I was able to set up the GPOs but I'm seeing an odd behaviour and I'm not sure which way to fix it..
So the thing I'm seeing is that the file we fetch from AD contains the user or group names already, but SSSD seems to be looking for SIDs in ad_gpo_access_check().
This is the contents of the file as downloaded on my machine. Note the garbage (?) at the beginning of the file:
# cat /var/lib/sss/gpo_cache/win.example.com/Policies/{94235CE6-B2F2-43C4-8C9F-CB24C0B61E0B}/Machine/Microsoft/Windows\ NT/SecEdit/GptTmpl.inf ��[Unicode] Unicode=yes [Version] signature="$CHICAGO$" Revision=1 [Privilege Rights] SeInteractiveLogonRight = tuser,tgroup1,*S-1-5-32-544,administrator
I'm using Windows server 2012 if that matters.
When I navigate to the SYSVOL folder on the server, I see the same contents, including user names (except for the extra bytes at the start).
So I'm pretty sure I must be missing the part that converts the names to SIDs or vice-versa..
The gpo code in sssd is indeed only looking for SIDs (not names) in the policy files it parses. In this particular case, it wouldn't be possible to convert the user/group name to a SID anyway, b/c the name is not fully-qualified (e.g. tuser).
After doing some investigation to figure out why I have never run into this issue, I found that this issue arises if one does not enter a fully-qualified name for the user/group (in the GPME GUI). If one specifies the user/group using a fully-qualified name (either by browsing for the name, or by manually entering a fully-qualified name in the text box provided by GPME), then the name is converted to a SID before being written to the GptTmpl.inf file (and the SID is prepended with an asterisk character). This is clearly the preferred (and most-used) approach. However, if one specifies the user/group by entering a non-fully-qualified name, then no SID conversion is done and the name itself (e.g. tuser, tgroup1) is written to the GptTmpl.inf file. For more details, see: http://morgansimonsen.wordpress.com/2008/05/26/working-with-group-policy-res...
I propose that we do *not* support this use case, since it seems like such a corner case (and I'm not sure how we would determine which domain the user/group belongs to). In other words, if someone manually enters a non-fully-qualified user/group name (or any other non-SID token), then the gpo code should simply continue to ignore that token. One could argue that the gpo code should throw a parsing error when it encounters a non-SID token (rather than ignoring it), but I think it is better to simply ignore it, b/c it is possible that policy files containing non-SID tokens exist in the wild (despite it being considered poor practice).
Regards, Yassir.
I did some more testing and experiments and found out that the dialog to add names (as opposed to browse for names) even lets you add nonexistant users...that's quite bad..
So I propose we act differently with respect to allow and deny lists. If we encounter a non-SID on the allow list, we skip it. If we encounter a non-SID in the deny list, we deny access and log verbosely that the admin needs to go and change his policies.
This would be in line with how we proposed to handle typos in the simple allow list btw.
I disagree.
My understanding is that, if an AD admin intentionally places a non-qualified name on either the allowed or denied list, then each domain member (running Windows) will either match that name, or not match that name (regardless of which list it's on). It is perfectly valid for some domain members to match, and others not to match. Considering that AD is a centralized system, this is very odd, but that is why it's considered such poor practice to use non-qualified names on these lists; this is a deprecated corner case. Since sssd can't possibly match a non-qualified name, I think we should simply never match (i.e. always skip).
Also, the simple access provider seems to consistently deny access if there are any typos on either of its allowed/denied lists (not just if there are typos on the denied list).
Regards, Yassir.
Do you know how exactly do windows client behave? I spoke to simo today on #sssd and he asked us to implement the same behaviour Windows clients implement:
18:55 < jhrozek> simo: do you have time to chime into a GPO discussion I was having with yelley ? 18:57 < simo> jhrozek: where ? 18:58 < jhrozek> simo: sssd-devel, the thread was called "[PATCH] AD: Cache gpo version; only download policy files if version changes" 18:59 < simo> jhrozek: ok what is the contentious point ? 19:00 < jhrozek> simo: what do do with entries that are represented as user names and not SIDs 19:00 < jhrozek> simo: especially in the deny case 19:01 < jhrozek> simo: AD does no validation so you can have completely nonexistant entries in the list and the names are not qualified, so ou have no idea which domain they apply to 19:01 < simo> resolve them > 19:01 < simo> ? 19:01 < jhrozek> to which domain? 19:01 < simo> jhrozek: what does AD do ? 19:02 < jhrozek> umm, I think yelley described that in the e-mail but I'm not 100% sure 19:03 < simo> jhrozek: it is unclear to me what windows does exactly 19:04 < jhrozek> simo: this is what yassir said and mind you, I haven't tested this myself: 19:04 < simo> does it deny if a name does not match ? 19:04 < jhrozek> then each domain member (running Windows) will either match that name, or not match that 19:04 < jhrozek> name (regardless of which list it's on). It is perfectly valid for some domain members to match, and others not to match. Considering that AD is a centralized system, this is very odd, but that is why it's 19:04 < jhrozek> considered such poor practice to use non-qualified names on these lists; this is a deprecated corner case. 19:04 < simo> I just read it 19:04 < simo> it is not clear 19:04 < jhrozek> ok, sorry 19:04 < simo> not sufficient to bnase a decision on it 19:04 < jhrozek> to me it reads like windows matches against its own domain only 19:04 < simo> if unqualified names are normally used in AD then we must deal with it 19:04 < jhrozek> which would be strange, though 19:04 < simo> and we need to deal with it the same way windows clients do 19:05 < simo> so testing is necessary 19:05 < jhrozek> ok _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
I have not tested this empirically, but my analysis is based on the following two sources: * http://support.microsoft.com/kb/890737 * http://morgansimonsen.wordpress.com/2008/05/26/working-with-group-policy-res... (unofficial blog posting)
As far as I can tell, you would only use non-qualified names in scenarios for which you want to use the same name across multiple domains. This is similar to using well-known SIDs, except that names can be different in different languages, and also that such names would only be "well-known" among your particular domains (not globally). Given these shortcoming, this is considered poor practice and well-known SIDs should be used instead.
As I mentioned initially, if someone browses to the built-in group "Administrators", then the corresponding SID ("*S-1-5-32-544") is placed in GptTmpl.inf, and all domain members recognize that SID (b/c it is well-known). However, manually entering "Administrators" results in "Administrator" being put in GptTmpl.inf, which means that only domain members that have the same account name in their SAM DB will recognize the group name. A domain member that is running a German version of windows will not recognize the group name (b/c it uses "Administratoren" instead of "Administrators"). So, recognizing the non-qualified name comes down to whether the client's locale is compatible with the domain controller's locale, whether the client has renamed one of the default built-in groups, etc.
Based on this, I believe a windows client behaves as follows: if (non-qualified name is encountered in GPO) { if (non-qualified name in GPO matches non-qualified name in client's SAM DB) { process matching name accordingly (depending on whether it is on allow list or deny list) } else { policy processing fails (at least for this client-side extension); error 1332 reported: "No mapping between account names and security IDs was done" } }
In the case of SSSD, since I don't think we store non-qualified names, there could never possibly be a match. If we want to behave the same way as windows clients behave, I think the correct behavior is to always fail (i.e. deny access) in the presence of non-qualified names in the GPO, regardless of whether the non-qualified name appears on the allow list or deny list.
Regards, Yassir.
On Wed, 2014-08-06 at 17:34 -0400, Yassir Elley wrote:
----- Original Message -----
On Tue, Aug 05, 2014 at 06:30:26PM -0400, Yassir Elley wrote:
----- Original Message -----
On Thu, Jul 31, 2014 at 10:45:57AM -0400, Yassir Elley wrote:
----- Original Message -----
On Wed, Jul 23, 2014 at 08:02:55PM -0400, Yassir Elley wrote: > > > ----- Original Message ----- > > On Wed, Jul 23, 2014 at 03:03:34AM -0400, Yassir Elley wrote: > > > > > > > > > ----- Original Message ----- > > > > On Tue, Jul 22, 2014 at 03:13:58AM -0400, Yassir Elley wrote: > > > > > > > > See some comments about ad_gpo.c inline, I'm still reading > > > > ad_gpo_child.c > > > > > > > > > From f3f00a78102ad5e389266b28c43d0396caf57592 Mon Sep 17 > > > > > 00:00:00 > > > > > 2001 > > > > > From: Yassir Elley yelley@redhat.com > > > > > Date: Tue, 22 Jul 2014 03:03:03 -0400 > > > > > Subject: [PATCH 2/2] AD-GPO: only download policy files if > > > > > gpo > > > > > version > > > > > changes > > > > > > > > [...] > > > > > > > > > @@ -1298,8 +1304,6 @@ ad_gpo_cse_step(struct tevent_req > > > > > *req) > > > > > { > > > > > struct tevent_req *subreq; > > > > > struct ad_gpo_access_state *state; > > > > > - char *cse_smb_uri; > > > > > - char *cse_unix_path; > > > > > int i = 0; > > > > > > > > > > state = tevent_req_data(req, struct > > > > > ad_gpo_access_state); > > > > > > > > It's not really visible from the diff, but ad_gpo_cse_step > > > > increments > > > > the index as the first thing it does and only then assigns to > > > > cse_filtered_gpo. This reads to me as if you were skipping > > > > the > > > > first > > > > entry...shouldn't the code read the other way around, first > > > > assign > > > > the > > > > GPO and then increment the index? > > > > > > I ran into problems when using an index initialized to 0. > > > Although > > > I > > > don't > > > remember what those problems were anymore, I was able to solve > > > them > > > by > > > using an index initialized to -1, which is what I am using now, > > > and > > > which > > > is why the current code works correctly (and doesn't skip the > > > first > > > entry). > > > > OK, thank you, I admit I haven't run any tests yet. Can you add a > > comment to that part of code, then? (You don't have to re-send > > the > > patches now) > > Actually, I agree with you that the code is currently confusing, so > I > have > changed it to use a cse_gpo_index initialized to 0. I have also > made > similar changes to som_index and gpo_index. > > > > > > > I wonder if you could call either talloc_steal to move the > > > > variables > > > > like smb_path, smb_share and smb_server to the gp_gpo context > > > > or > > > > assign > > > > to members of gp_gpo right away when calling > > > > ad_gpo_extract_smb_components() instead of allocating the > > > > result > > > > on > > > > state and assigning to local variables. Not a big deal > > > > though. > > > > > > If I store directly to the gp_gpo member variables, they are > > > initially > > > stored correctly. However, later on (in ad_gpo_cse_step), the > > > member > > > variables have corrupted values. Not sure why. As such, I have > > > left > > > this > > > part of the code unchanged. > > > > I'll try to play with this area of code during my testing. But > > the > > strdup shouldn't block the inclusion of the patches to master. > > Actually, I agree with you here as well, so I have changed the code > to > avoid the double-allocation (of allocating first to state and then > strdup'ing to gp_gpo). > > In a previous email, I mentioned that I tried to directly assign > the > output > values to member variables of gp->gpo, and that this was leading to > corruption. Thanks to Lukas's suggestion, I ran valgrind, which > showed > a > "use-after-free" condition leading to corruption, which made my > mistake > evident, which was that I was still using state (rather than > gp_gpo) as > the TALLOC_CTX, thereby disrupting the logical memory hierarchy. In > other > words, I was doing: > > ret = ad_gpo_extract_smb_components(state, > state->server_hostname, > file_sys_path, > &gp_gpo->smb_server, > &gp_gpo->smb_share, > &gp_gpo->smb_path); > > instead of the correct solution, which I am now doing, which is: > > ret = ad_gpo_extract_smb_components(gp_gpo, > state->server_hostname, > file_sys_path, > &gp_gpo->smb_server, > &gp_gpo->smb_share, > &gp_gpo->smb_path); > > > I have attached a revised patch that addresses these two issues. I > have > also attached the previous (unchanged) sysdb_gpo API patch for your > convenience. > > Thanks, > Yassir.
Hi,
after your helpful instructions I was able to set up the GPOs but I'm seeing an odd behaviour and I'm not sure which way to fix it..
So the thing I'm seeing is that the file we fetch from AD contains the user or group names already, but SSSD seems to be looking for SIDs in ad_gpo_access_check().
This is the contents of the file as downloaded on my machine. Note the garbage (?) at the beginning of the file:
# cat /var/lib/sss/gpo_cache/win.example.com/Policies/{94235CE6-B2F2-43C4-8C9F-CB24C0B61E0B}/Machine/Microsoft/Windows\ NT/SecEdit/GptTmpl.inf ��[Unicode] Unicode=yes [Version] signature="$CHICAGO$" Revision=1 [Privilege Rights] SeInteractiveLogonRight = tuser,tgroup1,*S-1-5-32-544,administrator
I'm using Windows server 2012 if that matters.
When I navigate to the SYSVOL folder on the server, I see the same contents, including user names (except for the extra bytes at the start).
So I'm pretty sure I must be missing the part that converts the names to SIDs or vice-versa..
The gpo code in sssd is indeed only looking for SIDs (not names) in the policy files it parses. In this particular case, it wouldn't be possible to convert the user/group name to a SID anyway, b/c the name is not fully-qualified (e.g. tuser).
After doing some investigation to figure out why I have never run into this issue, I found that this issue arises if one does not enter a fully-qualified name for the user/group (in the GPME GUI). If one specifies the user/group using a fully-qualified name (either by browsing for the name, or by manually entering a fully-qualified name in the text box provided by GPME), then the name is converted to a SID before being written to the GptTmpl.inf file (and the SID is prepended with an asterisk character). This is clearly the preferred (and most-used) approach. However, if one specifies the user/group by entering a non-fully-qualified name, then no SID conversion is done and the name itself (e.g. tuser, tgroup1) is written to the GptTmpl.inf file. For more details, see: http://morgansimonsen.wordpress.com/2008/05/26/working-with-group-policy-res...
I propose that we do *not* support this use case, since it seems like such a corner case (and I'm not sure how we would determine which domain the user/group belongs to). In other words, if someone manually enters a non-fully-qualified user/group name (or any other non-SID token), then the gpo code should simply continue to ignore that token. One could argue that the gpo code should throw a parsing error when it encounters a non-SID token (rather than ignoring it), but I think it is better to simply ignore it, b/c it is possible that policy files containing non-SID tokens exist in the wild (despite it being considered poor practice).
Regards, Yassir.
I did some more testing and experiments and found out that the dialog to add names (as opposed to browse for names) even lets you add nonexistant users...that's quite bad..
So I propose we act differently with respect to allow and deny lists. If we encounter a non-SID on the allow list, we skip it. If we encounter a non-SID in the deny list, we deny access and log verbosely that the admin needs to go and change his policies.
This would be in line with how we proposed to handle typos in the simple allow list btw.
I disagree.
My understanding is that, if an AD admin intentionally places a non-qualified name on either the allowed or denied list, then each domain member (running Windows) will either match that name, or not match that name (regardless of which list it's on). It is perfectly valid for some domain members to match, and others not to match. Considering that AD is a centralized system, this is very odd, but that is why it's considered such poor practice to use non-qualified names on these lists; this is a deprecated corner case. Since sssd can't possibly match a non-qualified name, I think we should simply never match (i.e. always skip).
Also, the simple access provider seems to consistently deny access if there are any typos on either of its allowed/denied lists (not just if there are typos on the denied list).
Regards, Yassir.
Do you know how exactly do windows client behave? I spoke to simo today on #sssd and he asked us to implement the same behaviour Windows clients implement:
18:55 < jhrozek> simo: do you have time to chime into a GPO discussion I was having with yelley ? 18:57 < simo> jhrozek: where ? 18:58 < jhrozek> simo: sssd-devel, the thread was called "[PATCH] AD: Cache gpo version; only download policy files if version changes" 18:59 < simo> jhrozek: ok what is the contentious point ? 19:00 < jhrozek> simo: what do do with entries that are represented as user names and not SIDs 19:00 < jhrozek> simo: especially in the deny case 19:01 < jhrozek> simo: AD does no validation so you can have completely nonexistant entries in the list and the names are not qualified, so ou have no idea which domain they apply to 19:01 < simo> resolve them > 19:01 < simo> ? 19:01 < jhrozek> to which domain? 19:01 < simo> jhrozek: what does AD do ? 19:02 < jhrozek> umm, I think yelley described that in the e-mail but I'm not 100% sure 19:03 < simo> jhrozek: it is unclear to me what windows does exactly 19:04 < jhrozek> simo: this is what yassir said and mind you, I haven't tested this myself: 19:04 < simo> does it deny if a name does not match ? 19:04 < jhrozek> then each domain member (running Windows) will either match that name, or not match that 19:04 < jhrozek> name (regardless of which list it's on). It is perfectly valid for some domain members to match, and others not to match. Considering that AD is a centralized system, this is very odd, but that is why it's 19:04 < jhrozek> considered such poor practice to use non-qualified names on these lists; this is a deprecated corner case. 19:04 < simo> I just read it 19:04 < simo> it is not clear 19:04 < jhrozek> ok, sorry 19:04 < simo> not sufficient to bnase a decision on it 19:04 < jhrozek> to me it reads like windows matches against its own domain only 19:04 < simo> if unqualified names are normally used in AD then we must deal with it 19:04 < jhrozek> which would be strange, though 19:04 < simo> and we need to deal with it the same way windows clients do 19:05 < simo> so testing is necessary 19:05 < jhrozek> ok _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
I have not tested this empirically, but my analysis is based on the following two sources:
- http://support.microsoft.com/kb/890737
- http://morgansimonsen.wordpress.com/2008/05/26/working-with-group-policy-res... (unofficial blog posting)
As far as I can tell, you would only use non-qualified names in scenarios for which you want to use the same name across multiple domains. This is similar to using well-known SIDs, except that names can be different in different languages, and also that such names would only be "well-known" among your particular domains (not globally). Given these shortcoming, this is considered poor practice and well-known SIDs should be used instead.
As I mentioned initially, if someone browses to the built-in group "Administrators", then the corresponding SID ("*S-1-5-32-544") is placed in GptTmpl.inf, and all domain members recognize that SID (b/c it is well-known). However, manually entering "Administrators" results in "Administrator" being put in GptTmpl.inf, which means that only domain members that have the same account name in their SAM DB will recognize the group name. A domain member that is running a German version of windows will not recognize the group name (b/c it uses "Administratoren" instead of "Administrators"). So, recognizing the non-qualified name comes down to whether the client's locale is compatible with the domain controller's locale, whether the client has renamed one of the default built-in groups, etc.
Based on this, I believe a windows client behaves as follows: if (non-qualified name is encountered in GPO) { if (non-qualified name in GPO matches non-qualified name in client's SAM DB) { process matching name accordingly (depending on whether it is on allow list or deny list) } else { policy processing fails (at least for this client-side extension); error 1332 reported: "No mapping between account names and security IDs was done" } }
In the case of SSSD, since I don't think we store non-qualified names, there could never possibly be a match. If we want to behave the same way as windows clients behave, I think the correct behavior is to always fail (i.e. deny access) in the presence of non-qualified names in the GPO, regardless of whether the non-qualified name appears on the allow list or deny list.
It is not really hard to match it to Administrators@domain, why shouldn't we behave just like windows ?
Simo.
----- Original Message -----
On Wed, 2014-08-06 at 17:34 -0400, Yassir Elley wrote:
----- Original Message -----
On Tue, Aug 05, 2014 at 06:30:26PM -0400, Yassir Elley wrote:
----- Original Message -----
On Thu, Jul 31, 2014 at 10:45:57AM -0400, Yassir Elley wrote:
----- Original Message ----- > On Wed, Jul 23, 2014 at 08:02:55PM -0400, Yassir Elley wrote: > > > > > > ----- Original Message ----- > > > On Wed, Jul 23, 2014 at 03:03:34AM -0400, Yassir Elley wrote: > > > > > > > > > > > > ----- Original Message ----- > > > > > On Tue, Jul 22, 2014 at 03:13:58AM -0400, Yassir Elley > > > > > wrote: > > > > > > > > > > See some comments about ad_gpo.c inline, I'm still > > > > > reading > > > > > ad_gpo_child.c > > > > > > > > > > > From f3f00a78102ad5e389266b28c43d0396caf57592 Mon Sep > > > > > > 17 > > > > > > 00:00:00 > > > > > > 2001 > > > > > > From: Yassir Elley yelley@redhat.com > > > > > > Date: Tue, 22 Jul 2014 03:03:03 -0400 > > > > > > Subject: [PATCH 2/2] AD-GPO: only download policy files > > > > > > if > > > > > > gpo > > > > > > version > > > > > > changes > > > > > > > > > > [...] > > > > > > > > > > > @@ -1298,8 +1304,6 @@ ad_gpo_cse_step(struct tevent_req > > > > > > *req) > > > > > > { > > > > > > struct tevent_req *subreq; > > > > > > struct ad_gpo_access_state *state; > > > > > > - char *cse_smb_uri; > > > > > > - char *cse_unix_path; > > > > > > int i = 0; > > > > > > > > > > > > state = tevent_req_data(req, struct > > > > > > ad_gpo_access_state); > > > > > > > > > > It's not really visible from the diff, but > > > > > ad_gpo_cse_step > > > > > increments > > > > > the index as the first thing it does and only then > > > > > assigns to > > > > > cse_filtered_gpo. This reads to me as if you were > > > > > skipping > > > > > the > > > > > first > > > > > entry...shouldn't the code read the other way around, > > > > > first > > > > > assign > > > > > the > > > > > GPO and then increment the index? > > > > > > > > I ran into problems when using an index initialized to 0. > > > > Although > > > > I > > > > don't > > > > remember what those problems were anymore, I was able to > > > > solve > > > > them > > > > by > > > > using an index initialized to -1, which is what I am using > > > > now, > > > > and > > > > which > > > > is why the current code works correctly (and doesn't skip > > > > the > > > > first > > > > entry). > > > > > > OK, thank you, I admit I haven't run any tests yet. Can you > > > add a > > > comment to that part of code, then? (You don't have to > > > re-send > > > the > > > patches now) > > > > Actually, I agree with you that the code is currently > > confusing, so > > I > > have > > changed it to use a cse_gpo_index initialized to 0. I have also > > made > > similar changes to som_index and gpo_index. > > > > > > > > > > I wonder if you could call either talloc_steal to move > > > > > the > > > > > variables > > > > > like smb_path, smb_share and smb_server to the gp_gpo > > > > > context > > > > > or > > > > > assign > > > > > to members of gp_gpo right away when calling > > > > > ad_gpo_extract_smb_components() instead of allocating the > > > > > result > > > > > on > > > > > state and assigning to local variables. Not a big deal > > > > > though. > > > > > > > > If I store directly to the gp_gpo member variables, they > > > > are > > > > initially > > > > stored correctly. However, later on (in ad_gpo_cse_step), > > > > the > > > > member > > > > variables have corrupted values. Not sure why. As such, I > > > > have > > > > left > > > > this > > > > part of the code unchanged. > > > > > > I'll try to play with this area of code during my testing. > > > But > > > the > > > strdup shouldn't block the inclusion of the patches to > > > master. > > > > Actually, I agree with you here as well, so I have changed the > > code > > to > > avoid the double-allocation (of allocating first to state and > > then > > strdup'ing to gp_gpo). > > > > In a previous email, I mentioned that I tried to directly > > assign > > the > > output > > values to member variables of gp->gpo, and that this was > > leading to > > corruption. Thanks to Lukas's suggestion, I ran valgrind, which > > showed > > a > > "use-after-free" condition leading to corruption, which made my > > mistake > > evident, which was that I was still using state (rather than > > gp_gpo) as > > the TALLOC_CTX, thereby disrupting the logical memory > > hierarchy. In > > other > > words, I was doing: > > > > ret = ad_gpo_extract_smb_components(state, > > state->server_hostname, > > file_sys_path, > > &gp_gpo->smb_server, > > &gp_gpo->smb_share, > > &gp_gpo->smb_path); > > > > instead of the correct solution, which I am now doing, which > > is: > > > > ret = ad_gpo_extract_smb_components(gp_gpo, > > state->server_hostname, > > file_sys_path, > > &gp_gpo->smb_server, > > &gp_gpo->smb_share, > > &gp_gpo->smb_path); > > > > > > I have attached a revised patch that addresses these two > > issues. I > > have > > also attached the previous (unchanged) sysdb_gpo API patch for > > your > > convenience. > > > > Thanks, > > Yassir. > > Hi, > > after your helpful instructions I was able to set up the GPOs but > I'm > seeing an odd behaviour and I'm not sure which way to fix it.. > > So the thing I'm seeing is that the file we fetch from AD > contains > the > user or group names already, but SSSD seems to be looking for > SIDs in > ad_gpo_access_check(). > > This is the contents of the file as downloaded on my machine. > Note > the > garbage (?) at the beginning of the file: > > # cat > /var/lib/sss/gpo_cache/win.example.com/Policies/{94235CE6-B2F2-43C4-8C9F-CB24C0B61E0B}/Machine/Microsoft/Windows\ > NT/SecEdit/GptTmpl.inf > ��[Unicode] > Unicode=yes > [Version] > signature="$CHICAGO$" > Revision=1 > [Privilege Rights] > SeInteractiveLogonRight = > tuser,tgroup1,*S-1-5-32-544,administrator > > I'm using Windows server 2012 if that matters. > > When I navigate to the SYSVOL folder on the server, I see the > same > contents, > including user names (except for the extra bytes at the start). > > So I'm pretty sure I must be missing the part that converts the > names > to > SIDs or vice-versa..
The gpo code in sssd is indeed only looking for SIDs (not names) in the policy files it parses. In this particular case, it wouldn't be possible to convert the user/group name to a SID anyway, b/c the name is not fully-qualified (e.g. tuser).
After doing some investigation to figure out why I have never run into this issue, I found that this issue arises if one does not enter a fully-qualified name for the user/group (in the GPME GUI). If one specifies the user/group using a fully-qualified name (either by browsing for the name, or by manually entering a fully-qualified name in the text box provided by GPME), then the name is converted to a SID before being written to the GptTmpl.inf file (and the SID is prepended with an asterisk character). This is clearly the preferred (and most-used) approach. However, if one specifies the user/group by entering a non-fully-qualified name, then no SID conversion is done and the name itself (e.g. tuser, tgroup1) is written to the GptTmpl.inf file. For more details, see: http://morgansimonsen.wordpress.com/2008/05/26/working-with-group-policy-res...
I propose that we do *not* support this use case, since it seems like such a corner case (and I'm not sure how we would determine which domain the user/group belongs to). In other words, if someone manually enters a non-fully-qualified user/group name (or any other non-SID token), then the gpo code should simply continue to ignore that token. One could argue that the gpo code should throw a parsing error when it encounters a non-SID token (rather than ignoring it), but I think it is better to simply ignore it, b/c it is possible that policy files containing non-SID tokens exist in the wild (despite it being considered poor practice).
Regards, Yassir.
I did some more testing and experiments and found out that the dialog to add names (as opposed to browse for names) even lets you add nonexistant users...that's quite bad..
So I propose we act differently with respect to allow and deny lists. If we encounter a non-SID on the allow list, we skip it. If we encounter a non-SID in the deny list, we deny access and log verbosely that the admin needs to go and change his policies.
This would be in line with how we proposed to handle typos in the simple allow list btw.
I disagree.
My understanding is that, if an AD admin intentionally places a non-qualified name on either the allowed or denied list, then each domain member (running Windows) will either match that name, or not match that name (regardless of which list it's on). It is perfectly valid for some domain members to match, and others not to match. Considering that AD is a centralized system, this is very odd, but that is why it's considered such poor practice to use non-qualified names on these lists; this is a deprecated corner case. Since sssd can't possibly match a non-qualified name, I think we should simply never match (i.e. always skip).
Also, the simple access provider seems to consistently deny access if there are any typos on either of its allowed/denied lists (not just if there are typos on the denied list).
Regards, Yassir.
Do you know how exactly do windows client behave? I spoke to simo today on #sssd and he asked us to implement the same behaviour Windows clients implement:
18:55 < jhrozek> simo: do you have time to chime into a GPO discussion I was having with yelley ? 18:57 < simo> jhrozek: where ? 18:58 < jhrozek> simo: sssd-devel, the thread was called "[PATCH] AD: Cache gpo version; only download policy files if version changes" 18:59 < simo> jhrozek: ok what is the contentious point ? 19:00 < jhrozek> simo: what do do with entries that are represented as user names and not SIDs 19:00 < jhrozek> simo: especially in the deny case 19:01 < jhrozek> simo: AD does no validation so you can have completely nonexistant entries in the list and the names are not qualified, so ou have no idea which domain they apply to 19:01 < simo> resolve them > 19:01 < simo> ? 19:01 < jhrozek> to which domain? 19:01 < simo> jhrozek: what does AD do ? 19:02 < jhrozek> umm, I think yelley described that in the e-mail but I'm not 100% sure 19:03 < simo> jhrozek: it is unclear to me what windows does exactly 19:04 < jhrozek> simo: this is what yassir said and mind you, I haven't tested this myself: 19:04 < simo> does it deny if a name does not match ? 19:04 < jhrozek> then each domain member (running Windows) will either match that name, or not match that 19:04 < jhrozek> name (regardless of which list it's on). It is perfectly valid for some domain members to match, and others not to match. Considering that AD is a centralized system, this is very odd, but that is why it's 19:04 < jhrozek> considered such poor practice to use non-qualified names on these lists; this is a deprecated corner case. 19:04 < simo> I just read it 19:04 < simo> it is not clear 19:04 < jhrozek> ok, sorry 19:04 < simo> not sufficient to bnase a decision on it 19:04 < jhrozek> to me it reads like windows matches against its own domain only 19:04 < simo> if unqualified names are normally used in AD then we must deal with it 19:04 < jhrozek> which would be strange, though 19:04 < simo> and we need to deal with it the same way windows clients do 19:05 < simo> so testing is necessary 19:05 < jhrozek> ok _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
I have not tested this empirically, but my analysis is based on the following two sources:
http://morgansimonsen.wordpress.com/2008/05/26/working-with-group-policy-res... (unofficial blog posting)
As far as I can tell, you would only use non-qualified names in scenarios for which you want to use the same name across multiple domains. This is similar to using well-known SIDs, except that names can be different in different languages, and also that such names would only be "well-known" among your particular domains (not globally). Given these shortcoming, this is considered poor practice and well-known SIDs should be used instead.
As I mentioned initially, if someone browses to the built-in group "Administrators", then the corresponding SID ("*S-1-5-32-544") is placed in GptTmpl.inf, and all domain members recognize that SID (b/c it is well-known). However, manually entering "Administrators" results in "Administrator" being put in GptTmpl.inf, which means that only domain members that have the same account name in their SAM DB will recognize the group name. A domain member that is running a German version of windows will not recognize the group name (b/c it uses "Administratoren" instead of "Administrators"). So, recognizing the non-qualified name comes down to whether the client's locale is compatible with the domain controller's locale, whether the client has renamed one of the default built-in groups, etc.
Based on this, I believe a windows client behaves as follows: if (non-qualified name is encountered in GPO) { if (non-qualified name in GPO matches non-qualified name in client's SAM DB) { process matching name accordingly (depending on whether it is on allow list or deny list) } else { policy processing fails (at least for this client-side extension); error 1332 reported: "No mapping between account names and security IDs was done" } }
In the case of SSSD, since I don't think we store non-qualified names, there could never possibly be a match. If we want to behave the same way as windows clients behave, I think the correct behavior is to always fail (i.e. deny access) in the presence of non-qualified names in the GPO, regardless of whether the non-qualified name appears on the allow list or deny list.
It is not really hard to match it to Administrators@domain, why shouldn't we behave just like windows ?
Simo.
I think a non-qualified name can be thought of as either a built-in group (such as "Administrators") or as a local group (such as "RandomGroup") which happens to have the same name on multiple clients (a la windows workgroups). Since sssd supports neither built-in groups nor local groups, we aren't going to be able to do exactly what a windows client would do. We should do whatever makes sense in an sssd context.
For example, if a Windows client encounters a non-qualified name (such as RandomGroup) in the GPO policy settings, the client doesn't qualify it with the client's domain (RandomGroup@domain) in order to determine a match. If it used that strategy, and RandomGroup@domain didn't appear among the user's memberships, it would simply ignore that group and process the other users/groups in the policy settings. However, that's not what a Windows client does. Rather, the Windows client consults its SAM database for a match against RandomGroup. If it doesn't find a match, it denies access and logs an error (and stops processing any other users/groups). So, the actions are quite different depending on the strategy used (ignore group vs. deny access).
For sssd, if we think it makes sense to match against RandomGroup@domain when we encounter RandomGroup in policy settings, we can certainly do that. If we think we should always deny access when we encounter non-qualified names, we can also do that. However, I think we should apply whatever strategy we decide upon consistently, regardless of whether the non-qualified name is on an allow list or a deny list.
Regards, Yassir.
On Wed, 2014-08-06 at 23:59 -0400, Yassir Elley wrote:
I think a non-qualified name can be thought of as either a built-in group (such as "Administrators") or as a local group (such as "RandomGroup") which happens to have the same name on multiple clients (a la windows workgroups). Since sssd supports neither built-in groups nor local groups, we aren't going to be able to do exactly what a windows client would do. We should do whatever makes sense in an sssd context.
Yes but we also need to match Windows administrators expectations and GPO general behavior. If "Administrator" matches both BUILTIN\Administrator as welll as DOMAIN\Administrator for example, we better match the latter as well.
Can you test what windows does if multiple users with the same unqualified name exist from different domains ?
For example, if a Windows client encounters a non-qualified name (such as RandomGroup) in the GPO policy settings, the client doesn't qualify it with the client's domain (RandomGroup@domain) in order to determine a match. If it used that strategy, and RandomGroup@domain didn't appear among the user's memberships, it would simply ignore that group and process the other users/groups in the policy settings. However, that's not what a Windows client does. Rather, the Windows client consults its SAM database for a match against RandomGroup. If it doesn't find a match, it denies access and logs an error (and stops processing any other users/groups). So, the actions are quite different depending on the strategy used (ignore group vs. deny access).
So you tested that unqualified names are only matched against the BUILTIN database ?
For sssd, if we think it makes sense to match against RandomGroup@domain when we encounter RandomGroup in policy settings, we can certainly do that. If we think we should always deny access when we encounter non-qualified names, we can also do that. However, I think we should apply whatever strategy we decide upon consistently, regardless of whether the non-qualified name is on an allow list or a deny list.
I fear we'll have to at least whitelist things like BUILTIN\Users or BUILTIN\Guests or we may get denials when policies that reference those special groups are encountered and that should not happen. However I would like to see some testing that confirm what a Windows client actually do, so we know for certain and can decide based on the actual behavior.
Simo.
----- Original Message -----
On Wed, 2014-08-06 at 23:59 -0400, Yassir Elley wrote:
I think a non-qualified name can be thought of as either a built-in group (such as "Administrators") or as a local group (such as "RandomGroup") which happens to have the same name on multiple clients (a la windows workgroups). Since sssd supports neither built-in groups nor local groups, we aren't going to be able to do exactly what a windows client would do. We should do whatever makes sense in an sssd context.
Yes but we also need to match Windows administrators expectations and GPO general behavior. If "Administrator" matches both BUILTIN\Administrator as welll as DOMAIN\Administrator for example, we better match the latter as well.
Can you test what windows does if multiple users with the same unqualified name exist from different domains ?
For example, if a Windows client encounters a non-qualified name (such as RandomGroup) in the GPO policy settings, the client doesn't qualify it with the client's domain (RandomGroup@domain) in order to determine a match. If it used that strategy, and RandomGroup@domain didn't appear among the user's memberships, it would simply ignore that group and process the other users/groups in the policy settings. However, that's not what a Windows client does. Rather, the Windows client consults its SAM database for a match against RandomGroup. If it doesn't find a match, it denies access and logs an error (and stops processing any other users/groups). So, the actions are quite different depending on the strategy used (ignore group vs. deny access).
So you tested that unqualified names are only matched against the BUILTIN database ?
Yes. The results of my testing indicate that unqualified names are only matched against the BUILTIN database. I tested this by performing the following steps: * I created two users: BUILTIN\RandomUser and DOMAIN\RandomUser. * I placed the unqualified name "RandomUser" on an Allow Logon Locally GPO (which I made sure would be processed). The semantics of this GPO are that no one is allowed to perform a console logon except for "RandomUser" * When I tried to logon as DOMAIN\RandomUser, it failed with the message "You cannot log on because the logon method you are using is not allowed on this computer." This is the message displayed when a user is denied access b/c of GPO Logon Rights.
For sssd, if we think it makes sense to match against RandomGroup@domain when we encounter RandomGroup in policy settings, we can certainly do that. If we think we should always deny access when we encounter non-qualified names, we can also do that. However, I think we should apply whatever strategy we decide upon consistently, regardless of whether the non-qualified name is on an allow list or a deny list.
I fear we'll have to at least whitelist things like BUILTIN\Users or BUILTIN\Guests or we may get denials when policies that reference those special groups are encountered and that should not happen.
I think that whatever we decide to do for BUILTIN groups, we should also do for well-known SIDS. Currently, we never match well-known SIDs, which means that if the AD admin has specified that only the well-known SID for BUILTIN\Users should be allowed access, we never match against that SID (b/c it doesn't exist in our cache) and we deny access. Similarly, if the AD admin has specified that the well-known SID for BUILTIN\Users should be denied access, we never match against that SID and we allow access.
However I would like to see some testing that confirm what a Windows client actually do, so we know for certain and can decide based on the actual behavior.
Simo.
There seem to be a few issues here: 1) What should the gpo code do if it encounters a well-known SID or well-known unqualified name of a BUILTIN group? 2) What should the gpo code do if it encounters an unqualified name that is not well-known? 3) Should the behavior in 1 and 2 be different depending on whether the name is encountered in an Allow list or a Deny list?
Regards, Yassir.
On Sat, 2014-08-09 at 23:14 -0400, Yassir Elley wrote:
----- Original Message -----
On Wed, 2014-08-06 at 23:59 -0400, Yassir Elley wrote:
I think a non-qualified name can be thought of as either a built-in group (such as "Administrators") or as a local group (such as "RandomGroup") which happens to have the same name on multiple clients (a la windows workgroups). Since sssd supports neither built-in groups nor local groups, we aren't going to be able to do exactly what a windows client would do. We should do whatever makes sense in an sssd context.
Yes but we also need to match Windows administrators expectations and GPO general behavior. If "Administrator" matches both BUILTIN\Administrator as welll as DOMAIN\Administrator for example, we better match the latter as well.
Can you test what windows does if multiple users with the same unqualified name exist from different domains ?
For example, if a Windows client encounters a non-qualified name (such as RandomGroup) in the GPO policy settings, the client doesn't qualify it with the client's domain (RandomGroup@domain) in order to determine a match. If it used that strategy, and RandomGroup@domain didn't appear among the user's memberships, it would simply ignore that group and process the other users/groups in the policy settings. However, that's not what a Windows client does. Rather, the Windows client consults its SAM database for a match against RandomGroup. If it doesn't find a match, it denies access and logs an error (and stops processing any other users/groups). So, the actions are quite different depending on the strategy used (ignore group vs. deny access).
So you tested that unqualified names are only matched against the BUILTIN database ?
Yes. The results of my testing indicate that unqualified names are only matched against the BUILTIN database. I tested this by performing the following steps:
- I created two users: BUILTIN\RandomUser and DOMAIN\RandomUser.
- I placed the unqualified name "RandomUser" on an Allow Logon Locally
GPO (which I made sure would be processed). The semantics of this GPO are that no one is allowed to perform a console logon except for "RandomUser"
- When I tried to logon as DOMAIN\RandomUser, it failed with the
message "You cannot log on because the logon method you are using is not allowed on this computer." This is the message displayed when a user is denied access b/c of GPO Logon Rights.
Ok, so this is just to match BUILTIN's which makes sense as you could have a LocalUser on each machine and on each one it would have potentially a different local sid.
For sssd, if we think it makes sense to match against RandomGroup@domain when we encounter RandomGroup in policy settings, we can certainly do that. If we think we should always deny access when we encounter non-qualified names, we can also do that. However, I think we should apply whatever strategy we decide upon consistently, regardless of whether the non-qualified name is on an allow list or a deny list.
I fear we'll have to at least whitelist things like BUILTIN\Users or BUILTIN\Guests or we may get denials when policies that reference those special groups are encountered and that should not happen.
I think that whatever we decide to do for BUILTIN groups, we should also do for well-known SIDS. Currently, we never match well-known SIDs, which means that if the AD admin has specified that only the well-known SID for BUILTIN\Users should be allowed access, we never match against that SID (b/c it doesn't exist in our cache) and we deny access. Similarly, if the AD admin has specified that the well-known SID for BUILTIN\Users should be denied access, we never match against that SID and we allow access.
We should probably treat local posix users as BUILTIN.
However I would like to see some testing that confirm what a Windows client actually do, so we know for certain and can decide based on the actual behavior.
Simo.
There seem to be a few issues here:
- What should the gpo code do if it encounters a well-known SID or
well-known unqualified name of a BUILTIN group?
I think we should match them against user/groups in /etc/passwd|group
We may also want to map well known BUILTIN groups to well known local posix groups anyway at some point.
- What should the gpo code do if it encounters an unqualified name
that is not well-known?
As above, match against local groups ?
- Should the behavior in 1 and 2 be different depending on whether
the name is encountered in an Allow list or a Deny list?
Different how ?
Simo.
----- Original Message -----
On Sat, 2014-08-09 at 23:14 -0400, Yassir Elley wrote:
----- Original Message -----
On Wed, 2014-08-06 at 23:59 -0400, Yassir Elley wrote:
I think a non-qualified name can be thought of as either a built-in group (such as "Administrators") or as a local group (such as "RandomGroup") which happens to have the same name on multiple clients (a la windows workgroups). Since sssd supports neither built-in groups nor local groups, we aren't going to be able to do exactly what a windows client would do. We should do whatever makes sense in an sssd context.
Yes but we also need to match Windows administrators expectations and GPO general behavior. If "Administrator" matches both BUILTIN\Administrator as welll as DOMAIN\Administrator for example, we better match the latter as well.
Can you test what windows does if multiple users with the same unqualified name exist from different domains ?
For example, if a Windows client encounters a non-qualified name (such as RandomGroup) in the GPO policy settings, the client doesn't qualify it with the client's domain (RandomGroup@domain) in order to determine a match. If it used that strategy, and RandomGroup@domain didn't appear among the user's memberships, it would simply ignore that group and process the other users/groups in the policy settings. However, that's not what a Windows client does. Rather, the Windows client consults its SAM database for a match against RandomGroup. If it doesn't find a match, it denies access and logs an error (and stops processing any other users/groups). So, the actions are quite different depending on the strategy used (ignore group vs. deny access).
So you tested that unqualified names are only matched against the BUILTIN database ?
Yes. The results of my testing indicate that unqualified names are only matched against the BUILTIN database. I tested this by performing the following steps:
- I created two users: BUILTIN\RandomUser and DOMAIN\RandomUser.
- I placed the unqualified name "RandomUser" on an Allow Logon Locally
GPO (which I made sure would be processed). The semantics of this GPO are that no one is allowed to perform a console logon except for "RandomUser"
- When I tried to logon as DOMAIN\RandomUser, it failed with the
message "You cannot log on because the logon method you are using is not allowed on this computer." This is the message displayed when a user is denied access b/c of GPO Logon Rights.
Ok, so this is just to match BUILTIN's which makes sense as you could have a LocalUser on each machine and on each one it would have potentially a different local sid.
For sssd, if we think it makes sense to match against RandomGroup@domain when we encounter RandomGroup in policy settings, we can certainly do that. If we think we should always deny access when we encounter non-qualified names, we can also do that. However, I think we should apply whatever strategy we decide upon consistently, regardless of whether the non-qualified name is on an allow list or a deny list.
I fear we'll have to at least whitelist things like BUILTIN\Users or BUILTIN\Guests or we may get denials when policies that reference those special groups are encountered and that should not happen.
I think that whatever we decide to do for BUILTIN groups, we should also do for well-known SIDS. Currently, we never match well-known SIDs, which means that if the AD admin has specified that only the well-known SID for BUILTIN\Users should be allowed access, we never match against that SID (b/c it doesn't exist in our cache) and we deny access. Similarly, if the AD admin has specified that the well-known SID for BUILTIN\Users should be denied access, we never match against that SID and we allow access.
We should probably treat local posix users as BUILTIN.
Conceptually, I agree that non-qualified names should be matched against local posix users and groups (since this is exactly what Windows clients do). However, I don't think sssd currently manages local users and groups. I know this was discussed at some point, but I seem to recall it was not trivial, as it required coordination with some other packages. Has local user/group support already been added to sssd?
However I would like to see some testing that confirm what a Windows client actually do, so we know for certain and can decide based on the actual behavior.
Simo.
There seem to be a few issues here:
- What should the gpo code do if it encounters a well-known SID or
well-known unqualified name of a BUILTIN group?
I think we should match them against user/groups in /etc/passwd|group
We may also want to map well known BUILTIN groups to well known local posix groups anyway at some point.
If support for local users/groups already exists in sssd, I agree. However, in the absence of that, as you suggested, we may still want to match against certain well-known SIDs or unqualified names (such as BUILTIN\Users and BUILTIN\Guests). Presumably, if the special groups appear in the Deny List, they should similarly always match (meaning we would always deny access if BUILTIN\Users was in the Deny List).
- What should the gpo code do if it encounters an unqualified name
that is not well-known?
As above, match against local groups ?
Again, if support for local users/groups already exists in sssd, I agree. However, in the absence of that, we need to decide whether to always match such unqualified names (that are not well-known, such as RandomUser), or to never match them. Since an AD admin should know that placing RandomUser on a GPO Allow/Deny list is highly deprecated, and that there is no guarantee that all Windows clients in the domain will match it, I think we should take advantage of this fact, and never match RandomUser, which is fully compliant b/c Windows clients in a German locale would similarly never match them.
- Should the behavior in 1 and 2 be different depending on whether
the name is encountered in an Allow list or a Deny list?
Different how ?
At the beginning of this thread, Jakub had suggested ignoring unqualified names if they appeared on the Allow list, but denying access if unqualified names appear on the Deny List. I guess this is somewhat equivalent to always matching unqualified names, although not exactly, b/c if RandomUser was the only entry on the Allow list, and we ignored it, then we would be denied access (whereas if we always matched it, we would be granted access).
Regards, Yassir.
----- Original Message -----
----- Original Message -----
On Sat, 2014-08-09 at 23:14 -0400, Yassir Elley wrote:
----- Original Message -----
On Wed, 2014-08-06 at 23:59 -0400, Yassir Elley wrote:
I think a non-qualified name can be thought of as either a built-in group (such as "Administrators") or as a local group (such as "RandomGroup") which happens to have the same name on multiple clients (a la windows workgroups). Since sssd supports neither built-in groups nor local groups, we aren't going to be able to do exactly what a windows client would do. We should do whatever makes sense in an sssd context.
Yes but we also need to match Windows administrators expectations and GPO general behavior. If "Administrator" matches both BUILTIN\Administrator as welll as DOMAIN\Administrator for example, we better match the latter as well.
Can you test what windows does if multiple users with the same unqualified name exist from different domains ?
For example, if a Windows client encounters a non-qualified name (such as RandomGroup) in the GPO policy settings, the client doesn't qualify it with the client's domain (RandomGroup@domain) in order to determine a match. If it used that strategy, and RandomGroup@domain didn't appear among the user's memberships, it would simply ignore that group and process the other users/groups in the policy settings. However, that's not what a Windows client does. Rather, the Windows client consults its SAM database for a match against RandomGroup. If it doesn't find a match, it denies access and logs an error (and stops processing any other users/groups). So, the actions are quite different depending on the strategy used (ignore group vs. deny access).
So you tested that unqualified names are only matched against the BUILTIN database ?
Yes. The results of my testing indicate that unqualified names are only matched against the BUILTIN database. I tested this by performing the following steps:
- I created two users: BUILTIN\RandomUser and DOMAIN\RandomUser.
- I placed the unqualified name "RandomUser" on an Allow Logon Locally
GPO (which I made sure would be processed). The semantics of this GPO are that no one is allowed to perform a console logon except for "RandomUser"
- When I tried to logon as DOMAIN\RandomUser, it failed with the
message "You cannot log on because the logon method you are using is not allowed on this computer." This is the message displayed when a user is denied access b/c of GPO Logon Rights.
Ok, so this is just to match BUILTIN's which makes sense as you could have a LocalUser on each machine and on each one it would have potentially a different local sid.
For sssd, if we think it makes sense to match against RandomGroup@domain when we encounter RandomGroup in policy settings, we can certainly do that. If we think we should always deny access when we encounter non-qualified names, we can also do that. However, I think we should apply whatever strategy we decide upon consistently, regardless of whether the non-qualified name is on an allow list or a deny list.
I fear we'll have to at least whitelist things like BUILTIN\Users or BUILTIN\Guests or we may get denials when policies that reference those special groups are encountered and that should not happen.
I think that whatever we decide to do for BUILTIN groups, we should also do for well-known SIDS. Currently, we never match well-known SIDs, which means that if the AD admin has specified that only the well-known SID for BUILTIN\Users should be allowed access, we never match against that SID (b/c it doesn't exist in our cache) and we deny access. Similarly, if the AD admin has specified that the well-known SID for BUILTIN\Users should be denied access, we never match against that SID and we allow access.
We should probably treat local posix users as BUILTIN.
Conceptually, I agree that non-qualified names should be matched against local posix users and groups (since this is exactly what Windows clients do). However, I don't think sssd currently manages local users and groups. I know this was discussed at some point, but I seem to recall it was not trivial, as it required coordination with some other packages. Has local user/group support already been added to sssd?
However I would like to see some testing that confirm what a Windows client actually do, so we know for certain and can decide based on the actual behavior.
Simo.
There seem to be a few issues here:
- What should the gpo code do if it encounters a well-known SID or
well-known unqualified name of a BUILTIN group?
I think we should match them against user/groups in /etc/passwd|group
We may also want to map well known BUILTIN groups to well known local posix groups anyway at some point.
If support for local users/groups already exists in sssd, I agree. However, in the absence of that, as you suggested, we may still want to match against certain well-known SIDs or unqualified names (such as BUILTIN\Users and BUILTIN\Guests). Presumably, if the special groups appear in the Deny List, they should similarly always match (meaning we would always deny access if BUILTIN\Users was in the Deny List).
- What should the gpo code do if it encounters an unqualified name
that is not well-known?
As above, match against local groups ?
Again, if support for local users/groups already exists in sssd, I agree. However, in the absence of that, we need to decide whether to always match such unqualified names (that are not well-known, such as RandomUser), or to never match them. Since an AD admin should know that placing RandomUser on a GPO Allow/Deny list is highly deprecated, and that there is no guarantee that all Windows clients in the domain will match it, I think we should take advantage of this fact, and never match RandomUser, which is fully compliant b/c Windows clients in a German locale would similarly never match them.
- Should the behavior in 1 and 2 be different depending on whether
the name is encountered in an Allow list or a Deny list?
Different how ?
At the beginning of this thread, Jakub had suggested ignoring unqualified names if they appeared on the Allow list, but denying access if unqualified names appear on the Deny List. I guess this is somewhat equivalent to always matching unqualified names, although not exactly, b/c if RandomUser was the only entry on the Allow list, and we ignored it, then we would be denied access (whereas if we always matched it, we would be granted access).
Regards, Yassir. _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
We haven't had any activity on this thread for some time, so let me summarize the discussion as I see it.
Issue: In the common case, the whitelists and blacklists in GptTmpl.inf are specified in terms of sids. Using GPME, if the Windows admin "browses" to add a user to a list, or if he manually enters a fully-domain-qualified name, then GPME converts those into sids before writing them to the inf file. However, if the admin manually enters an unqualified name, then that name string is written to the inf file.
Proposal 1: Simo suggested that we match the unqualified name against users/groups in /etc/passwd|group.
Concern: Conceptually, I agree that non-qualified names should be matched against local posix users and groups (since this is exactly what Windows clients do). However, I don't think sssd currently manages local users and groups. I know this was discussed at some point, but I seem to recall it was not trivial, as it required coordination with some other packages. Has local user/group support already been added to sssd?
Proposal 2: If local user/group support does not exist in sssd, I propose that we adopt a policy of either always matching or never matching against such unqualified names (although we may want to always match certain common group names, such as Users and Guests).
Regards, Yassir.
On (22/07/14 03:13), Yassir Elley wrote:
On Sun, Jul 20, 2014 at 05:48:45PM -0400, Yassir Elley wrote:
Hi,
The attached patch adds support for gpo version checking and for conditional downloading of policy files. In order to get that to work, this patch also replaces the deprecated smb functions (e.g. smbc_open, smbc_read, etc) I had been using (not knowing they were deprecated) with the newer smb functions, such as "smbc_getFunctionOpen(smbc_ctx)(smbc_ctx, smb_uri, O_RDONLY, 0755);".
Before this patch, the policy files were being unconditionally downloaded every time. After this patch, the backend sends the cached_gpt_version (from a previous transaction, if any) to the gpo_child. The gpo_child downloads the per-GPO GPT.INI file, from which it extracts the sysvol_gpt_version. Only if the sysvol_gpt_version is greater than the cached_gpt_version (or if there is no cached_gpt_version) does the gpo_child download the policy files. The gpo_child returns the sysvol_gpt_version to the backend, which stores it in the cache.
Note that, while a GPO has both a SYSVOL-stored Group Policy Template (gpt) version, as well as an LDAP-stored Group Policy Container (gpc) version (which should be equal), it is best practice to use the gpt version for version checking b/c SYSVOL replication is typically much faster than AD replication. In other words, the gpt version is quickly up-to-date on all DCs for a given domain, while the gpc version may be stale for some time after a change is made.
In order to keep the gpo_child as simple as possible, the gpo_child does not interact with the sysdb cache at all (only the backend does). However, the gpo_child does need to parse the GPT.INI file in order to determine whether to download the policy files. As such, the gpo_child includes GPT.INI file parsing functionality (which is per-GPO, and not CSE-specific).
Note that this patch does not add support for offline mode, which will be implemented in a subsequent patch.
Regards, Yassir.
Hi Jakub,
I agree with all of your comments so far and have attached revised patches to this email. I split the original patch into two patches, as you suggested, and I addressed your review comments in the revised patches. Thanks for the review! Keep the comments coming! :)
Regards, Yassir.
From f3f00a78102ad5e389266b28c43d0396caf57592 Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Tue, 22 Jul 2014 03:03:03 -0400 Subject: [PATCH 2/2] AD-GPO: only download policy files if gpo version changes
src/providers/ad/ad_gpo.c | 306 +++++++++++++++++++----------- src/providers/ad/ad_gpo_child.c | 404 ++++++++++++++++++++++++++++++++-------- 2 files changed, 517 insertions(+), 193 deletions(-)
I have few comments to the ad_gpo_child
diff --git a/src/providers/ad/ad_gpo_child.c b/src/providers/ad/ad_gpo_child.c index 22b8b52046b6298f2eeaf425cef7bf0271865fe2..93ae592e591eefe812212cc99ddbec70c4e0aba9 100644 --- a/src/providers/ad/ad_gpo_child.c +++ b/src/providers/ad/ad_gpo_child.c @@ -27,6 +27,7 @@ #include <sys/stat.h> #include <popt.h> #include <libsmbclient.h> +#include <ini_configobj.h> #include <security/pam_modules.h>
#include "util/util.h" @@ -34,14 +35,17 @@ #include "providers/dp_backend.h" #include "sss_cli.h"
-#define RIGHTS_SECTION "Privilege Rights" -#define ALLOW_LOGON_LOCALLY "SeInteractiveLogonRight" -#define DENY_LOGON_LOCALLY "SeDenyInteractiveLogonRight" #define SMB_BUFFER_SIZE 65536 +#define GPT_INI "/GPT.INI" +#define INI_GENERAL_SECTION "General" +#define GPT_INI_VERSION "Version"
struct input_buffer {
- const char *smb_uri;
- const char *unix_path;
- int cached_gpt_version;
- const char *smb_server;
- const char *smb_share;
- const char *smb_path;
- const char *smb_cse_suffix;
};
static errno_t @@ -51,34 +55,62 @@ unpack_buffer(uint8_t *buf, { size_t p = 0; uint32_t len;
- uint32_t cached_gpt_version;
- /* smb_uri size and length */
- SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
- DEBUG(SSSDBG_TRACE_ALL, "smb_uri size: %d\n", len);
/* cached_gpt_version */
SAFEALIGN_COPY_UINT32_CHECK(&cached_gpt_version, buf + p, size, &p);
DEBUG(SSSDBG_TRACE_FUNC, "cached_gpt_version: %d\n", cached_gpt_version);
ibuf->cached_gpt_version = cached_gpt_version;
/* smb_server */
SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
DEBUG(SSSDBG_TRACE_ALL, "smb_server length: %d\n", len); if (len == 0) { return EINVAL; } else { if ((p + len ) > size) return EINVAL;
ibuf->smb_uri = talloc_strndup(ibuf, (char *)(buf + p), len);if (ibuf->smb_uri == NULL) return ENOMEM;DEBUG(SSSDBG_TRACE_ALL, "got smb_uri: %s\n", ibuf->smb_uri);
ibuf->smb_server = talloc_strndup(ibuf, (char *)(buf + p), len);if (ibuf->smb_server == NULL) return ENOMEM; }DEBUG(SSSDBG_TRACE_ALL, "smb_server: %s\n", ibuf->smb_server); p += len;
- /* unix_path size and length */
- /* smb_share */ SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
- DEBUG(SSSDBG_TRACE_ALL, "smb_share length: %d\n", len);
- if (len == 0) {
return EINVAL;- } else {
if ((p + len ) > size) return EINVAL;ibuf->smb_share = talloc_strndup(ibuf, (char *)(buf + p), len);if (ibuf->smb_share == NULL) return ENOMEM;DEBUG(SSSDBG_TRACE_ALL, "smb_share: %s\n", ibuf->smb_share);p += len;- }
- DEBUG(SSSDBG_TRACE_ALL, "unix_path size: %d\n", len);
/* smb_path */
SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
DEBUG(SSSDBG_TRACE_ALL, "smb_path length: %d\n", len);
if (len == 0) {
return EINVAL;} else {
if ((p + len ) > size) return EINVAL;ibuf->smb_path = talloc_strndup(ibuf, (char *)(buf + p), len);if (ibuf->smb_path == NULL) return ENOMEM;DEBUG(SSSDBG_TRACE_ALL, "smb_path: %s\n", ibuf->smb_path);p += len;}
/* smb_cse_suffix */
SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
DEBUG(SSSDBG_TRACE_ALL, "smb_cse_suffix length: %d\n", len); if (len == 0) { return EINVAL; } else { if ((p + len ) > size) return EINVAL;
ibuf->unix_path = talloc_strndup(ibuf, (char *)(buf + p), len);if (ibuf->unix_path == NULL) return ENOMEM;DEBUG(SSSDBG_TRACE_ALL, "got unix_path: %s\n", ibuf->unix_path);
ibuf->smb_cse_suffix = talloc_strndup(ibuf, (char *)(buf + p), len);if (ibuf->smb_cse_suffix == NULL) return ENOMEM; }DEBUG(SSSDBG_TRACE_ALL, "smb_cse_suffix: %s\n", ibuf->smb_cse_suffix); p += len;@@ -88,14 +120,16 @@ unpack_buffer(uint8_t *buf,
static errno_t pack_buffer(struct response *r,
int sysvol_gpt_version, int result){ size_t p = 0;
/* A buffer with the following structure must be created:
* uint32_t sysvol_gpt_version (required)*/
- uint32_t status of the request (required)
- r->size = sizeof(uint32_t);
r->size = 2 * sizeof(uint32_t);
r->buf = talloc_array(r, uint8_t, r->size); if(r->buf == NULL) {
@@ -104,6 +138,9 @@ pack_buffer(struct response *r,
DEBUG(SSSDBG_TRACE_FUNC, "result [%d]\n", result);
- /* sysvol_gpt_version */
- SAFEALIGN_SET_UINT32(&r->buf[p], sysvol_gpt_version, &p);
- /* result */ SAFEALIGN_SET_UINT32(&r->buf[p], result, &p);
@@ -112,6 +149,7 @@ pack_buffer(struct response *r,
static errno_t prepare_response(TALLOC_CTX *mem_ctx,
int sysvol_gpt_version, int result, struct response **rsp){ @@ -126,14 +164,14 @@ prepare_response(TALLOC_CTX *mem_ctx, r->buf = NULL; r->size = 0;
- ret = pack_buffer(r, result);
ret = pack_buffer(r, sysvol_gpt_version, result); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "pack_buffer failed\n"); return ret; }
*rsp = r;
- DEBUG(SSSDBG_TRACE_FUNC, "r->size: %zu\n", r->size);
- DEBUG(SSSDBG_TRACE_ALL, "r->size: %zu\n", r->size); return EOK;
}
@@ -153,12 +191,12 @@ sssd_krb_get_auth_data_fn(const char * pServer,
/*
- This function prepares the gpo_cache by:
- parsing the input_unix_path into its component directories
- parsing the input_smb_path into its component directories
- creating each component directory (if it doesn't already exist)
*/ static errno_t prepare_gpo_cache(TALLOC_CTX *mem_ctx, const char *cache_dir,
const char *input_unix_path)
const char *input_smb_path_with_suffix){ char *current_dir; char *ptr; @@ -167,25 +205,27 @@ static errno_t prepare_gpo_cache(TALLOC_CTX *mem_ctx, int i; char *first = NULL; char *last = NULL;
- char *unix_path = NULL;
- char *smb_path_with_suffix = NULL;
- unix_path = talloc_strdup(mem_ctx, input_unix_path);
- if (unix_path == NULL) {
smb_path_with_suffix = talloc_strdup(mem_ctx, input_smb_path_with_suffix);
if (smb_path_with_suffix == NULL) { return ENOMEM; }
DEBUG(SSSDBG_TRACE_ALL, "smb_path_with_suffix: %s\n", smb_path_with_suffix);
current_dir = talloc_strdup(mem_ctx, cache_dir); if (current_dir == NULL) { return ENOMEM; }
- ptr = unix_path + 1;
- ptr = smb_path_with_suffix + 1; while ((ptr = strchr(ptr, delim))) { ptr++; num_dirs++; }
- ptr = unix_path + 1;
ptr = smb_path_with_suffix + 1;
for (i = 0; i < num_dirs; i++) { first = ptr;
@@ -210,12 +250,16 @@ static errno_t prepare_gpo_cache(TALLOC_CTX *mem_ctx,
/*
- This function stores the input buf to a local file, whose file path
- is constructed by concatenating GPO_CACHE_PATH to the input unix_path.
- Note that the backend will later read the policy file from the same file path.
- is constructed by concatenating:
- GPO_CACHE_PATH,
- input smb_path,
- input smb_cse_suffix
- Note that the backend will later read the file from the same file path.
*/ -static errno_t store_bytes_in_gpo_cache(const char *unix_path,
uint8_t *buf,int buflen)+static errno_t gpo_cache_store_file(const char *smb_path,
const char *smb_cse_suffix,uint8_t *buf,int buflen){ int ret; int fd = -1; @@ -223,6 +267,7 @@ static errno_t store_bytes_in_gpo_cache(const char *unix_path, ssize_t written; mode_t old_umask; char *filename = NULL;
char *smb_path_with_suffix = NULL; TALLOC_CTX *tmp_ctx = NULL;
tmp_ctx = talloc_new(NULL);
@@ -231,10 +276,18 @@ static errno_t store_bytes_in_gpo_cache(const char *unix_path, goto done; }
- /* create component directories of unix_path, if needed */
- ret = prepare_gpo_cache(tmp_ctx, GPO_CACHE_PATH, unix_path);
- smb_path_with_suffix =
talloc_asprintf(tmp_ctx, "%s%s", smb_path, smb_cse_suffix);- if (smb_path_with_suffix == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");ret = ENOMEM;goto done;- }
- filename = talloc_asprintf(tmp_ctx, GPO_CACHE_PATH"%s", unix_path);
- /* create component directories of smb_path, if needed */
- ret = prepare_gpo_cache(tmp_ctx, GPO_CACHE_PATH, smb_path_with_suffix);
^^^ return code is ignored. It may cause problems if prepare_gpo_cache fail.
- filename = talloc_asprintf(tmp_ctx, GPO_CACHE_PATH"%s", smb_path_with_suffix); tmp_name = talloc_asprintf(tmp_ctx, "%sXXXXXX", filename); if (tmp_name == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
@@ -303,21 +356,132 @@ static errno_t store_bytes_in_gpo_cache(const char *unix_path, return ret; }
+static errno_t +parse_ini_file_with_libini(struct ini_cfgobj *ini_config,
int *_gpt_version)+{
- int ret = 0;
- struct value_obj *vobj = NULL;
- int gpt_version;
- ret = ini_get_config_valueobj(INI_GENERAL_SECTION, GPT_INI_VERSION,
ini_config, INI_GET_FIRST_VALUE, &vobj);- if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,"ini_get_config_valueobj failed [%d][%s]\n", ret, strerror(ret));goto done;- }
- if (vobj == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "section/name not found: [%s][%s]\n",INI_GENERAL_SECTION, GPT_INI_VERSION);ret = EINVAL;goto done;- }
- gpt_version = ini_get_int32_config_value(vobj, 0, -1, &ret);
- if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,"ini_get_int32_config_value failed [%d][%s]\n",ret, strerror(ret));goto done;- }
- *_gpt_version = gpt_version;
- ret = EOK;
- done:
- return ret;
+}
+/*
- This function parses the GPT_INI file stored in the gpo_cache, and uses the
- results to populate the output parameters ...
- */
+static errno_t +ad_gpo_parse_ini_file(const char *smb_path,
int *_gpt_version)+{
- struct ini_cfgfile *file_ctx = NULL;
- struct ini_cfgobj *ini_config = NULL;
- const char *ini_filename;
- int ret;
- int gpt_version;
- TALLOC_CTX *tmp_ctx = NULL;
- tmp_ctx = talloc_new(NULL);
- if (tmp_ctx == NULL) {
ret = ENOMEM;goto done;- }
- ini_filename = talloc_asprintf(tmp_ctx, GPO_CACHE_PATH"%s%s",
smb_path, GPT_INI);
could you test if allocation was successful?
- DEBUG(SSSDBG_TRACE_FUNC, "ini_filename:%s\n", ini_filename);
- ret = ini_config_create(&ini_config);
- if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,"ini_config_create failed [%d][%s]\n", ret, strerror(ret));goto done;- }
- ret = ini_config_file_open(ini_filename, 0, &file_ctx);
- if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,"ini_config_file_open failed [%d][%s]\n", ret, strerror(ret));goto done;- }
- ret = ini_config_parse(file_ctx, INI_STOP_ON_NONE, 0, 0, ini_config);
- if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,"ini_config_parse failed [%d][%s]\n", ret, strerror(ret));goto done;- }
- ret = parse_ini_file_with_libini(ini_config, &gpt_version);
- if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,"parse_ini_file_with_libini failed [%d][%s]\n",ret, strerror(ret));goto done;- }
- *_gpt_version = gpt_version;
- done:
- if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "Error encountered: %d.\n", ret);- }
- ini_config_file_destroy(file_ctx);
- ini_config_destroy(ini_config);
- talloc_free(tmp_ctx);
- return ret;
+}
/*
- This cse-specific function (GP_EXT_GUID_SECURITY) opens an SMB connection,
- retrieves the data referenced by the input smb_uri, and then closes the SMB
- connection. The data is then written to a file in the GPO_CACHE directory,
- to be read by the backend.
- This function uses the input smb uri components to download a sysvol file
- (e.g. INI file, policy file, etc) and store it to the GPO_CACHE directory.
*/ static errno_t -process_security_settings_cse(TALLOC_CTX *mem_ctx,
const char *smb_uri,const char *unix_path)+copy_smb_file_to_gpo_cache(SMBCCTX *smbc_ctx,
const char *smb_server,const char *smb_share,const char *smb_path,const char *smb_cse_suffix){
- SMBCCTX *context;
char *smb_uri = NULL;
SMBCFILE *file; int ret = 0; uint8_t *buf = NULL; int buflen = 0;
TALLOC_CTX *tmp_ctx = NULL;
tmp_ctx = talloc_new(NULL);
@@ -325,59 +489,131 @@ process_security_settings_cse(TALLOC_CTX *mem_ctx, return ENOMEM; }
- DEBUG(SSSDBG_TRACE_ALL, "%s\n", smb_uri);
- DEBUG(SSSDBG_TRACE_ALL, "%s\n", unix_path);
- smb_uri = talloc_asprintf(tmp_ctx, "%s%s%s%s", smb_server,
smb_share, smb_path, smb_cse_suffix);- DEBUG(SSSDBG_TRACE_FUNC, "smb_uri: %s\n", smb_uri);
- context = smbc_new_context();
- if (context == NULL) {
- errno = 0;
- file = smbc_getFunctionOpen(smbc_ctx)(smbc_ctx, smb_uri, O_RDONLY, 0755);
- if (file == NULL) {
ret = errno;DEBUG(SSSDBG_CRIT_FAILURE, "smbc_getFunctionOpen failed [%d][%s]\n",ret, strerror(ret));goto done;- }
- buf = talloc_array(tmp_ctx, uint8_t, SMB_BUFFER_SIZE);
- errno = 0;
- buflen = smbc_getFunctionRead(smbc_ctx)(smbc_ctx, file, buf, SMB_BUFFER_SIZE);
- if(buflen < 0) {
ret = errno;DEBUG(SSSDBG_CRIT_FAILURE, "smbc_getFunctionRead failed [%d][%s]\n",ret, strerror(ret));goto done;- }
- DEBUG(SSSDBG_TRACE_ALL, "smb_buflen: %d\n", buflen);
- ret = gpo_cache_store_file(smb_path, smb_cse_suffix, buf, buflen);
- if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,"gpo_cache_store_file failed [%d][%s]\n",ret, strerror(ret));goto done;- }
- done:
- talloc_free(tmp_ctx);
- return ret;
+}
+/*
- Using its smb_uri components and cached_gpt_version inputs, this function
- does several things:
- it downloads the GPT_INI file to GPO_CACHE
- it parses the sysvol_gpt_version field from the GPT_INI file
- if the sysvol_gpt_version is greater than the cached_gpt_version
- it downloads the policy file to GPO_CACHE
- else
- it doesn't retrieve the policy file
- in this case, the backend will use the existing policy file in GPO_CACHE
- it returns the sysvol_gpt_version in the _sysvol_gpt_version output param
- Note that if the cached_gpt_version sent by the backend is -1 (to indicate
- that no gpt_version has been set in the cache for the corresponding gpo_guid),
- then the parsed sysvol_gpt_version (which must be at least 0) will be greater
- than the cached_gpt_version, thereby triggering a fresh download.
- Note that the backend will later do the following:
- backend will save the the sysvol_gpt_version to sysdb cache
- backend will read the policy file from the GPO_CACHE
- */
+static errno_t +perform_smb_operations(int cached_gpt_version,
const char *smb_server,const char *smb_share,const char *smb_path,const char *smb_cse_suffix,int *_sysvol_gpt_version)+{
- SMBCCTX *smbc_ctx;
- int ret = 0;
I don't think we need to initialise ret to zero. From defensive point of view it would be better to init with EINVAL. I think zero can be removed.
- int sysvol_gpt_version;
- smbc_ctx = smbc_new_context();
- if (smbc_ctx == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Could not allocate new smbc context\n"); ret = ENOMEM; goto done; }
- smbc_setFunctionAuthData(context, sssd_krb_get_auth_data_fn);
- smbc_setOptionUseKerberos(context, 1);
smbc_setFunctionAuthData(smbc_ctx, sssd_krb_get_auth_data_fn);
smbc_setOptionUseKerberos(smbc_ctx, 1);
/* Initialize the context using the previously specified options */
- if (smbc_init_context(context) == NULL) {
- if (smbc_init_context(smbc_ctx) == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Could not initialize smbc context\n"); ret = ENOMEM; goto done; }
- /* Tell the compatibility layer to use this context */
- smbc_set_context(context);
- int remotehandle = smbc_open(smb_uri, O_RDONLY, 0755);
- if (remotehandle < 0) {
DEBUG(SSSDBG_CRIT_FAILURE, "smbc_open failed\n");ret = EPIPE;goto done;- }
- buf = talloc_array(tmp_ctx, uint8_t, SMB_BUFFER_SIZE);
- buflen = smbc_read(remotehandle, buf, SMB_BUFFER_SIZE);
- if(buflen < 0) {
DEBUG(SSSDBG_CRIT_FAILURE, "smbc_read failed\n");ret = EPIPE;goto done;- }
- DEBUG(SSSDBG_CRIT_FAILURE, "buflen: %d\n", buflen);
- smbc_close(remotehandle);
- ret = store_bytes_in_gpo_cache(unix_path, buf, buflen);
- /* download ini file */
- ret = copy_smb_file_to_gpo_cache(smbc_ctx, smb_server, smb_share, smb_path,
if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE,GPT_INI);
"store_bytes_in_gpo_cache failed [%d][%s]\n",
"copy_smb_file_to_gpo_cache failed [%d][%s]\n", ret, strerror(ret)); goto done;}
ret = ad_gpo_parse_ini_file(smb_path, &sysvol_gpt_version);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,"Cannot parse ini file: [%d][%s]\n", ret, strerror(ret));goto done;}
DEBUG(SSSDBG_TRACE_FUNC, "sysvol_gpt_version: %d\n", sysvol_gpt_version);
if (sysvol_gpt_version > cached_gpt_version) {
/* download policy file */ret = copy_smb_file_to_gpo_cache(smbc_ctx, smb_server, smb_share,smb_path, smb_cse_suffix);if (ret != EOK) {DEBUG(SSSDBG_CRIT_FAILURE,"copy_smb_file_to_gpo_cache failed [%d][%s]\n",ret, strerror(ret));goto done;}}
*_sysvol_gpt_version = sysvol_gpt_version;
done:
- smbc_free_context(context, 0);
- talloc_free(tmp_ctx);
- smbc_free_context(smbc_ctx, 0); return ret;
}
@@ -388,6 +624,7 @@ main(int argc, const char *argv[]) poptContext pc; int debug_fd = -1; errno_t ret;
- int sysvol_gpt_version; int result; TALLOC_CTX *main_ctx = NULL; uint8_t *buf = NULL;
@@ -484,18 +721,21 @@ main(int argc, const char *argv[]) goto fail; }
- DEBUG(SSSDBG_TRACE_FUNC, "processing security settings\n");
- DEBUG(SSSDBG_TRACE_FUNC, "performing smb operations\n");
- result = process_security_settings_cse(main_ctx,
ibuf->smb_uri,ibuf->unix_path);
- result = perform_smb_operations(ibuf->cached_gpt_version,
ibuf->smb_server,ibuf->smb_share,ibuf->smb_path,ibuf->smb_cse_suffix, if (result != EOK) { DEBUG(SSSDBG_CRIT_FAILURE,&sysvol_gpt_version);
"process_security_settings_cse failed.[%d][%s].\n",
"perform_smb_operations failed.[%d][%s].\n", result, strerror(result));
missing goto fail. This is a reason of 1st warning. src/providers/ad/ad_gpo_child.c:142:34: error: ‘sysvol_gpt_version’ may be used uninitialized in this function [-Werror=maybe-uninitialized] SAFEALIGN_SET_UINT32(&r->buf[p], sysvol_gpt_version, &p); ^ src/providers/ad/ad_gpo_child.c:627:9: note: ‘sysvol_gpt_version’ was declared here int sysvol_gpt_version;
}
There is also another gcc warning. It is only visible with -O1 or higher. The same warning is with ferao 20/rawhide and rhel6
src/providers/ad/ad_gpo_child.c: In function ‘main’: src/providers/ad/ad_gpo_child.c:142:34: error: ‘gpt_version’ may be used uninitialized in this function [-Werror=maybe-uninitialized] SAFEALIGN_SET_UINT32(&r->buf[p], sysvol_gpt_version, &p); ^ src/providers/ad/ad_gpo_child.c:413:9: note: ‘gpt_version’ was declared here int gpt_version; ^ cc1: all warnings being treated as errors
There are two possibel solutions: a) explicit initialisation to unexpected value. --- a/src/providers/ad/ad_gpo_child.c +++ b/src/providers/ad/ad_gpo_child.c @@ -410,7 +410,7 @@ ad_gpo_parse_ini_file(const char *smb_path, struct ini_cfgobj *ini_config = NULL; const char *ini_filename; int ret; - int gpt_version; + int gpt_version = -1; TALLOC_CTX *tmp_ctx = NULL;
b) disable inlining of parse_ini_file_with_libini. --- a/src/providers/ad/ad_gpo_child.c +++ b/src/providers/ad/ad_gpo_child.c @@ -359,7 +359,7 @@ static errno_t gpo_cache_store_file(const char *smb_path, return ret; }
-static errno_t +errno_t parse_ini_file_with_libini(struct ini_cfgobj *ini_config, int *_gpt_version) {
LS
On Tue, Jul 22, 2014 at 07:32:17PM +0200, Lukas Slebodnik wrote:
On (22/07/14 03:13), Yassir Elley wrote:
On Sun, Jul 20, 2014 at 05:48:45PM -0400, Yassir Elley wrote:
Hi,
The attached patch adds support for gpo version checking and for conditional downloading of policy files. In order to get that to work, this patch also replaces the deprecated smb functions (e.g. smbc_open, smbc_read, etc) I had been using (not knowing they were deprecated) with the newer smb functions, such as "smbc_getFunctionOpen(smbc_ctx)(smbc_ctx, smb_uri, O_RDONLY, 0755);".
Before this patch, the policy files were being unconditionally downloaded every time. After this patch, the backend sends the cached_gpt_version (from a previous transaction, if any) to the gpo_child. The gpo_child downloads the per-GPO GPT.INI file, from which it extracts the sysvol_gpt_version. Only if the sysvol_gpt_version is greater than the cached_gpt_version (or if there is no cached_gpt_version) does the gpo_child download the policy files. The gpo_child returns the sysvol_gpt_version to the backend, which stores it in the cache.
Note that, while a GPO has both a SYSVOL-stored Group Policy Template (gpt) version, as well as an LDAP-stored Group Policy Container (gpc) version (which should be equal), it is best practice to use the gpt version for version checking b/c SYSVOL replication is typically much faster than AD replication. In other words, the gpt version is quickly up-to-date on all DCs for a given domain, while the gpc version may be stale for some time after a change is made.
In order to keep the gpo_child as simple as possible, the gpo_child does not interact with the sysdb cache at all (only the backend does). However, the gpo_child does need to parse the GPT.INI file in order to determine whether to download the policy files. As such, the gpo_child includes GPT.INI file parsing functionality (which is per-GPO, and not CSE-specific).
Note that this patch does not add support for offline mode, which will be implemented in a subsequent patch.
Regards, Yassir.
Hi Jakub,
I agree with all of your comments so far and have attached revised patches to this email. I split the original patch into two patches, as you suggested, and I addressed your review comments in the revised patches. Thanks for the review! Keep the comments coming! :)
Regards, Yassir.
From f3f00a78102ad5e389266b28c43d0396caf57592 Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Tue, 22 Jul 2014 03:03:03 -0400 Subject: [PATCH 2/2] AD-GPO: only download policy files if gpo version changes
src/providers/ad/ad_gpo.c | 306 +++++++++++++++++++----------- src/providers/ad/ad_gpo_child.c | 404 ++++++++++++++++++++++++++++++++-------- 2 files changed, 517 insertions(+), 193 deletions(-)
I have few comments to the ad_gpo_child
Thank you for the second pair of eyes. I agree with your suggestions, see some more comments inline.
[...]
+copy_smb_file_to_gpo_cache(SMBCCTX *smbc_ctx,
const char *smb_server,const char *smb_share,const char *smb_path,const char *smb_cse_suffix){
- SMBCCTX *context;
char *smb_uri = NULL;
SMBCFILE *file; int ret = 0; uint8_t *buf = NULL; int buflen = 0;
TALLOC_CTX *tmp_ctx = NULL;
tmp_ctx = talloc_new(NULL);
@@ -325,59 +489,131 @@ process_security_settings_cse(TALLOC_CTX *mem_ctx, return ENOMEM; }
- DEBUG(SSSDBG_TRACE_ALL, "%s\n", smb_uri);
- DEBUG(SSSDBG_TRACE_ALL, "%s\n", unix_path);
- smb_uri = talloc_asprintf(tmp_ctx, "%s%s%s%s", smb_server,
smb_share, smb_path, smb_cse_suffix);
Unchecked allocation.
- DEBUG(SSSDBG_TRACE_FUNC, "smb_uri: %s\n", smb_uri);
- context = smbc_new_context();
- if (context == NULL) {
- errno = 0;
- file = smbc_getFunctionOpen(smbc_ctx)(smbc_ctx, smb_uri, O_RDONLY, 0755);
- if (file == NULL) {
ret = errno;DEBUG(SSSDBG_CRIT_FAILURE, "smbc_getFunctionOpen failed [%d][%s]\n",ret, strerror(ret));goto done;- }
- buf = talloc_array(tmp_ctx, uint8_t, SMB_BUFFER_SIZE);
Another unchecked allocation.
- errno = 0;
- buflen = smbc_getFunctionRead(smbc_ctx)(smbc_ctx, file, buf, SMB_BUFFER_SIZE);
- if(buflen < 0) {
While you're changing the code, can you also put a space after if here? (I know, total nitpick..)
ret = errno;DEBUG(SSSDBG_CRIT_FAILURE, "smbc_getFunctionRead failed [%d][%s]\n",ret, strerror(ret));goto done;- }
- DEBUG(SSSDBG_TRACE_ALL, "smb_buflen: %d\n", buflen);
- ret = gpo_cache_store_file(smb_path, smb_cse_suffix, buf, buflen);
General question here -- what happens if two users whose GPO policies reference the same files log in simultaneously? Do we clobber the files?
- if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,"gpo_cache_store_file failed [%d][%s]\n",ret, strerror(ret));goto done;- }
- done:
- talloc_free(tmp_ctx);
- return ret;
+}
+/*
- Using its smb_uri components and cached_gpt_version inputs, this function
- does several things:
- it downloads the GPT_INI file to GPO_CACHE
- it parses the sysvol_gpt_version field from the GPT_INI file
- if the sysvol_gpt_version is greater than the cached_gpt_version
- it downloads the policy file to GPO_CACHE
- else
- it doesn't retrieve the policy file
- in this case, the backend will use the existing policy file in GPO_CACHE
- it returns the sysvol_gpt_version in the _sysvol_gpt_version output param
- Note that if the cached_gpt_version sent by the backend is -1 (to indicate
- that no gpt_version has been set in the cache for the corresponding gpo_guid),
- then the parsed sysvol_gpt_version (which must be at least 0) will be greater
- than the cached_gpt_version, thereby triggering a fresh download.
- Note that the backend will later do the following:
- backend will save the the sysvol_gpt_version to sysdb cache
- backend will read the policy file from the GPO_CACHE
- */
+static errno_t +perform_smb_operations(int cached_gpt_version,
const char *smb_server,const char *smb_share,const char *smb_path,const char *smb_cse_suffix,int *_sysvol_gpt_version)+{
- SMBCCTX *smbc_ctx;
- int ret = 0;
I don't think we need to initialise ret to zero. From defensive point of view it would be better to init with EINVAL. I think zero can be removed.
I think it's better to set ret to zero (or EOK) prior to entering the done label from the main control flow, like this:
static int fn(ctx) { ret = do_stuff(ctx); if (ret != EOK) { goto done; }
ret = EOK; done: return ret; }
- int sysvol_gpt_version;
- smbc_ctx = smbc_new_context();
- if (smbc_ctx == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Could not allocate new smbc context\n"); ret = ENOMEM; goto done; }
- smbc_setFunctionAuthData(context, sssd_krb_get_auth_data_fn);
- smbc_setOptionUseKerberos(context, 1);
smbc_setFunctionAuthData(smbc_ctx, sssd_krb_get_auth_data_fn);
smbc_setOptionUseKerberos(smbc_ctx, 1);
/* Initialize the context using the previously specified options */
- if (smbc_init_context(context) == NULL) {
- if (smbc_init_context(smbc_ctx) == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Could not initialize smbc context\n"); ret = ENOMEM; goto done; }
- /* Tell the compatibility layer to use this context */
- smbc_set_context(context);
- int remotehandle = smbc_open(smb_uri, O_RDONLY, 0755);
- if (remotehandle < 0) {
DEBUG(SSSDBG_CRIT_FAILURE, "smbc_open failed\n");ret = EPIPE;goto done;- }
- buf = talloc_array(tmp_ctx, uint8_t, SMB_BUFFER_SIZE);
- buflen = smbc_read(remotehandle, buf, SMB_BUFFER_SIZE);
- if(buflen < 0) {
DEBUG(SSSDBG_CRIT_FAILURE, "smbc_read failed\n");ret = EPIPE;goto done;- }
- DEBUG(SSSDBG_CRIT_FAILURE, "buflen: %d\n", buflen);
- smbc_close(remotehandle);
- ret = store_bytes_in_gpo_cache(unix_path, buf, buflen);
- /* download ini file */
- ret = copy_smb_file_to_gpo_cache(smbc_ctx, smb_server, smb_share, smb_path,
if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE,GPT_INI);
"store_bytes_in_gpo_cache failed [%d][%s]\n",
"copy_smb_file_to_gpo_cache failed [%d][%s]\n", ret, strerror(ret)); goto done;}
ret = ad_gpo_parse_ini_file(smb_path, &sysvol_gpt_version);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,"Cannot parse ini file: [%d][%s]\n", ret, strerror(ret));goto done;}
DEBUG(SSSDBG_TRACE_FUNC, "sysvol_gpt_version: %d\n", sysvol_gpt_version);
if (sysvol_gpt_version > cached_gpt_version) {
/* download policy file */ret = copy_smb_file_to_gpo_cache(smbc_ctx, smb_server, smb_share,smb_path, smb_cse_suffix);if (ret != EOK) {DEBUG(SSSDBG_CRIT_FAILURE,"copy_smb_file_to_gpo_cache failed [%d][%s]\n",ret, strerror(ret));goto done;}}
*_sysvol_gpt_version = sysvol_gpt_version;
done:
- smbc_free_context(context, 0);
- talloc_free(tmp_ctx);
- smbc_free_context(smbc_ctx, 0); return ret;
}
@@ -388,6 +624,7 @@ main(int argc, const char *argv[]) poptContext pc; int debug_fd = -1; errno_t ret;
- int sysvol_gpt_version; int result; TALLOC_CTX *main_ctx = NULL; uint8_t *buf = NULL;
@@ -484,18 +721,21 @@ main(int argc, const char *argv[]) goto fail; }
- DEBUG(SSSDBG_TRACE_FUNC, "processing security settings\n");
- DEBUG(SSSDBG_TRACE_FUNC, "performing smb operations\n");
- result = process_security_settings_cse(main_ctx,
ibuf->smb_uri,ibuf->unix_path);
- result = perform_smb_operations(ibuf->cached_gpt_version,
ibuf->smb_server,ibuf->smb_share,ibuf->smb_path,ibuf->smb_cse_suffix, if (result != EOK) { DEBUG(SSSDBG_CRIT_FAILURE,&sysvol_gpt_version);
"process_security_settings_cse failed.[%d][%s].\n",
"perform_smb_operations failed.[%d][%s].\n", result, strerror(result));missing goto fail.This is a reason of 1st warning. src/providers/ad/ad_gpo_child.c:142:34: error: ‘sysvol_gpt_version’ may be used uninitialized in this function [-Werror=maybe-uninitialized] SAFEALIGN_SET_UINT32(&r->buf[p], sysvol_gpt_version, &p); ^ src/providers/ad/ad_gpo_child.c:627:9: note: ‘sysvol_gpt_version’ was declared here int sysvol_gpt_version;
}There is also another gcc warning. It is only visible with -O1 or higher. The same warning is with ferao 20/rawhide and rhel6
Nice catch, I only tested with clang and -O0.
src/providers/ad/ad_gpo_child.c: In function ‘main’: src/providers/ad/ad_gpo_child.c:142:34: error: ‘gpt_version’ may be used uninitialized in this function [-Werror=maybe-uninitialized] SAFEALIGN_SET_UINT32(&r->buf[p], sysvol_gpt_version, &p); ^ src/providers/ad/ad_gpo_child.c:413:9: note: ‘gpt_version’ was declared here int gpt_version; ^ cc1: all warnings being treated as errors
There are two possibel solutions: a) explicit initialisation to unexpected value. --- a/src/providers/ad/ad_gpo_child.c +++ b/src/providers/ad/ad_gpo_child.c @@ -410,7 +410,7 @@ ad_gpo_parse_ini_file(const char *smb_path, struct ini_cfgobj *ini_config = NULL; const char *ini_filename; int ret;
- int gpt_version;
- int gpt_version = -1; TALLOC_CTX *tmp_ctx = NULL;
b) disable inlining of parse_ini_file_with_libini. --- a/src/providers/ad/ad_gpo_child.c +++ b/src/providers/ad/ad_gpo_child.c @@ -359,7 +359,7 @@ static errno_t gpo_cache_store_file(const char *smb_path, return ret; }
-static errno_t +errno_t parse_ini_file_with_libini(struct ini_cfgobj *ini_config, int *_gpt_version) {
LS
I personally prefer a) here.
----- Original Message -----
On Tue, Jul 22, 2014 at 07:32:17PM +0200, Lukas Slebodnik wrote:
On (22/07/14 03:13), Yassir Elley wrote:
On Sun, Jul 20, 2014 at 05:48:45PM -0400, Yassir Elley wrote:
Hi,
The attached patch adds support for gpo version checking and for conditional downloading of policy files. In order to get that to work, this patch also replaces the deprecated smb functions (e.g. smbc_open, smbc_read, etc) I had been using (not knowing they were deprecated) with the newer smb functions, such as "smbc_getFunctionOpen(smbc_ctx)(smbc_ctx, smb_uri, O_RDONLY, 0755);".
Before this patch, the policy files were being unconditionally downloaded every time. After this patch, the backend sends the cached_gpt_version (from a previous transaction, if any) to the gpo_child. The gpo_child downloads the per-GPO GPT.INI file, from which it extracts the sysvol_gpt_version. Only if the sysvol_gpt_version is greater than the cached_gpt_version (or if there is no cached_gpt_version) does the gpo_child download the policy files. The gpo_child returns the sysvol_gpt_version to the backend, which stores it in the cache.
Note that, while a GPO has both a SYSVOL-stored Group Policy Template (gpt) version, as well as an LDAP-stored Group Policy Container (gpc) version (which should be equal), it is best practice to use the gpt version for version checking b/c SYSVOL replication is typically much faster than AD replication. In other words, the gpt version is quickly up-to-date on all DCs for a given domain, while the gpc version may be stale for some time after a change is made.
In order to keep the gpo_child as simple as possible, the gpo_child does not interact with the sysdb cache at all (only the backend does). However, the gpo_child does need to parse the GPT.INI file in order to determine whether to download the policy files. As such, the gpo_child includes GPT.INI file parsing functionality (which is per-GPO, and not CSE-specific).
Note that this patch does not add support for offline mode, which will be implemented in a subsequent patch.
Regards, Yassir.
Hi Jakub,
I agree with all of your comments so far and have attached revised patches to this email. I split the original patch into two patches, as you suggested, and I addressed your review comments in the revised patches. Thanks for the review! Keep the comments coming! :)
Regards, Yassir.
From f3f00a78102ad5e389266b28c43d0396caf57592 Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Tue, 22 Jul 2014 03:03:03 -0400 Subject: [PATCH 2/2] AD-GPO: only download policy files if gpo version changes
src/providers/ad/ad_gpo.c | 306 +++++++++++++++++++----------- src/providers/ad/ad_gpo_child.c | 404 ++++++++++++++++++++++++++++++++-------- 2 files changed, 517 insertions(+), 193 deletions(-)
I have few comments to the ad_gpo_child
Thank you for the second pair of eyes. I agree with your suggestions, see some more comments inline.
[...]
+copy_smb_file_to_gpo_cache(SMBCCTX *smbc_ctx,
const char *smb_server,const char *smb_share,const char *smb_path,const char *smb_cse_suffix){
- SMBCCTX *context;
char *smb_uri = NULL;
SMBCFILE *file; int ret = 0; uint8_t *buf = NULL; int buflen = 0;
TALLOC_CTX *tmp_ctx = NULL;
tmp_ctx = talloc_new(NULL);
@@ -325,59 +489,131 @@ process_security_settings_cse(TALLOC_CTX *mem_ctx, return ENOMEM; }
- DEBUG(SSSDBG_TRACE_ALL, "%s\n", smb_uri);
- DEBUG(SSSDBG_TRACE_ALL, "%s\n", unix_path);
- smb_uri = talloc_asprintf(tmp_ctx, "%s%s%s%s", smb_server,
smb_share, smb_path, smb_cse_suffix);Unchecked allocation.
OK
- DEBUG(SSSDBG_TRACE_FUNC, "smb_uri: %s\n", smb_uri);
- context = smbc_new_context();
- if (context == NULL) {
- errno = 0;
- file = smbc_getFunctionOpen(smbc_ctx)(smbc_ctx, smb_uri, O_RDONLY,
0755);
- if (file == NULL) {
ret = errno;DEBUG(SSSDBG_CRIT_FAILURE, "smbc_getFunctionOpen failed[%d][%s]\n",
ret, strerror(ret));goto done;- }
- buf = talloc_array(tmp_ctx, uint8_t, SMB_BUFFER_SIZE);
Another unchecked allocation.
OK
- errno = 0;
- buflen = smbc_getFunctionRead(smbc_ctx)(smbc_ctx, file, buf,
SMB_BUFFER_SIZE);
- if(buflen < 0) {
While you're changing the code, can you also put a space after if here? (I know, total nitpick..)
OK
ret = errno;DEBUG(SSSDBG_CRIT_FAILURE, "smbc_getFunctionRead failed[%d][%s]\n",
ret, strerror(ret));goto done;- }
- DEBUG(SSSDBG_TRACE_ALL, "smb_buflen: %d\n", buflen);
- ret = gpo_cache_store_file(smb_path, smb_cse_suffix, buf, buflen);
General question here -- what happens if two users whose GPO policies reference the same files log in simultaneously? Do we clobber the files?
With the current patch, the answer is "yes" (the files are over-written). However, in the next patch, I am including a gpo_cache_expire_timeout option, in which case the answer will be "no". Specifically, if the policy files related to a gpo-guid have been cached at a certain time, and if the same gpo-guid is needed again within the expire_timeout time, whether by the same user or by a different user, then the gpo code will re-use the existing files in the GPO_CACHE dir (and will not interact with the SMB server at all). Does that make sense?
- if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,"gpo_cache_store_file failed [%d][%s]\n",ret, strerror(ret));goto done;- }
- done:
- talloc_free(tmp_ctx);
- return ret;
+}
+/*
- Using its smb_uri components and cached_gpt_version inputs, this
function
- does several things:
- it downloads the GPT_INI file to GPO_CACHE
- it parses the sysvol_gpt_version field from the GPT_INI file
- if the sysvol_gpt_version is greater than the cached_gpt_version
- it downloads the policy file to GPO_CACHE
- else
- it doesn't retrieve the policy file
- in this case, the backend will use the existing policy file in
GPO_CACHE
- it returns the sysvol_gpt_version in the _sysvol_gpt_version output
param
- Note that if the cached_gpt_version sent by the backend is -1 (to
indicate
- that no gpt_version has been set in the cache for the corresponding
gpo_guid),
- then the parsed sysvol_gpt_version (which must be at least 0) will be
greater
- than the cached_gpt_version, thereby triggering a fresh download.
- Note that the backend will later do the following:
- backend will save the the sysvol_gpt_version to sysdb cache
- backend will read the policy file from the GPO_CACHE
- */
+static errno_t +perform_smb_operations(int cached_gpt_version,
const char *smb_server,const char *smb_share,const char *smb_path,const char *smb_cse_suffix,int *_sysvol_gpt_version)+{
- SMBCCTX *smbc_ctx;
- int ret = 0;
I don't think we need to initialise ret to zero. From defensive point of view it would be better to init with EINVAL. I think zero can be removed.I think it's better to set ret to zero (or EOK) prior to entering the done label from the main control flow, like this:
static int fn(ctx) { ret = do_stuff(ctx); if (ret != EOK) { goto done; } ret = EOK; done: return ret; }
Why? In your example, if do_stuff() returns EOK, won't ret already be set to EOK (by the time it reaches the "ret = EOK;" statement)?
- int sysvol_gpt_version;
- smbc_ctx = smbc_new_context();
- if (smbc_ctx == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Could not allocate new smbc context\n"); ret = ENOMEM; goto done; }
- smbc_setFunctionAuthData(context, sssd_krb_get_auth_data_fn);
- smbc_setOptionUseKerberos(context, 1);
smbc_setFunctionAuthData(smbc_ctx, sssd_krb_get_auth_data_fn);
smbc_setOptionUseKerberos(smbc_ctx, 1);
/* Initialize the context using the previously specified options */
- if (smbc_init_context(context) == NULL) {
- if (smbc_init_context(smbc_ctx) == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Could not initialize smbc context\n"); ret = ENOMEM; goto done; }
- /* Tell the compatibility layer to use this context */
- smbc_set_context(context);
- int remotehandle = smbc_open(smb_uri, O_RDONLY, 0755);
- if (remotehandle < 0) {
DEBUG(SSSDBG_CRIT_FAILURE, "smbc_open failed\n");ret = EPIPE;goto done;- }
- buf = talloc_array(tmp_ctx, uint8_t, SMB_BUFFER_SIZE);
- buflen = smbc_read(remotehandle, buf, SMB_BUFFER_SIZE);
- if(buflen < 0) {
DEBUG(SSSDBG_CRIT_FAILURE, "smbc_read failed\n");ret = EPIPE;goto done;- }
- DEBUG(SSSDBG_CRIT_FAILURE, "buflen: %d\n", buflen);
- smbc_close(remotehandle);
- ret = store_bytes_in_gpo_cache(unix_path, buf, buflen);
- /* download ini file */
- ret = copy_smb_file_to_gpo_cache(smbc_ctx, smb_server, smb_share,
smb_path,
if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE,GPT_INI);
"store_bytes_in_gpo_cache failed [%d][%s]\n",
"copy_smb_file_to_gpo_cache failed [%d][%s]\n", ret, strerror(ret)); goto done;}
ret = ad_gpo_parse_ini_file(smb_path, &sysvol_gpt_version);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,"Cannot parse ini file: [%d][%s]\n", ret, strerror(ret));goto done;}
DEBUG(SSSDBG_TRACE_FUNC, "sysvol_gpt_version: %d\n",
sysvol_gpt_version);
- if (sysvol_gpt_version > cached_gpt_version) {
/* download policy file */ret = copy_smb_file_to_gpo_cache(smbc_ctx, smb_server, smb_share,smb_path, smb_cse_suffix);if (ret != EOK) {DEBUG(SSSDBG_CRIT_FAILURE,"copy_smb_file_to_gpo_cache failed [%d][%s]\n",ret, strerror(ret));goto done;}- }
- *_sysvol_gpt_version = sysvol_gpt_version;
done:
- smbc_free_context(context, 0);
- talloc_free(tmp_ctx);
- smbc_free_context(smbc_ctx, 0); return ret;
}
@@ -388,6 +624,7 @@ main(int argc, const char *argv[]) poptContext pc; int debug_fd = -1; errno_t ret;
- int sysvol_gpt_version; int result; TALLOC_CTX *main_ctx = NULL; uint8_t *buf = NULL;
@@ -484,18 +721,21 @@ main(int argc, const char *argv[]) goto fail; }
- DEBUG(SSSDBG_TRACE_FUNC, "processing security settings\n");
- DEBUG(SSSDBG_TRACE_FUNC, "performing smb operations\n");
- result = process_security_settings_cse(main_ctx,
ibuf->smb_uri,ibuf->unix_path);
- result = perform_smb_operations(ibuf->cached_gpt_version,
ibuf->smb_server,ibuf->smb_share,ibuf->smb_path,ibuf->smb_cse_suffix, if (result != EOK) { DEBUG(SSSDBG_CRIT_FAILURE,&sysvol_gpt_version);
"process_security_settings_cse failed.[%d][%s].\n",
"perform_smb_operations failed.[%d][%s].\n", result, strerror(result));missing goto fail.This is a reason of 1st warning. src/providers/ad/ad_gpo_child.c:142:34: error: ‘sysvol_gpt_version’ may be used uninitialized in this function [-Werror=maybe-uninitialized] SAFEALIGN_SET_UINT32(&r->buf[p], sysvol_gpt_version, &p); ^ src/providers/ad/ad_gpo_child.c:627:9: note: ‘sysvol_gpt_version’ was declared here int sysvol_gpt_version;
}There is also another gcc warning. It is only visible with -O1 or higher. The same warning is with ferao 20/rawhide and rhel6
Nice catch, I only tested with clang and -O0.
src/providers/ad/ad_gpo_child.c: In function ‘main’: src/providers/ad/ad_gpo_child.c:142:34: error: ‘gpt_version’ may be used uninitialized in this function [-Werror=maybe-uninitialized] SAFEALIGN_SET_UINT32(&r->buf[p], sysvol_gpt_version, &p); ^ src/providers/ad/ad_gpo_child.c:413:9: note: ‘gpt_version’ was declared here int gpt_version; ^ cc1: all warnings being treated as errors
There are two possibel solutions: a) explicit initialisation to unexpected value. --- a/src/providers/ad/ad_gpo_child.c +++ b/src/providers/ad/ad_gpo_child.c @@ -410,7 +410,7 @@ ad_gpo_parse_ini_file(const char *smb_path, struct ini_cfgobj *ini_config = NULL; const char *ini_filename; int ret;
- int gpt_version;
- int gpt_version = -1; TALLOC_CTX *tmp_ctx = NULL;
b) disable inlining of parse_ini_file_with_libini. --- a/src/providers/ad/ad_gpo_child.c +++ b/src/providers/ad/ad_gpo_child.c @@ -359,7 +359,7 @@ static errno_t gpo_cache_store_file(const char *smb_path, return ret; }
-static errno_t +errno_t parse_ini_file_with_libini(struct ini_cfgobj *ini_config, int *_gpt_version) {
LS
I personally prefer a) here. _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
Revised patch attached, which addresses the last three code review emails for this patch (Jakub-1, Lukas, Jakub-2).
Thanks, Yassir.
On Wed, Jul 23, 2014 at 03:29:04AM -0400, Yassir Elley wrote:
General question here -- what happens if two users whose GPO policies reference the same files log in simultaneously? Do we clobber the files?
With the current patch, the answer is "yes" (the files are over-written). However, in the next patch, I am including a gpo_cache_expire_timeout option, in which case the answer will be "no". Specifically, if the policy files related to a gpo-guid have been cached at a certain time, and if the same gpo-guid is needed again within the expire_timeout time, whether by the same user or by a different user, then the gpo code will re-use the existing files in the GPO_CACHE dir (and will not interact with the SMB server at all). Does that make sense?
As long as there is a plan, I'm fine. Thank you for the explanation.
- if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,"gpo_cache_store_file failed [%d][%s]\n",ret, strerror(ret));goto done;- }
- done:
- talloc_free(tmp_ctx);
- return ret;
+}
+/*
- Using its smb_uri components and cached_gpt_version inputs, this
function
- does several things:
- it downloads the GPT_INI file to GPO_CACHE
- it parses the sysvol_gpt_version field from the GPT_INI file
- if the sysvol_gpt_version is greater than the cached_gpt_version
- it downloads the policy file to GPO_CACHE
- else
- it doesn't retrieve the policy file
- in this case, the backend will use the existing policy file in
GPO_CACHE
- it returns the sysvol_gpt_version in the _sysvol_gpt_version output
param
- Note that if the cached_gpt_version sent by the backend is -1 (to
indicate
- that no gpt_version has been set in the cache for the corresponding
gpo_guid),
- then the parsed sysvol_gpt_version (which must be at least 0) will be
greater
- than the cached_gpt_version, thereby triggering a fresh download.
- Note that the backend will later do the following:
- backend will save the the sysvol_gpt_version to sysdb cache
- backend will read the policy file from the GPO_CACHE
- */
+static errno_t +perform_smb_operations(int cached_gpt_version,
const char *smb_server,const char *smb_share,const char *smb_path,const char *smb_cse_suffix,int *_sysvol_gpt_version)+{
- SMBCCTX *smbc_ctx;
- int ret = 0;
I don't think we need to initialise ret to zero. From defensive point of view it would be better to init with EINVAL. I think zero can be removed.I think it's better to set ret to zero (or EOK) prior to entering the done label from the main control flow, like this:
static int fn(ctx) { ret = do_stuff(ctx); if (ret != EOK) { goto done; } ret = EOK; done: return ret; }Why? In your example, if do_stuff() returns EOK, won't ret already be set to EOK (by the time it reaches the "ret = EOK;" statement)?
I might have confused you, I was answering to Lukas mostly. But in general, the point I was trying to make was that I feel it's safer that the the functions generally return EOK if the code flows all the way to the done label w/o any jumps. (of course shortcuts are a special case when there's nothing to do for the function for example.)
[...]
I personally prefer a) here. _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
Revised patch attached, which addresses the last three code review emails for this patch (Jakub-1, Lukas, Jakub-2).
Thanks, Yassir.
The code now looks OK to me. I will just do some testing -- if you have more patches that depend on this one, feel free to send them right away so that you're not blocked on review.
Thanks for the fast turnaround fixing the issues we spotted.
----- Original Message -----
On (22/07/14 03:13), Yassir Elley wrote:
On Sun, Jul 20, 2014 at 05:48:45PM -0400, Yassir Elley wrote:
Hi,
The attached patch adds support for gpo version checking and for conditional downloading of policy files. In order to get that to work, this patch also replaces the deprecated smb functions (e.g. smbc_open, smbc_read, etc) I had been using (not knowing they were deprecated) with the newer smb functions, such as "smbc_getFunctionOpen(smbc_ctx)(smbc_ctx, smb_uri, O_RDONLY, 0755);".
Before this patch, the policy files were being unconditionally downloaded every time. After this patch, the backend sends the cached_gpt_version (from a previous transaction, if any) to the gpo_child. The gpo_child downloads the per-GPO GPT.INI file, from which it extracts the sysvol_gpt_version. Only if the sysvol_gpt_version is greater than the cached_gpt_version (or if there is no cached_gpt_version) does the gpo_child download the policy files. The gpo_child returns the sysvol_gpt_version to the backend, which stores it in the cache.
Note that, while a GPO has both a SYSVOL-stored Group Policy Template (gpt) version, as well as an LDAP-stored Group Policy Container (gpc) version (which should be equal), it is best practice to use the gpt version for version checking b/c SYSVOL replication is typically much faster than AD replication. In other words, the gpt version is quickly up-to-date on all DCs for a given domain, while the gpc version may be stale for some time after a change is made.
In order to keep the gpo_child as simple as possible, the gpo_child does not interact with the sysdb cache at all (only the backend does). However, the gpo_child does need to parse the GPT.INI file in order to determine whether to download the policy files. As such, the gpo_child includes GPT.INI file parsing functionality (which is per-GPO, and not CSE-specific).
Note that this patch does not add support for offline mode, which will be implemented in a subsequent patch.
Regards, Yassir.
Hi Jakub,
I agree with all of your comments so far and have attached revised patches to this email. I split the original patch into two patches, as you suggested, and I addressed your review comments in the revised patches. Thanks for the review! Keep the comments coming! :)
Regards, Yassir.
From f3f00a78102ad5e389266b28c43d0396caf57592 Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Tue, 22 Jul 2014 03:03:03 -0400 Subject: [PATCH 2/2] AD-GPO: only download policy files if gpo version changes
src/providers/ad/ad_gpo.c | 306 +++++++++++++++++++----------- src/providers/ad/ad_gpo_child.c | 404 ++++++++++++++++++++++++++++++++-------- 2 files changed, 517 insertions(+), 193 deletions(-)
I have few comments to the ad_gpo_child
diff --git a/src/providers/ad/ad_gpo_child.c b/src/providers/ad/ad_gpo_child.c index 22b8b52046b6298f2eeaf425cef7bf0271865fe2..93ae592e591eefe812212cc99ddbec70c4e0aba9 100644 --- a/src/providers/ad/ad_gpo_child.c +++ b/src/providers/ad/ad_gpo_child.c @@ -27,6 +27,7 @@ #include <sys/stat.h> #include <popt.h> #include <libsmbclient.h> +#include <ini_configobj.h> #include <security/pam_modules.h>
#include "util/util.h" @@ -34,14 +35,17 @@ #include "providers/dp_backend.h" #include "sss_cli.h"
-#define RIGHTS_SECTION "Privilege Rights" -#define ALLOW_LOGON_LOCALLY "SeInteractiveLogonRight" -#define DENY_LOGON_LOCALLY "SeDenyInteractiveLogonRight" #define SMB_BUFFER_SIZE 65536 +#define GPT_INI "/GPT.INI" +#define INI_GENERAL_SECTION "General" +#define GPT_INI_VERSION "Version"
struct input_buffer {
- const char *smb_uri;
- const char *unix_path;
- int cached_gpt_version;
- const char *smb_server;
- const char *smb_share;
- const char *smb_path;
- const char *smb_cse_suffix;
};
static errno_t @@ -51,34 +55,62 @@ unpack_buffer(uint8_t *buf, { size_t p = 0; uint32_t len;
- uint32_t cached_gpt_version;
- /* smb_uri size and length */
- SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
- DEBUG(SSSDBG_TRACE_ALL, "smb_uri size: %d\n", len);
- /* cached_gpt_version */
- SAFEALIGN_COPY_UINT32_CHECK(&cached_gpt_version, buf + p, size, &p);
- DEBUG(SSSDBG_TRACE_FUNC, "cached_gpt_version: %d\n",
cached_gpt_version);
ibuf->cached_gpt_version = cached_gpt_version;
/* smb_server */
SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
DEBUG(SSSDBG_TRACE_ALL, "smb_server length: %d\n", len); if (len == 0) { return EINVAL; } else { if ((p + len ) > size) return EINVAL;
ibuf->smb_uri = talloc_strndup(ibuf, (char *)(buf + p), len);if (ibuf->smb_uri == NULL) return ENOMEM;DEBUG(SSSDBG_TRACE_ALL, "got smb_uri: %s\n", ibuf->smb_uri);
ibuf->smb_server = talloc_strndup(ibuf, (char *)(buf + p), len);if (ibuf->smb_server == NULL) return ENOMEM; }DEBUG(SSSDBG_TRACE_ALL, "smb_server: %s\n", ibuf->smb_server); p += len;
- /* unix_path size and length */
- /* smb_share */ SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
- DEBUG(SSSDBG_TRACE_ALL, "smb_share length: %d\n", len);
- if (len == 0) {
return EINVAL;- } else {
if ((p + len ) > size) return EINVAL;ibuf->smb_share = talloc_strndup(ibuf, (char *)(buf + p), len);if (ibuf->smb_share == NULL) return ENOMEM;DEBUG(SSSDBG_TRACE_ALL, "smb_share: %s\n", ibuf->smb_share);p += len;- }
- DEBUG(SSSDBG_TRACE_ALL, "unix_path size: %d\n", len);
/* smb_path */
SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
DEBUG(SSSDBG_TRACE_ALL, "smb_path length: %d\n", len);
if (len == 0) {
return EINVAL;} else {
if ((p + len ) > size) return EINVAL;ibuf->smb_path = talloc_strndup(ibuf, (char *)(buf + p), len);if (ibuf->smb_path == NULL) return ENOMEM;DEBUG(SSSDBG_TRACE_ALL, "smb_path: %s\n", ibuf->smb_path);p += len;}
/* smb_cse_suffix */
SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
DEBUG(SSSDBG_TRACE_ALL, "smb_cse_suffix length: %d\n", len); if (len == 0) { return EINVAL; } else { if ((p + len ) > size) return EINVAL;
ibuf->unix_path = talloc_strndup(ibuf, (char *)(buf + p), len);if (ibuf->unix_path == NULL) return ENOMEM;DEBUG(SSSDBG_TRACE_ALL, "got unix_path: %s\n", ibuf->unix_path);
ibuf->smb_cse_suffix = talloc_strndup(ibuf, (char *)(buf + p),len);
if (ibuf->smb_cse_suffix == NULL) return ENOMEM;DEBUG(SSSDBG_TRACE_ALL, "smb_cse_suffix: %s\n",ibuf->smb_cse_suffix); p += len; }
@@ -88,14 +120,16 @@ unpack_buffer(uint8_t *buf,
static errno_t pack_buffer(struct response *r,
int sysvol_gpt_version, int result){ size_t p = 0;
/* A buffer with the following structure must be created:
* uint32_t sysvol_gpt_version (required)*/
- uint32_t status of the request (required)
- r->size = sizeof(uint32_t);
r->size = 2 * sizeof(uint32_t);
r->buf = talloc_array(r, uint8_t, r->size); if(r->buf == NULL) {
@@ -104,6 +138,9 @@ pack_buffer(struct response *r,
DEBUG(SSSDBG_TRACE_FUNC, "result [%d]\n", result);
- /* sysvol_gpt_version */
- SAFEALIGN_SET_UINT32(&r->buf[p], sysvol_gpt_version, &p);
- /* result */ SAFEALIGN_SET_UINT32(&r->buf[p], result, &p);
@@ -112,6 +149,7 @@ pack_buffer(struct response *r,
static errno_t prepare_response(TALLOC_CTX *mem_ctx,
int sysvol_gpt_version, int result, struct response **rsp){ @@ -126,14 +164,14 @@ prepare_response(TALLOC_CTX *mem_ctx, r->buf = NULL; r->size = 0;
- ret = pack_buffer(r, result);
ret = pack_buffer(r, sysvol_gpt_version, result); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "pack_buffer failed\n"); return ret; }
*rsp = r;
- DEBUG(SSSDBG_TRACE_FUNC, "r->size: %zu\n", r->size);
- DEBUG(SSSDBG_TRACE_ALL, "r->size: %zu\n", r->size); return EOK;
}
@@ -153,12 +191,12 @@ sssd_krb_get_auth_data_fn(const char * pServer,
/*
- This function prepares the gpo_cache by:
- parsing the input_unix_path into its component directories
- parsing the input_smb_path into its component directories
- creating each component directory (if it doesn't already exist)
*/ static errno_t prepare_gpo_cache(TALLOC_CTX *mem_ctx, const char *cache_dir,
const char *input_unix_path)
const char *input_smb_path_with_suffix){ char *current_dir; char *ptr; @@ -167,25 +205,27 @@ static errno_t prepare_gpo_cache(TALLOC_CTX *mem_ctx, int i; char *first = NULL; char *last = NULL;
- char *unix_path = NULL;
- char *smb_path_with_suffix = NULL;
- unix_path = talloc_strdup(mem_ctx, input_unix_path);
- if (unix_path == NULL) {
- smb_path_with_suffix = talloc_strdup(mem_ctx,
input_smb_path_with_suffix);
if (smb_path_with_suffix == NULL) { return ENOMEM; }
DEBUG(SSSDBG_TRACE_ALL, "smb_path_with_suffix: %s\n",
smb_path_with_suffix);
- current_dir = talloc_strdup(mem_ctx, cache_dir); if (current_dir == NULL) { return ENOMEM; }
- ptr = unix_path + 1;
- ptr = smb_path_with_suffix + 1; while ((ptr = strchr(ptr, delim))) { ptr++; num_dirs++; }
- ptr = unix_path + 1;
ptr = smb_path_with_suffix + 1;
for (i = 0; i < num_dirs; i++) { first = ptr;
@@ -210,12 +250,16 @@ static errno_t prepare_gpo_cache(TALLOC_CTX *mem_ctx,
/*
- This function stores the input buf to a local file, whose file path
- is constructed by concatenating GPO_CACHE_PATH to the input unix_path.
- Note that the backend will later read the policy file from the same file
path.
- is constructed by concatenating:
- GPO_CACHE_PATH,
- input smb_path,
- input smb_cse_suffix
- Note that the backend will later read the file from the same file path.
*/ -static errno_t store_bytes_in_gpo_cache(const char *unix_path,
uint8_t *buf,int buflen)+static errno_t gpo_cache_store_file(const char *smb_path,
const char *smb_cse_suffix,uint8_t *buf,int buflen){ int ret; int fd = -1; @@ -223,6 +267,7 @@ static errno_t store_bytes_in_gpo_cache(const char *unix_path, ssize_t written; mode_t old_umask; char *filename = NULL;
char *smb_path_with_suffix = NULL; TALLOC_CTX *tmp_ctx = NULL;
tmp_ctx = talloc_new(NULL);
@@ -231,10 +276,18 @@ static errno_t store_bytes_in_gpo_cache(const char *unix_path, goto done; }
- /* create component directories of unix_path, if needed */
- ret = prepare_gpo_cache(tmp_ctx, GPO_CACHE_PATH, unix_path);
- smb_path_with_suffix =
talloc_asprintf(tmp_ctx, "%s%s", smb_path, smb_cse_suffix);- if (smb_path_with_suffix == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");ret = ENOMEM;goto done;- }
- filename = talloc_asprintf(tmp_ctx, GPO_CACHE_PATH"%s", unix_path);
- /* create component directories of smb_path, if needed */
- ret = prepare_gpo_cache(tmp_ctx, GPO_CACHE_PATH, smb_path_with_suffix);
^^^ return code is ignored. It may cause problems if prepare_gpo_cache fail.
OK
- filename = talloc_asprintf(tmp_ctx, GPO_CACHE_PATH"%s",
smb_path_with_suffix); tmp_name = talloc_asprintf(tmp_ctx, "%sXXXXXX", filename); if (tmp_name == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n"); @@ -303,21 +356,132 @@ static errno_t store_bytes_in_gpo_cache(const char *unix_path, return ret; }
+static errno_t +parse_ini_file_with_libini(struct ini_cfgobj *ini_config,
int *_gpt_version)+{
- int ret = 0;
- struct value_obj *vobj = NULL;
- int gpt_version;
- ret = ini_get_config_valueobj(INI_GENERAL_SECTION, GPT_INI_VERSION,
ini_config, INI_GET_FIRST_VALUE, &vobj);- if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,"ini_get_config_valueobj failed [%d][%s]\n", ret,strerror(ret));
goto done;- }
- if (vobj == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "section/name not found: [%s][%s]\n",INI_GENERAL_SECTION, GPT_INI_VERSION);ret = EINVAL;goto done;- }
- gpt_version = ini_get_int32_config_value(vobj, 0, -1, &ret);
- if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,"ini_get_int32_config_value failed [%d][%s]\n",ret, strerror(ret));goto done;- }
- *_gpt_version = gpt_version;
- ret = EOK;
- done:
- return ret;
+}
+/*
- This function parses the GPT_INI file stored in the gpo_cache, and uses
the
- results to populate the output parameters ...
- */
+static errno_t +ad_gpo_parse_ini_file(const char *smb_path,
int *_gpt_version)+{
- struct ini_cfgfile *file_ctx = NULL;
- struct ini_cfgobj *ini_config = NULL;
- const char *ini_filename;
- int ret;
- int gpt_version;
- TALLOC_CTX *tmp_ctx = NULL;
- tmp_ctx = talloc_new(NULL);
- if (tmp_ctx == NULL) {
ret = ENOMEM;goto done;- }
- ini_filename = talloc_asprintf(tmp_ctx, GPO_CACHE_PATH"%s%s",
smb_path, GPT_INI);could you test if allocation was successful?
OK. This happens in several other places in ad_gpo_child.c, so I will fix those too.
- DEBUG(SSSDBG_TRACE_FUNC, "ini_filename:%s\n", ini_filename);
- ret = ini_config_create(&ini_config);
- if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,"ini_config_create failed [%d][%s]\n", ret, strerror(ret));goto done;- }
- ret = ini_config_file_open(ini_filename, 0, &file_ctx);
- if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,"ini_config_file_open failed [%d][%s]\n", ret,strerror(ret));
goto done;- }
- ret = ini_config_parse(file_ctx, INI_STOP_ON_NONE, 0, 0, ini_config);
- if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,"ini_config_parse failed [%d][%s]\n", ret, strerror(ret));goto done;- }
- ret = parse_ini_file_with_libini(ini_config, &gpt_version);
- if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,"parse_ini_file_with_libini failed [%d][%s]\n",ret, strerror(ret));goto done;- }
- *_gpt_version = gpt_version;
- done:
- if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "Error encountered: %d.\n", ret);- }
- ini_config_file_destroy(file_ctx);
- ini_config_destroy(ini_config);
- talloc_free(tmp_ctx);
- return ret;
+}
/*
- This cse-specific function (GP_EXT_GUID_SECURITY) opens an SMB
connection,
- retrieves the data referenced by the input smb_uri, and then closes the
SMB
- connection. The data is then written to a file in the GPO_CACHE
directory,
- to be read by the backend.
- This function uses the input smb uri components to download a sysvol
file
- (e.g. INI file, policy file, etc) and store it to the GPO_CACHE
directory. */ static errno_t -process_security_settings_cse(TALLOC_CTX *mem_ctx,
const char *smb_uri,const char *unix_path)+copy_smb_file_to_gpo_cache(SMBCCTX *smbc_ctx,
const char *smb_server,const char *smb_share,const char *smb_path,const char *smb_cse_suffix){
- SMBCCTX *context;
char *smb_uri = NULL;
SMBCFILE *file; int ret = 0; uint8_t *buf = NULL; int buflen = 0;
TALLOC_CTX *tmp_ctx = NULL;
tmp_ctx = talloc_new(NULL);
@@ -325,59 +489,131 @@ process_security_settings_cse(TALLOC_CTX *mem_ctx, return ENOMEM; }
- DEBUG(SSSDBG_TRACE_ALL, "%s\n", smb_uri);
- DEBUG(SSSDBG_TRACE_ALL, "%s\n", unix_path);
- smb_uri = talloc_asprintf(tmp_ctx, "%s%s%s%s", smb_server,
smb_share, smb_path, smb_cse_suffix);- DEBUG(SSSDBG_TRACE_FUNC, "smb_uri: %s\n", smb_uri);
- context = smbc_new_context();
- if (context == NULL) {
- errno = 0;
- file = smbc_getFunctionOpen(smbc_ctx)(smbc_ctx, smb_uri, O_RDONLY,
0755);
- if (file == NULL) {
ret = errno;DEBUG(SSSDBG_CRIT_FAILURE, "smbc_getFunctionOpen failed[%d][%s]\n",
ret, strerror(ret));goto done;- }
- buf = talloc_array(tmp_ctx, uint8_t, SMB_BUFFER_SIZE);
- errno = 0;
- buflen = smbc_getFunctionRead(smbc_ctx)(smbc_ctx, file, buf,
SMB_BUFFER_SIZE);
- if(buflen < 0) {
ret = errno;DEBUG(SSSDBG_CRIT_FAILURE, "smbc_getFunctionRead failed[%d][%s]\n",
ret, strerror(ret));goto done;- }
- DEBUG(SSSDBG_TRACE_ALL, "smb_buflen: %d\n", buflen);
- ret = gpo_cache_store_file(smb_path, smb_cse_suffix, buf, buflen);
- if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,"gpo_cache_store_file failed [%d][%s]\n",ret, strerror(ret));goto done;- }
- done:
- talloc_free(tmp_ctx);
- return ret;
+}
+/*
- Using its smb_uri components and cached_gpt_version inputs, this
function
- does several things:
- it downloads the GPT_INI file to GPO_CACHE
- it parses the sysvol_gpt_version field from the GPT_INI file
- if the sysvol_gpt_version is greater than the cached_gpt_version
- it downloads the policy file to GPO_CACHE
- else
- it doesn't retrieve the policy file
- in this case, the backend will use the existing policy file in
GPO_CACHE
- it returns the sysvol_gpt_version in the _sysvol_gpt_version output
param
- Note that if the cached_gpt_version sent by the backend is -1 (to
indicate
- that no gpt_version has been set in the cache for the corresponding
gpo_guid),
- then the parsed sysvol_gpt_version (which must be at least 0) will be
greater
- than the cached_gpt_version, thereby triggering a fresh download.
- Note that the backend will later do the following:
- backend will save the the sysvol_gpt_version to sysdb cache
- backend will read the policy file from the GPO_CACHE
- */
+static errno_t +perform_smb_operations(int cached_gpt_version,
const char *smb_server,const char *smb_share,const char *smb_path,const char *smb_cse_suffix,int *_sysvol_gpt_version)+{
- SMBCCTX *smbc_ctx;
- int ret = 0;
I don't think we need to initialise ret to zero. From defensive point of view it would be better to init with EINVAL. I think zero can be removed.
OK.
- int sysvol_gpt_version;
- smbc_ctx = smbc_new_context();
- if (smbc_ctx == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Could not allocate new smbc context\n"); ret = ENOMEM; goto done; }
- smbc_setFunctionAuthData(context, sssd_krb_get_auth_data_fn);
- smbc_setOptionUseKerberos(context, 1);
smbc_setFunctionAuthData(smbc_ctx, sssd_krb_get_auth_data_fn);
smbc_setOptionUseKerberos(smbc_ctx, 1);
/* Initialize the context using the previously specified options */
- if (smbc_init_context(context) == NULL) {
- if (smbc_init_context(smbc_ctx) == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Could not initialize smbc context\n"); ret = ENOMEM; goto done; }
- /* Tell the compatibility layer to use this context */
- smbc_set_context(context);
- int remotehandle = smbc_open(smb_uri, O_RDONLY, 0755);
- if (remotehandle < 0) {
DEBUG(SSSDBG_CRIT_FAILURE, "smbc_open failed\n");ret = EPIPE;goto done;- }
- buf = talloc_array(tmp_ctx, uint8_t, SMB_BUFFER_SIZE);
- buflen = smbc_read(remotehandle, buf, SMB_BUFFER_SIZE);
- if(buflen < 0) {
DEBUG(SSSDBG_CRIT_FAILURE, "smbc_read failed\n");ret = EPIPE;goto done;- }
- DEBUG(SSSDBG_CRIT_FAILURE, "buflen: %d\n", buflen);
- smbc_close(remotehandle);
- ret = store_bytes_in_gpo_cache(unix_path, buf, buflen);
- /* download ini file */
- ret = copy_smb_file_to_gpo_cache(smbc_ctx, smb_server, smb_share,
smb_path,
if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE,GPT_INI);
"store_bytes_in_gpo_cache failed [%d][%s]\n",
"copy_smb_file_to_gpo_cache failed [%d][%s]\n", ret, strerror(ret)); goto done;}
ret = ad_gpo_parse_ini_file(smb_path, &sysvol_gpt_version);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,"Cannot parse ini file: [%d][%s]\n", ret, strerror(ret));goto done;}
DEBUG(SSSDBG_TRACE_FUNC, "sysvol_gpt_version: %d\n",
sysvol_gpt_version);
- if (sysvol_gpt_version > cached_gpt_version) {
/* download policy file */ret = copy_smb_file_to_gpo_cache(smbc_ctx, smb_server, smb_share,smb_path, smb_cse_suffix);if (ret != EOK) {DEBUG(SSSDBG_CRIT_FAILURE,"copy_smb_file_to_gpo_cache failed [%d][%s]\n",ret, strerror(ret));goto done;}- }
- *_sysvol_gpt_version = sysvol_gpt_version;
done:
- smbc_free_context(context, 0);
- talloc_free(tmp_ctx);
- smbc_free_context(smbc_ctx, 0); return ret;
}
@@ -388,6 +624,7 @@ main(int argc, const char *argv[]) poptContext pc; int debug_fd = -1; errno_t ret;
- int sysvol_gpt_version; int result; TALLOC_CTX *main_ctx = NULL; uint8_t *buf = NULL;
@@ -484,18 +721,21 @@ main(int argc, const char *argv[]) goto fail; }
- DEBUG(SSSDBG_TRACE_FUNC, "processing security settings\n");
- DEBUG(SSSDBG_TRACE_FUNC, "performing smb operations\n");
- result = process_security_settings_cse(main_ctx,
ibuf->smb_uri,ibuf->unix_path);
- result = perform_smb_operations(ibuf->cached_gpt_version,
ibuf->smb_server,ibuf->smb_share,ibuf->smb_path,ibuf->smb_cse_suffix, if (result != EOK) { DEBUG(SSSDBG_CRIT_FAILURE,&sysvol_gpt_version);
"process_security_settings_cse failed.[%d][%s].\n",
"perform_smb_operations failed.[%d][%s].\n", result, strerror(result));missing goto fail.This is a reason of 1st warning. src/providers/ad/ad_gpo_child.c:142:34: error: ‘sysvol_gpt_version’ may be used uninitialized in this function [-Werror=maybe-uninitialized] SAFEALIGN_SET_UINT32(&r->buf[p], sysvol_gpt_version, &p); ^ src/providers/ad/ad_gpo_child.c:627:9: note: ‘sysvol_gpt_version’ was declared here int sysvol_gpt_version;
}There is also another gcc warning. It is only visible with -O1 or higher. The same warning is with ferao 20/rawhide and rhel6
src/providers/ad/ad_gpo_child.c: In function ‘main’: src/providers/ad/ad_gpo_child.c:142:34: error: ‘gpt_version’ may be used uninitialized in this function [-Werror=maybe-uninitialized] SAFEALIGN_SET_UINT32(&r->buf[p], sysvol_gpt_version, &p); ^ src/providers/ad/ad_gpo_child.c:413:9: note: ‘gpt_version’ was declared here int gpt_version; ^ cc1: all warnings being treated as errors
There are two possibel solutions: a) explicit initialisation to unexpected value. --- a/src/providers/ad/ad_gpo_child.c +++ b/src/providers/ad/ad_gpo_child.c @@ -410,7 +410,7 @@ ad_gpo_parse_ini_file(const char *smb_path, struct ini_cfgobj *ini_config = NULL; const char *ini_filename; int ret;
- int gpt_version;
- int gpt_version = -1; TALLOC_CTX *tmp_ctx = NULL;
b) disable inlining of parse_ini_file_with_libini. --- a/src/providers/ad/ad_gpo_child.c +++ b/src/providers/ad/ad_gpo_child.c @@ -359,7 +359,7 @@ static errno_t gpo_cache_store_file(const char *smb_path, return ret; }
-static errno_t +errno_t parse_ini_file_with_libini(struct ini_cfgobj *ini_config, int *_gpt_version) {
LS _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
I will initialize gpt_version to -1, as that seems to remove the warning.
However, I am a little confused. My understanding of why the sysvol_gpt_version warning is occurring is that perform_smb_operations() might fail. Since I don't have a "goto fail" in the error path, prepare_response() might use an uninitialized value. Adding the "goto fail" makes this warning go away (as you mentioned).
However, I don't understand why the gpt_version warning is occuring. Even though gpt_version is uninitialized in ad_gpo_parse_ini_file, the value of gpt_version is only being used if ad_gpo_parse_ini_file returns EOK. By the time gpt_version is used in pack_buffer, it should definitely be initialized, no?
I will send a revised patch later.
Thanks, Yassir.
On (23/07/14 03:14), Yassir Elley wrote:
----- Original Message -----
On (22/07/14 03:13), Yassir Elley wrote:
On Sun, Jul 20, 2014 at 05:48:45PM -0400, Yassir Elley wrote:
Hi,
The attached patch adds support for gpo version checking and for conditional downloading of policy files. In order to get that to work, this patch also replaces the deprecated smb functions (e.g. smbc_open, smbc_read, etc) I had been using (not knowing they were deprecated) with the newer smb functions, such as "smbc_getFunctionOpen(smbc_ctx)(smbc_ctx, smb_uri, O_RDONLY, 0755);".
Before this patch, the policy files were being unconditionally downloaded every time. After this patch, the backend sends the cached_gpt_version (from a previous transaction, if any) to the gpo_child. The gpo_child downloads the per-GPO GPT.INI file, from which it extracts the sysvol_gpt_version. Only if the sysvol_gpt_version is greater than the cached_gpt_version (or if there is no cached_gpt_version) does the gpo_child download the policy files. The gpo_child returns the sysvol_gpt_version to the backend, which stores it in the cache.
Note that, while a GPO has both a SYSVOL-stored Group Policy Template (gpt) version, as well as an LDAP-stored Group Policy Container (gpc) version (which should be equal), it is best practice to use the gpt version for version checking b/c SYSVOL replication is typically much faster than AD replication. In other words, the gpt version is quickly up-to-date on all DCs for a given domain, while the gpc version may be stale for some time after a change is made.
In order to keep the gpo_child as simple as possible, the gpo_child does not interact with the sysdb cache at all (only the backend does). However, the gpo_child does need to parse the GPT.INI file in order to determine whether to download the policy files. As such, the gpo_child includes GPT.INI file parsing functionality (which is per-GPO, and not CSE-specific).
Note that this patch does not add support for offline mode, which will be implemented in a subsequent patch.
Regards, Yassir.
Hi Jakub,
I agree with all of your comments so far and have attached revised patches to this email. I split the original patch into two patches, as you suggested, and I addressed your review comments in the revised patches. Thanks for the review! Keep the comments coming! :)
Regards, Yassir.
From f3f00a78102ad5e389266b28c43d0396caf57592 Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Tue, 22 Jul 2014 03:03:03 -0400 Subject: [PATCH 2/2] AD-GPO: only download policy files if gpo version changes
src/providers/ad/ad_gpo.c | 306 +++++++++++++++++++----------- src/providers/ad/ad_gpo_child.c | 404 ++++++++++++++++++++++++++++++++-------- 2 files changed, 517 insertions(+), 193 deletions(-)
I have few comments to the ad_gpo_child
diff --git a/src/providers/ad/ad_gpo_child.c b/src/providers/ad/ad_gpo_child.c index 22b8b52046b6298f2eeaf425cef7bf0271865fe2..93ae592e591eefe812212cc99ddbec70c4e0aba9 100644 --- a/src/providers/ad/ad_gpo_child.c +++ b/src/providers/ad/ad_gpo_child.c @@ -27,6 +27,7 @@ #include <sys/stat.h> #include <popt.h> #include <libsmbclient.h> +#include <ini_configobj.h> #include <security/pam_modules.h>
#include "util/util.h" @@ -34,14 +35,17 @@ #include "providers/dp_backend.h" #include "sss_cli.h"
-#define RIGHTS_SECTION "Privilege Rights" -#define ALLOW_LOGON_LOCALLY "SeInteractiveLogonRight" -#define DENY_LOGON_LOCALLY "SeDenyInteractiveLogonRight" #define SMB_BUFFER_SIZE 65536 +#define GPT_INI "/GPT.INI" +#define INI_GENERAL_SECTION "General" +#define GPT_INI_VERSION "Version"
struct input_buffer {
- const char *smb_uri;
- const char *unix_path;
- int cached_gpt_version;
- const char *smb_server;
- const char *smb_share;
- const char *smb_path;
- const char *smb_cse_suffix;
};
static errno_t @@ -51,34 +55,62 @@ unpack_buffer(uint8_t *buf, { size_t p = 0; uint32_t len;
- uint32_t cached_gpt_version;
- /* smb_uri size and length */
- SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
- DEBUG(SSSDBG_TRACE_ALL, "smb_uri size: %d\n", len);
- /* cached_gpt_version */
- SAFEALIGN_COPY_UINT32_CHECK(&cached_gpt_version, buf + p, size, &p);
- DEBUG(SSSDBG_TRACE_FUNC, "cached_gpt_version: %d\n",
cached_gpt_version);
ibuf->cached_gpt_version = cached_gpt_version;
/* smb_server */
SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
DEBUG(SSSDBG_TRACE_ALL, "smb_server length: %d\n", len); if (len == 0) { return EINVAL; } else { if ((p + len ) > size) return EINVAL;
ibuf->smb_uri = talloc_strndup(ibuf, (char *)(buf + p), len);if (ibuf->smb_uri == NULL) return ENOMEM;DEBUG(SSSDBG_TRACE_ALL, "got smb_uri: %s\n", ibuf->smb_uri);
ibuf->smb_server = talloc_strndup(ibuf, (char *)(buf + p), len);if (ibuf->smb_server == NULL) return ENOMEM; }DEBUG(SSSDBG_TRACE_ALL, "smb_server: %s\n", ibuf->smb_server); p += len;
- /* unix_path size and length */
- /* smb_share */ SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
- DEBUG(SSSDBG_TRACE_ALL, "smb_share length: %d\n", len);
- if (len == 0) {
return EINVAL;- } else {
if ((p + len ) > size) return EINVAL;ibuf->smb_share = talloc_strndup(ibuf, (char *)(buf + p), len);if (ibuf->smb_share == NULL) return ENOMEM;DEBUG(SSSDBG_TRACE_ALL, "smb_share: %s\n", ibuf->smb_share);p += len;- }
- DEBUG(SSSDBG_TRACE_ALL, "unix_path size: %d\n", len);
/* smb_path */
SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
DEBUG(SSSDBG_TRACE_ALL, "smb_path length: %d\n", len);
if (len == 0) {
return EINVAL;} else {
if ((p + len ) > size) return EINVAL;ibuf->smb_path = talloc_strndup(ibuf, (char *)(buf + p), len);if (ibuf->smb_path == NULL) return ENOMEM;DEBUG(SSSDBG_TRACE_ALL, "smb_path: %s\n", ibuf->smb_path);p += len;}
/* smb_cse_suffix */
SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
DEBUG(SSSDBG_TRACE_ALL, "smb_cse_suffix length: %d\n", len); if (len == 0) { return EINVAL; } else { if ((p + len ) > size) return EINVAL;
ibuf->unix_path = talloc_strndup(ibuf, (char *)(buf + p), len);if (ibuf->unix_path == NULL) return ENOMEM;DEBUG(SSSDBG_TRACE_ALL, "got unix_path: %s\n", ibuf->unix_path);
ibuf->smb_cse_suffix = talloc_strndup(ibuf, (char *)(buf + p),len);
if (ibuf->smb_cse_suffix == NULL) return ENOMEM;DEBUG(SSSDBG_TRACE_ALL, "smb_cse_suffix: %s\n",ibuf->smb_cse_suffix); p += len; }
@@ -88,14 +120,16 @@ unpack_buffer(uint8_t *buf,
static errno_t pack_buffer(struct response *r,
int sysvol_gpt_version, int result){ size_t p = 0;
/* A buffer with the following structure must be created:
* uint32_t sysvol_gpt_version (required)*/
- uint32_t status of the request (required)
- r->size = sizeof(uint32_t);
r->size = 2 * sizeof(uint32_t);
r->buf = talloc_array(r, uint8_t, r->size); if(r->buf == NULL) {
@@ -104,6 +138,9 @@ pack_buffer(struct response *r,
DEBUG(SSSDBG_TRACE_FUNC, "result [%d]\n", result);
- /* sysvol_gpt_version */
- SAFEALIGN_SET_UINT32(&r->buf[p], sysvol_gpt_version, &p);
- /* result */ SAFEALIGN_SET_UINT32(&r->buf[p], result, &p);
@@ -112,6 +149,7 @@ pack_buffer(struct response *r,
static errno_t prepare_response(TALLOC_CTX *mem_ctx,
int sysvol_gpt_version, int result, struct response **rsp){ @@ -126,14 +164,14 @@ prepare_response(TALLOC_CTX *mem_ctx, r->buf = NULL; r->size = 0;
- ret = pack_buffer(r, result);
ret = pack_buffer(r, sysvol_gpt_version, result); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "pack_buffer failed\n"); return ret; }
*rsp = r;
- DEBUG(SSSDBG_TRACE_FUNC, "r->size: %zu\n", r->size);
- DEBUG(SSSDBG_TRACE_ALL, "r->size: %zu\n", r->size); return EOK;
}
@@ -153,12 +191,12 @@ sssd_krb_get_auth_data_fn(const char * pServer,
/*
- This function prepares the gpo_cache by:
- parsing the input_unix_path into its component directories
- parsing the input_smb_path into its component directories
- creating each component directory (if it doesn't already exist)
*/ static errno_t prepare_gpo_cache(TALLOC_CTX *mem_ctx, const char *cache_dir,
const char *input_unix_path)
const char *input_smb_path_with_suffix){ char *current_dir; char *ptr; @@ -167,25 +205,27 @@ static errno_t prepare_gpo_cache(TALLOC_CTX *mem_ctx, int i; char *first = NULL; char *last = NULL;
- char *unix_path = NULL;
- char *smb_path_with_suffix = NULL;
- unix_path = talloc_strdup(mem_ctx, input_unix_path);
- if (unix_path == NULL) {
- smb_path_with_suffix = talloc_strdup(mem_ctx,
input_smb_path_with_suffix);
if (smb_path_with_suffix == NULL) { return ENOMEM; }
DEBUG(SSSDBG_TRACE_ALL, "smb_path_with_suffix: %s\n",
smb_path_with_suffix);
- current_dir = talloc_strdup(mem_ctx, cache_dir); if (current_dir == NULL) { return ENOMEM; }
- ptr = unix_path + 1;
- ptr = smb_path_with_suffix + 1; while ((ptr = strchr(ptr, delim))) { ptr++; num_dirs++; }
- ptr = unix_path + 1;
ptr = smb_path_with_suffix + 1;
for (i = 0; i < num_dirs; i++) { first = ptr;
@@ -210,12 +250,16 @@ static errno_t prepare_gpo_cache(TALLOC_CTX *mem_ctx,
/*
- This function stores the input buf to a local file, whose file path
- is constructed by concatenating GPO_CACHE_PATH to the input unix_path.
- Note that the backend will later read the policy file from the same file
path.
- is constructed by concatenating:
- GPO_CACHE_PATH,
- input smb_path,
- input smb_cse_suffix
- Note that the backend will later read the file from the same file path.
*/ -static errno_t store_bytes_in_gpo_cache(const char *unix_path,
uint8_t *buf,int buflen)+static errno_t gpo_cache_store_file(const char *smb_path,
const char *smb_cse_suffix,uint8_t *buf,int buflen){ int ret; int fd = -1; @@ -223,6 +267,7 @@ static errno_t store_bytes_in_gpo_cache(const char *unix_path, ssize_t written; mode_t old_umask; char *filename = NULL;
char *smb_path_with_suffix = NULL; TALLOC_CTX *tmp_ctx = NULL;
tmp_ctx = talloc_new(NULL);
@@ -231,10 +276,18 @@ static errno_t store_bytes_in_gpo_cache(const char *unix_path, goto done; }
- /* create component directories of unix_path, if needed */
- ret = prepare_gpo_cache(tmp_ctx, GPO_CACHE_PATH, unix_path);
- smb_path_with_suffix =
talloc_asprintf(tmp_ctx, "%s%s", smb_path, smb_cse_suffix);- if (smb_path_with_suffix == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");ret = ENOMEM;goto done;- }
- filename = talloc_asprintf(tmp_ctx, GPO_CACHE_PATH"%s", unix_path);
- /* create component directories of smb_path, if needed */
- ret = prepare_gpo_cache(tmp_ctx, GPO_CACHE_PATH, smb_path_with_suffix);
^^^ return code is ignored. It may cause problems if prepare_gpo_cache fail.OK
- filename = talloc_asprintf(tmp_ctx, GPO_CACHE_PATH"%s",
smb_path_with_suffix); tmp_name = talloc_asprintf(tmp_ctx, "%sXXXXXX", filename); if (tmp_name == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n"); @@ -303,21 +356,132 @@ static errno_t store_bytes_in_gpo_cache(const char *unix_path, return ret; }
+static errno_t +parse_ini_file_with_libini(struct ini_cfgobj *ini_config,
int *_gpt_version)+{
- int ret = 0;
- struct value_obj *vobj = NULL;
- int gpt_version;
- ret = ini_get_config_valueobj(INI_GENERAL_SECTION, GPT_INI_VERSION,
ini_config, INI_GET_FIRST_VALUE, &vobj);- if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,"ini_get_config_valueobj failed [%d][%s]\n", ret,strerror(ret));
goto done;- }
- if (vobj == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "section/name not found: [%s][%s]\n",INI_GENERAL_SECTION, GPT_INI_VERSION);ret = EINVAL;goto done;- }
- gpt_version = ini_get_int32_config_value(vobj, 0, -1, &ret);
- if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,"ini_get_int32_config_value failed [%d][%s]\n",ret, strerror(ret));goto done;- }
- *_gpt_version = gpt_version;
- ret = EOK;
- done:
- return ret;
+}
+/*
- This function parses the GPT_INI file stored in the gpo_cache, and uses
the
- results to populate the output parameters ...
- */
+static errno_t +ad_gpo_parse_ini_file(const char *smb_path,
int *_gpt_version)+{
- struct ini_cfgfile *file_ctx = NULL;
- struct ini_cfgobj *ini_config = NULL;
- const char *ini_filename;
- int ret;
- int gpt_version;
- TALLOC_CTX *tmp_ctx = NULL;
- tmp_ctx = talloc_new(NULL);
- if (tmp_ctx == NULL) {
ret = ENOMEM;goto done;- }
- ini_filename = talloc_asprintf(tmp_ctx, GPO_CACHE_PATH"%s%s",
smb_path, GPT_INI);could you test if allocation was successful?OK. This happens in several other places in ad_gpo_child.c, so I will fix those too.
- DEBUG(SSSDBG_TRACE_FUNC, "ini_filename:%s\n", ini_filename);
- ret = ini_config_create(&ini_config);
- if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,"ini_config_create failed [%d][%s]\n", ret, strerror(ret));goto done;- }
- ret = ini_config_file_open(ini_filename, 0, &file_ctx);
- if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,"ini_config_file_open failed [%d][%s]\n", ret,strerror(ret));
goto done;- }
- ret = ini_config_parse(file_ctx, INI_STOP_ON_NONE, 0, 0, ini_config);
- if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,"ini_config_parse failed [%d][%s]\n", ret, strerror(ret));goto done;- }
- ret = parse_ini_file_with_libini(ini_config, &gpt_version);
- if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,"parse_ini_file_with_libini failed [%d][%s]\n",ret, strerror(ret));goto done;- }
- *_gpt_version = gpt_version;
- done:
- if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "Error encountered: %d.\n", ret);- }
- ini_config_file_destroy(file_ctx);
- ini_config_destroy(ini_config);
- talloc_free(tmp_ctx);
- return ret;
+}
/*
- This cse-specific function (GP_EXT_GUID_SECURITY) opens an SMB
connection,
- retrieves the data referenced by the input smb_uri, and then closes the
SMB
- connection. The data is then written to a file in the GPO_CACHE
directory,
- to be read by the backend.
- This function uses the input smb uri components to download a sysvol
file
- (e.g. INI file, policy file, etc) and store it to the GPO_CACHE
directory. */ static errno_t -process_security_settings_cse(TALLOC_CTX *mem_ctx,
const char *smb_uri,const char *unix_path)+copy_smb_file_to_gpo_cache(SMBCCTX *smbc_ctx,
const char *smb_server,const char *smb_share,const char *smb_path,const char *smb_cse_suffix){
- SMBCCTX *context;
char *smb_uri = NULL;
SMBCFILE *file; int ret = 0; uint8_t *buf = NULL; int buflen = 0;
TALLOC_CTX *tmp_ctx = NULL;
tmp_ctx = talloc_new(NULL);
@@ -325,59 +489,131 @@ process_security_settings_cse(TALLOC_CTX *mem_ctx, return ENOMEM; }
- DEBUG(SSSDBG_TRACE_ALL, "%s\n", smb_uri);
- DEBUG(SSSDBG_TRACE_ALL, "%s\n", unix_path);
- smb_uri = talloc_asprintf(tmp_ctx, "%s%s%s%s", smb_server,
smb_share, smb_path, smb_cse_suffix);- DEBUG(SSSDBG_TRACE_FUNC, "smb_uri: %s\n", smb_uri);
- context = smbc_new_context();
- if (context == NULL) {
- errno = 0;
- file = smbc_getFunctionOpen(smbc_ctx)(smbc_ctx, smb_uri, O_RDONLY,
0755);
- if (file == NULL) {
ret = errno;DEBUG(SSSDBG_CRIT_FAILURE, "smbc_getFunctionOpen failed[%d][%s]\n",
ret, strerror(ret));goto done;- }
- buf = talloc_array(tmp_ctx, uint8_t, SMB_BUFFER_SIZE);
- errno = 0;
- buflen = smbc_getFunctionRead(smbc_ctx)(smbc_ctx, file, buf,
SMB_BUFFER_SIZE);
- if(buflen < 0) {
ret = errno;DEBUG(SSSDBG_CRIT_FAILURE, "smbc_getFunctionRead failed[%d][%s]\n",
ret, strerror(ret));goto done;- }
- DEBUG(SSSDBG_TRACE_ALL, "smb_buflen: %d\n", buflen);
- ret = gpo_cache_store_file(smb_path, smb_cse_suffix, buf, buflen);
- if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,"gpo_cache_store_file failed [%d][%s]\n",ret, strerror(ret));goto done;- }
- done:
- talloc_free(tmp_ctx);
- return ret;
+}
+/*
- Using its smb_uri components and cached_gpt_version inputs, this
function
- does several things:
- it downloads the GPT_INI file to GPO_CACHE
- it parses the sysvol_gpt_version field from the GPT_INI file
- if the sysvol_gpt_version is greater than the cached_gpt_version
- it downloads the policy file to GPO_CACHE
- else
- it doesn't retrieve the policy file
- in this case, the backend will use the existing policy file in
GPO_CACHE
- it returns the sysvol_gpt_version in the _sysvol_gpt_version output
param
- Note that if the cached_gpt_version sent by the backend is -1 (to
indicate
- that no gpt_version has been set in the cache for the corresponding
gpo_guid),
- then the parsed sysvol_gpt_version (which must be at least 0) will be
greater
- than the cached_gpt_version, thereby triggering a fresh download.
- Note that the backend will later do the following:
- backend will save the the sysvol_gpt_version to sysdb cache
- backend will read the policy file from the GPO_CACHE
- */
+static errno_t +perform_smb_operations(int cached_gpt_version,
const char *smb_server,const char *smb_share,const char *smb_path,const char *smb_cse_suffix,int *_sysvol_gpt_version)+{
- SMBCCTX *smbc_ctx;
- int ret = 0;
I don't think we need to initialise ret to zero. From defensive point of view it would be better to init with EINVAL. I think zero can be removed.OK.
- int sysvol_gpt_version;
- smbc_ctx = smbc_new_context();
- if (smbc_ctx == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Could not allocate new smbc context\n"); ret = ENOMEM; goto done; }
- smbc_setFunctionAuthData(context, sssd_krb_get_auth_data_fn);
- smbc_setOptionUseKerberos(context, 1);
smbc_setFunctionAuthData(smbc_ctx, sssd_krb_get_auth_data_fn);
smbc_setOptionUseKerberos(smbc_ctx, 1);
/* Initialize the context using the previously specified options */
- if (smbc_init_context(context) == NULL) {
- if (smbc_init_context(smbc_ctx) == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Could not initialize smbc context\n"); ret = ENOMEM; goto done; }
- /* Tell the compatibility layer to use this context */
- smbc_set_context(context);
- int remotehandle = smbc_open(smb_uri, O_RDONLY, 0755);
- if (remotehandle < 0) {
DEBUG(SSSDBG_CRIT_FAILURE, "smbc_open failed\n");ret = EPIPE;goto done;- }
- buf = talloc_array(tmp_ctx, uint8_t, SMB_BUFFER_SIZE);
- buflen = smbc_read(remotehandle, buf, SMB_BUFFER_SIZE);
- if(buflen < 0) {
DEBUG(SSSDBG_CRIT_FAILURE, "smbc_read failed\n");ret = EPIPE;goto done;- }
- DEBUG(SSSDBG_CRIT_FAILURE, "buflen: %d\n", buflen);
- smbc_close(remotehandle);
- ret = store_bytes_in_gpo_cache(unix_path, buf, buflen);
- /* download ini file */
- ret = copy_smb_file_to_gpo_cache(smbc_ctx, smb_server, smb_share,
smb_path,
if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE,GPT_INI);
"store_bytes_in_gpo_cache failed [%d][%s]\n",
"copy_smb_file_to_gpo_cache failed [%d][%s]\n", ret, strerror(ret)); goto done;}
ret = ad_gpo_parse_ini_file(smb_path, &sysvol_gpt_version);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,"Cannot parse ini file: [%d][%s]\n", ret, strerror(ret));goto done;}
DEBUG(SSSDBG_TRACE_FUNC, "sysvol_gpt_version: %d\n",
sysvol_gpt_version);
- if (sysvol_gpt_version > cached_gpt_version) {
/* download policy file */ret = copy_smb_file_to_gpo_cache(smbc_ctx, smb_server, smb_share,smb_path, smb_cse_suffix);if (ret != EOK) {DEBUG(SSSDBG_CRIT_FAILURE,"copy_smb_file_to_gpo_cache failed [%d][%s]\n",ret, strerror(ret));goto done;}- }
- *_sysvol_gpt_version = sysvol_gpt_version;
done:
- smbc_free_context(context, 0);
- talloc_free(tmp_ctx);
- smbc_free_context(smbc_ctx, 0); return ret;
}
@@ -388,6 +624,7 @@ main(int argc, const char *argv[]) poptContext pc; int debug_fd = -1; errno_t ret;
- int sysvol_gpt_version; int result; TALLOC_CTX *main_ctx = NULL; uint8_t *buf = NULL;
@@ -484,18 +721,21 @@ main(int argc, const char *argv[]) goto fail; }
- DEBUG(SSSDBG_TRACE_FUNC, "processing security settings\n");
- DEBUG(SSSDBG_TRACE_FUNC, "performing smb operations\n");
- result = process_security_settings_cse(main_ctx,
ibuf->smb_uri,ibuf->unix_path);
- result = perform_smb_operations(ibuf->cached_gpt_version,
ibuf->smb_server,ibuf->smb_share,ibuf->smb_path,ibuf->smb_cse_suffix, if (result != EOK) { DEBUG(SSSDBG_CRIT_FAILURE,&sysvol_gpt_version);
"process_security_settings_cse failed.[%d][%s].\n",
"perform_smb_operations failed.[%d][%s].\n", result, strerror(result));missing goto fail.This is a reason of 1st warning. src/providers/ad/ad_gpo_child.c:142:34: error: ‘sysvol_gpt_version’ may be used uninitialized in this function [-Werror=maybe-uninitialized] SAFEALIGN_SET_UINT32(&r->buf[p], sysvol_gpt_version, &p); ^ src/providers/ad/ad_gpo_child.c:627:9: note: ‘sysvol_gpt_version’ was declared here int sysvol_gpt_version;
}There is also another gcc warning. It is only visible with -O1 or higher. The same warning is with ferao 20/rawhide and rhel6
src/providers/ad/ad_gpo_child.c: In function ‘main’: src/providers/ad/ad_gpo_child.c:142:34: error: ‘gpt_version’ may be used uninitialized in this function [-Werror=maybe-uninitialized] SAFEALIGN_SET_UINT32(&r->buf[p], sysvol_gpt_version, &p); ^ src/providers/ad/ad_gpo_child.c:413:9: note: ‘gpt_version’ was declared here int gpt_version; ^ cc1: all warnings being treated as errors
There are two possibel solutions: a) explicit initialisation to unexpected value. --- a/src/providers/ad/ad_gpo_child.c +++ b/src/providers/ad/ad_gpo_child.c @@ -410,7 +410,7 @@ ad_gpo_parse_ini_file(const char *smb_path, struct ini_cfgobj *ini_config = NULL; const char *ini_filename; int ret;
- int gpt_version;
- int gpt_version = -1; TALLOC_CTX *tmp_ctx = NULL;
b) disable inlining of parse_ini_file_with_libini. --- a/src/providers/ad/ad_gpo_child.c +++ b/src/providers/ad/ad_gpo_child.c @@ -359,7 +359,7 @@ static errno_t gpo_cache_store_file(const char *smb_path, return ret; }
-static errno_t +errno_t parse_ini_file_with_libini(struct ini_cfgobj *ini_config, int *_gpt_version) {
LS _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
I will initialize gpt_version to -1, as that seems to remove the warning.
However, I am a little confused. My understanding of whyi the sysvol_gpt_version warning is occurring is that perform_smb_operations() might fail. Since I don't have a "goto fail" in the error path, prepare_response() might use an uninitialized value. Adding the "goto fail" makes this warning go away (as you mentioned).
However, I don't understand why the gpt_version warning is occuring. Even though gpt_version is uninitialized in ad_gpo_parse_ini_file, the value of gpt_version is only being used if ad_gpo_parse_ini_file returns EOK. By the time gpt_version is used in pack_buffer, it should definitely be initialized, no?
It have to be some hidden gcc optimization, because disabling inlining function parse_ini_file_with_libini will solve problem (either with -O2 or with removing static modifier). clang does not do this kind of optimization, because there isn't such warning. I checked code flow few times and it should never happen. You properly checked return codes (ret != 0).
On the other side, compiler never lies and sssd is built in koji with gcc and -O2
LS
----- Original Message -----
On (23/07/14 03:14), Yassir Elley wrote:
----- Original Message -----
On (22/07/14 03:13), Yassir Elley wrote:
On Sun, Jul 20, 2014 at 05:48:45PM -0400, Yassir Elley wrote:
Hi,
The attached patch adds support for gpo version checking and for conditional downloading of policy files. In order to get that to work, this patch also replaces the deprecated smb functions (e.g. smbc_open, smbc_read, etc) I had been using (not knowing they were deprecated) with the newer smb functions, such as "smbc_getFunctionOpen(smbc_ctx)(smbc_ctx, smb_uri, O_RDONLY, 0755);".
Before this patch, the policy files were being unconditionally downloaded every time. After this patch, the backend sends the cached_gpt_version (from a previous transaction, if any) to the gpo_child. The gpo_child downloads the per-GPO GPT.INI file, from which it extracts the sysvol_gpt_version. Only if the sysvol_gpt_version is greater than the cached_gpt_version (or if there is no cached_gpt_version) does the gpo_child download the policy files. The gpo_child returns the sysvol_gpt_version to the backend, which stores it in the cache.
Note that, while a GPO has both a SYSVOL-stored Group Policy Template (gpt) version, as well as an LDAP-stored Group Policy Container (gpc) version (which should be equal), it is best practice to use the gpt version for version checking b/c SYSVOL replication is typically much faster than AD replication. In other words, the gpt version is quickly up-to-date on all DCs for a given domain, while the gpc version may be stale for some time after a change is made.
In order to keep the gpo_child as simple as possible, the gpo_child does not interact with the sysdb cache at all (only the backend does). However, the gpo_child does need to parse the GPT.INI file in order to determine whether to download the policy files. As such, the gpo_child includes GPT.INI file parsing functionality (which is per-GPO, and not CSE-specific).
Note that this patch does not add support for offline mode, which will be implemented in a subsequent patch.
Regards, Yassir.
Hi Jakub,
I agree with all of your comments so far and have attached revised patches to this email. I split the original patch into two patches, as you suggested, and I addressed your review comments in the revised patches. Thanks for the review! Keep the comments coming! :)
Regards, Yassir.
From f3f00a78102ad5e389266b28c43d0396caf57592 Mon Sep 17 00:00:00 2001 From: Yassir Elley yelley@redhat.com Date: Tue, 22 Jul 2014 03:03:03 -0400 Subject: [PATCH 2/2] AD-GPO: only download policy files if gpo version changes
src/providers/ad/ad_gpo.c | 306 +++++++++++++++++++----------- src/providers/ad/ad_gpo_child.c | 404 ++++++++++++++++++++++++++++++++-------- 2 files changed, 517 insertions(+), 193 deletions(-)
I have few comments to the ad_gpo_child
diff --git a/src/providers/ad/ad_gpo_child.c b/src/providers/ad/ad_gpo_child.c index 22b8b52046b6298f2eeaf425cef7bf0271865fe2..93ae592e591eefe812212cc99ddbec70c4e0aba9 100644 --- a/src/providers/ad/ad_gpo_child.c +++ b/src/providers/ad/ad_gpo_child.c @@ -27,6 +27,7 @@ #include <sys/stat.h> #include <popt.h> #include <libsmbclient.h> +#include <ini_configobj.h> #include <security/pam_modules.h>
#include "util/util.h" @@ -34,14 +35,17 @@ #include "providers/dp_backend.h" #include "sss_cli.h"
-#define RIGHTS_SECTION "Privilege Rights" -#define ALLOW_LOGON_LOCALLY "SeInteractiveLogonRight" -#define DENY_LOGON_LOCALLY "SeDenyInteractiveLogonRight" #define SMB_BUFFER_SIZE 65536 +#define GPT_INI "/GPT.INI" +#define INI_GENERAL_SECTION "General" +#define GPT_INI_VERSION "Version"
struct input_buffer {
- const char *smb_uri;
- const char *unix_path;
- int cached_gpt_version;
- const char *smb_server;
- const char *smb_share;
- const char *smb_path;
- const char *smb_cse_suffix;
};
static errno_t @@ -51,34 +55,62 @@ unpack_buffer(uint8_t *buf, { size_t p = 0; uint32_t len;
- uint32_t cached_gpt_version;
- /* smb_uri size and length */
- SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
- DEBUG(SSSDBG_TRACE_ALL, "smb_uri size: %d\n", len);
- /* cached_gpt_version */
- SAFEALIGN_COPY_UINT32_CHECK(&cached_gpt_version, buf + p, size, &p);
- DEBUG(SSSDBG_TRACE_FUNC, "cached_gpt_version: %d\n",
cached_gpt_version);
ibuf->cached_gpt_version = cached_gpt_version;
/* smb_server */
SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
DEBUG(SSSDBG_TRACE_ALL, "smb_server length: %d\n", len); if (len == 0) { return EINVAL; } else { if ((p + len ) > size) return EINVAL;
ibuf->smb_uri = talloc_strndup(ibuf, (char *)(buf + p), len);if (ibuf->smb_uri == NULL) return ENOMEM;DEBUG(SSSDBG_TRACE_ALL, "got smb_uri: %s\n", ibuf->smb_uri);
ibuf->smb_server = talloc_strndup(ibuf, (char *)(buf + p), len);if (ibuf->smb_server == NULL) return ENOMEM; }DEBUG(SSSDBG_TRACE_ALL, "smb_server: %s\n", ibuf->smb_server); p += len;
- /* unix_path size and length */
- /* smb_share */ SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
- DEBUG(SSSDBG_TRACE_ALL, "smb_share length: %d\n", len);
- if (len == 0) {
return EINVAL;- } else {
if ((p + len ) > size) return EINVAL;ibuf->smb_share = talloc_strndup(ibuf, (char *)(buf + p), len);if (ibuf->smb_share == NULL) return ENOMEM;DEBUG(SSSDBG_TRACE_ALL, "smb_share: %s\n", ibuf->smb_share);p += len;- }
- DEBUG(SSSDBG_TRACE_ALL, "unix_path size: %d\n", len);
/* smb_path */
SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
DEBUG(SSSDBG_TRACE_ALL, "smb_path length: %d\n", len);
if (len == 0) {
return EINVAL;} else {
if ((p + len ) > size) return EINVAL;ibuf->smb_path = talloc_strndup(ibuf, (char *)(buf + p), len);if (ibuf->smb_path == NULL) return ENOMEM;DEBUG(SSSDBG_TRACE_ALL, "smb_path: %s\n", ibuf->smb_path);p += len;}
/* smb_cse_suffix */
SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
DEBUG(SSSDBG_TRACE_ALL, "smb_cse_suffix length: %d\n", len); if (len == 0) { return EINVAL; } else { if ((p + len ) > size) return EINVAL;
ibuf->unix_path = talloc_strndup(ibuf, (char *)(buf + p), len);if (ibuf->unix_path == NULL) return ENOMEM;DEBUG(SSSDBG_TRACE_ALL, "got unix_path: %s\n", ibuf->unix_path);
ibuf->smb_cse_suffix = talloc_strndup(ibuf, (char *)(buf + p),len);
if (ibuf->smb_cse_suffix == NULL) return ENOMEM;DEBUG(SSSDBG_TRACE_ALL, "smb_cse_suffix: %s\n",ibuf->smb_cse_suffix); p += len; }
@@ -88,14 +120,16 @@ unpack_buffer(uint8_t *buf,
static errno_t pack_buffer(struct response *r,
int sysvol_gpt_version, int result){ size_t p = 0;
/* A buffer with the following structure must be created:
* uint32_t sysvol_gpt_version (required)*/
- uint32_t status of the request (required)
- r->size = sizeof(uint32_t);
r->size = 2 * sizeof(uint32_t);
r->buf = talloc_array(r, uint8_t, r->size); if(r->buf == NULL) {
@@ -104,6 +138,9 @@ pack_buffer(struct response *r,
DEBUG(SSSDBG_TRACE_FUNC, "result [%d]\n", result);
- /* sysvol_gpt_version */
- SAFEALIGN_SET_UINT32(&r->buf[p], sysvol_gpt_version, &p);
- /* result */ SAFEALIGN_SET_UINT32(&r->buf[p], result, &p);
@@ -112,6 +149,7 @@ pack_buffer(struct response *r,
static errno_t prepare_response(TALLOC_CTX *mem_ctx,
int sysvol_gpt_version, int result, struct response **rsp){ @@ -126,14 +164,14 @@ prepare_response(TALLOC_CTX *mem_ctx, r->buf = NULL; r->size = 0;
- ret = pack_buffer(r, result);
ret = pack_buffer(r, sysvol_gpt_version, result); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "pack_buffer failed\n"); return ret; }
*rsp = r;
- DEBUG(SSSDBG_TRACE_FUNC, "r->size: %zu\n", r->size);
- DEBUG(SSSDBG_TRACE_ALL, "r->size: %zu\n", r->size); return EOK;
}
@@ -153,12 +191,12 @@ sssd_krb_get_auth_data_fn(const char * pServer,
/*
- This function prepares the gpo_cache by:
- parsing the input_unix_path into its component directories
- parsing the input_smb_path into its component directories
- creating each component directory (if it doesn't already exist)
*/ static errno_t prepare_gpo_cache(TALLOC_CTX *mem_ctx, const char *cache_dir,
const char *input_unix_path)
const char *input_smb_path_with_suffix){ char *current_dir; char *ptr; @@ -167,25 +205,27 @@ static errno_t prepare_gpo_cache(TALLOC_CTX *mem_ctx, int i; char *first = NULL; char *last = NULL;
- char *unix_path = NULL;
- char *smb_path_with_suffix = NULL;
- unix_path = talloc_strdup(mem_ctx, input_unix_path);
- if (unix_path == NULL) {
- smb_path_with_suffix = talloc_strdup(mem_ctx,
input_smb_path_with_suffix);
if (smb_path_with_suffix == NULL) { return ENOMEM; }
DEBUG(SSSDBG_TRACE_ALL, "smb_path_with_suffix: %s\n",
smb_path_with_suffix);
- current_dir = talloc_strdup(mem_ctx, cache_dir); if (current_dir == NULL) { return ENOMEM; }
- ptr = unix_path + 1;
- ptr = smb_path_with_suffix + 1; while ((ptr = strchr(ptr, delim))) { ptr++; num_dirs++; }
- ptr = unix_path + 1;
ptr = smb_path_with_suffix + 1;
for (i = 0; i < num_dirs; i++) { first = ptr;
@@ -210,12 +250,16 @@ static errno_t prepare_gpo_cache(TALLOC_CTX *mem_ctx,
/*
- This function stores the input buf to a local file, whose file path
- is constructed by concatenating GPO_CACHE_PATH to the input
unix_path.
- Note that the backend will later read the policy file from the same
file path.
- is constructed by concatenating:
- GPO_CACHE_PATH,
- input smb_path,
- input smb_cse_suffix
- Note that the backend will later read the file from the same file
path. */ -static errno_t store_bytes_in_gpo_cache(const char *unix_path,
uint8_t *buf,int buflen)+static errno_t gpo_cache_store_file(const char *smb_path,
const char *smb_cse_suffix,uint8_t *buf,int buflen){ int ret; int fd = -1; @@ -223,6 +267,7 @@ static errno_t store_bytes_in_gpo_cache(const char *unix_path, ssize_t written; mode_t old_umask; char *filename = NULL;
char *smb_path_with_suffix = NULL; TALLOC_CTX *tmp_ctx = NULL;
tmp_ctx = talloc_new(NULL);
@@ -231,10 +276,18 @@ static errno_t store_bytes_in_gpo_cache(const char *unix_path, goto done; }
- /* create component directories of unix_path, if needed */
- ret = prepare_gpo_cache(tmp_ctx, GPO_CACHE_PATH, unix_path);
- smb_path_with_suffix =
talloc_asprintf(tmp_ctx, "%s%s", smb_path, smb_cse_suffix);- if (smb_path_with_suffix == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");ret = ENOMEM;goto done;- }
- filename = talloc_asprintf(tmp_ctx, GPO_CACHE_PATH"%s", unix_path);
- /* create component directories of smb_path, if needed */
- ret = prepare_gpo_cache(tmp_ctx, GPO_CACHE_PATH,
smb_path_with_suffix);
^^^ return code is ignored. It may cause problems if prepare_gpo_cache fail.OK
- filename = talloc_asprintf(tmp_ctx, GPO_CACHE_PATH"%s",
smb_path_with_suffix); tmp_name = talloc_asprintf(tmp_ctx, "%sXXXXXX", filename); if (tmp_name == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n"); @@ -303,21 +356,132 @@ static errno_t store_bytes_in_gpo_cache(const char *unix_path, return ret; }
+static errno_t +parse_ini_file_with_libini(struct ini_cfgobj *ini_config,
int *_gpt_version)+{
- int ret = 0;
- struct value_obj *vobj = NULL;
- int gpt_version;
- ret = ini_get_config_valueobj(INI_GENERAL_SECTION, GPT_INI_VERSION,
ini_config, INI_GET_FIRST_VALUE,&vobj);
- if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,"ini_get_config_valueobj failed [%d][%s]\n", ret,strerror(ret));
goto done;- }
- if (vobj == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "section/name not found: [%s][%s]\n",INI_GENERAL_SECTION, GPT_INI_VERSION);ret = EINVAL;goto done;- }
- gpt_version = ini_get_int32_config_value(vobj, 0, -1, &ret);
- if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,"ini_get_int32_config_value failed [%d][%s]\n",ret, strerror(ret));goto done;- }
- *_gpt_version = gpt_version;
- ret = EOK;
- done:
- return ret;
+}
+/*
- This function parses the GPT_INI file stored in the gpo_cache, and
uses the
- results to populate the output parameters ...
- */
+static errno_t +ad_gpo_parse_ini_file(const char *smb_path,
int *_gpt_version)+{
- struct ini_cfgfile *file_ctx = NULL;
- struct ini_cfgobj *ini_config = NULL;
- const char *ini_filename;
- int ret;
- int gpt_version;
- TALLOC_CTX *tmp_ctx = NULL;
- tmp_ctx = talloc_new(NULL);
- if (tmp_ctx == NULL) {
ret = ENOMEM;goto done;- }
- ini_filename = talloc_asprintf(tmp_ctx, GPO_CACHE_PATH"%s%s",
smb_path, GPT_INI);could you test if allocation was successful?OK. This happens in several other places in ad_gpo_child.c, so I will fix those too.
- DEBUG(SSSDBG_TRACE_FUNC, "ini_filename:%s\n", ini_filename);
- ret = ini_config_create(&ini_config);
- if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,"ini_config_create failed [%d][%s]\n", ret,strerror(ret));
goto done;- }
- ret = ini_config_file_open(ini_filename, 0, &file_ctx);
- if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,"ini_config_file_open failed [%d][%s]\n", ret,strerror(ret));
goto done;- }
- ret = ini_config_parse(file_ctx, INI_STOP_ON_NONE, 0, 0,
ini_config);
- if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,"ini_config_parse failed [%d][%s]\n", ret, strerror(ret));goto done;- }
- ret = parse_ini_file_with_libini(ini_config, &gpt_version);
- if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,"parse_ini_file_with_libini failed [%d][%s]\n",ret, strerror(ret));goto done;- }
- *_gpt_version = gpt_version;
- done:
- if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "Error encountered: %d.\n", ret);- }
- ini_config_file_destroy(file_ctx);
- ini_config_destroy(ini_config);
- talloc_free(tmp_ctx);
- return ret;
+}
/*
- This cse-specific function (GP_EXT_GUID_SECURITY) opens an SMB
connection,
- retrieves the data referenced by the input smb_uri, and then closes
the SMB
- connection. The data is then written to a file in the GPO_CACHE
directory,
- to be read by the backend.
- This function uses the input smb uri components to download a sysvol
file
- (e.g. INI file, policy file, etc) and store it to the GPO_CACHE
directory. */ static errno_t -process_security_settings_cse(TALLOC_CTX *mem_ctx,
const char *smb_uri,const char *unix_path)+copy_smb_file_to_gpo_cache(SMBCCTX *smbc_ctx,
const char *smb_server,const char *smb_share,const char *smb_path,const char *smb_cse_suffix){
- SMBCCTX *context;
char *smb_uri = NULL;
SMBCFILE *file; int ret = 0; uint8_t *buf = NULL; int buflen = 0;
TALLOC_CTX *tmp_ctx = NULL;
tmp_ctx = talloc_new(NULL);
@@ -325,59 +489,131 @@ process_security_settings_cse(TALLOC_CTX *mem_ctx, return ENOMEM; }
- DEBUG(SSSDBG_TRACE_ALL, "%s\n", smb_uri);
- DEBUG(SSSDBG_TRACE_ALL, "%s\n", unix_path);
- smb_uri = talloc_asprintf(tmp_ctx, "%s%s%s%s", smb_server,
smb_share, smb_path, smb_cse_suffix);- DEBUG(SSSDBG_TRACE_FUNC, "smb_uri: %s\n", smb_uri);
- context = smbc_new_context();
- if (context == NULL) {
- errno = 0;
- file = smbc_getFunctionOpen(smbc_ctx)(smbc_ctx, smb_uri, O_RDONLY,
0755);
- if (file == NULL) {
ret = errno;DEBUG(SSSDBG_CRIT_FAILURE, "smbc_getFunctionOpen failed[%d][%s]\n",
ret, strerror(ret));goto done;- }
- buf = talloc_array(tmp_ctx, uint8_t, SMB_BUFFER_SIZE);
- errno = 0;
- buflen = smbc_getFunctionRead(smbc_ctx)(smbc_ctx, file, buf,
SMB_BUFFER_SIZE);
- if(buflen < 0) {
ret = errno;DEBUG(SSSDBG_CRIT_FAILURE, "smbc_getFunctionRead failed[%d][%s]\n",
ret, strerror(ret));goto done;- }
- DEBUG(SSSDBG_TRACE_ALL, "smb_buflen: %d\n", buflen);
- ret = gpo_cache_store_file(smb_path, smb_cse_suffix, buf, buflen);
- if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,"gpo_cache_store_file failed [%d][%s]\n",ret, strerror(ret));goto done;- }
- done:
- talloc_free(tmp_ctx);
- return ret;
+}
+/*
- Using its smb_uri components and cached_gpt_version inputs, this
function
- does several things:
- it downloads the GPT_INI file to GPO_CACHE
- it parses the sysvol_gpt_version field from the GPT_INI file
- if the sysvol_gpt_version is greater than the cached_gpt_version
- it downloads the policy file to GPO_CACHE
- else
- it doesn't retrieve the policy file
- in this case, the backend will use the existing policy file in
GPO_CACHE
- it returns the sysvol_gpt_version in the _sysvol_gpt_version output
param
- Note that if the cached_gpt_version sent by the backend is -1 (to
indicate
- that no gpt_version has been set in the cache for the corresponding
gpo_guid),
- then the parsed sysvol_gpt_version (which must be at least 0) will be
greater
- than the cached_gpt_version, thereby triggering a fresh download.
- Note that the backend will later do the following:
- backend will save the the sysvol_gpt_version to sysdb cache
- backend will read the policy file from the GPO_CACHE
- */
+static errno_t +perform_smb_operations(int cached_gpt_version,
const char *smb_server,const char *smb_share,const char *smb_path,const char *smb_cse_suffix,int *_sysvol_gpt_version)+{
- SMBCCTX *smbc_ctx;
- int ret = 0;
I don't think we need to initialise ret to zero. From defensive point of view it would be better to init with EINVAL. I think zero can be removed.OK.
- int sysvol_gpt_version;
- smbc_ctx = smbc_new_context();
- if (smbc_ctx == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Could not allocate new smbc context\n"); ret = ENOMEM; goto done; }
- smbc_setFunctionAuthData(context, sssd_krb_get_auth_data_fn);
- smbc_setOptionUseKerberos(context, 1);
smbc_setFunctionAuthData(smbc_ctx, sssd_krb_get_auth_data_fn);
smbc_setOptionUseKerberos(smbc_ctx, 1);
/* Initialize the context using the previously specified options */
- if (smbc_init_context(context) == NULL) {
- if (smbc_init_context(smbc_ctx) == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Could not initialize smbc context\n"); ret = ENOMEM; goto done; }
- /* Tell the compatibility layer to use this context */
- smbc_set_context(context);
- int remotehandle = smbc_open(smb_uri, O_RDONLY, 0755);
- if (remotehandle < 0) {
DEBUG(SSSDBG_CRIT_FAILURE, "smbc_open failed\n");ret = EPIPE;goto done;- }
- buf = talloc_array(tmp_ctx, uint8_t, SMB_BUFFER_SIZE);
- buflen = smbc_read(remotehandle, buf, SMB_BUFFER_SIZE);
- if(buflen < 0) {
DEBUG(SSSDBG_CRIT_FAILURE, "smbc_read failed\n");ret = EPIPE;goto done;- }
- DEBUG(SSSDBG_CRIT_FAILURE, "buflen: %d\n", buflen);
- smbc_close(remotehandle);
- ret = store_bytes_in_gpo_cache(unix_path, buf, buflen);
- /* download ini file */
- ret = copy_smb_file_to_gpo_cache(smbc_ctx, smb_server, smb_share,
smb_path,
if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE,GPT_INI);
"store_bytes_in_gpo_cache failed [%d][%s]\n",
"copy_smb_file_to_gpo_cache failed [%d][%s]\n", ret, strerror(ret)); goto done;}
ret = ad_gpo_parse_ini_file(smb_path, &sysvol_gpt_version);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,"Cannot parse ini file: [%d][%s]\n", ret, strerror(ret));goto done;}
DEBUG(SSSDBG_TRACE_FUNC, "sysvol_gpt_version: %d\n",
sysvol_gpt_version);
- if (sysvol_gpt_version > cached_gpt_version) {
/* download policy file */ret = copy_smb_file_to_gpo_cache(smbc_ctx, smb_server,smb_share,
smb_path, smb_cse_suffix);if (ret != EOK) {DEBUG(SSSDBG_CRIT_FAILURE,"copy_smb_file_to_gpo_cache failed [%d][%s]\n",ret, strerror(ret));goto done;}- }
- *_sysvol_gpt_version = sysvol_gpt_version;
done:
- smbc_free_context(context, 0);
- talloc_free(tmp_ctx);
- smbc_free_context(smbc_ctx, 0); return ret;
}
@@ -388,6 +624,7 @@ main(int argc, const char *argv[]) poptContext pc; int debug_fd = -1; errno_t ret;
- int sysvol_gpt_version; int result; TALLOC_CTX *main_ctx = NULL; uint8_t *buf = NULL;
@@ -484,18 +721,21 @@ main(int argc, const char *argv[]) goto fail; }
- DEBUG(SSSDBG_TRACE_FUNC, "processing security settings\n");
- DEBUG(SSSDBG_TRACE_FUNC, "performing smb operations\n");
- result = process_security_settings_cse(main_ctx,
ibuf->smb_uri,ibuf->unix_path);
- result = perform_smb_operations(ibuf->cached_gpt_version,
ibuf->smb_server,ibuf->smb_share,ibuf->smb_path,ibuf->smb_cse_suffix, if (result != EOK) { DEBUG(SSSDBG_CRIT_FAILURE,&sysvol_gpt_version);
"process_security_settings_cse failed.[%d][%s].\n",
"perform_smb_operations failed.[%d][%s].\n", result, strerror(result));missing goto fail.This is a reason of 1st warning. src/providers/ad/ad_gpo_child.c:142:34: error: ‘sysvol_gpt_version’ may be used uninitialized in this function [-Werror=maybe-uninitialized] SAFEALIGN_SET_UINT32(&r->buf[p], sysvol_gpt_version, &p); ^ src/providers/ad/ad_gpo_child.c:627:9: note: ‘sysvol_gpt_version’ was declared here int sysvol_gpt_version;
}There is also another gcc warning. It is only visible with -O1 or higher. The same warning is with ferao 20/rawhide and rhel6
src/providers/ad/ad_gpo_child.c: In function ‘main’: src/providers/ad/ad_gpo_child.c:142:34: error: ‘gpt_version’ may be used uninitialized in this function [-Werror=maybe-uninitialized] SAFEALIGN_SET_UINT32(&r->buf[p], sysvol_gpt_version, &p); ^ src/providers/ad/ad_gpo_child.c:413:9: note: ‘gpt_version’ was declared here int gpt_version; ^ cc1: all warnings being treated as errors
There are two possibel solutions: a) explicit initialisation to unexpected value. --- a/src/providers/ad/ad_gpo_child.c +++ b/src/providers/ad/ad_gpo_child.c @@ -410,7 +410,7 @@ ad_gpo_parse_ini_file(const char *smb_path, struct ini_cfgobj *ini_config = NULL; const char *ini_filename; int ret;
- int gpt_version;
- int gpt_version = -1; TALLOC_CTX *tmp_ctx = NULL;
b) disable inlining of parse_ini_file_with_libini. --- a/src/providers/ad/ad_gpo_child.c +++ b/src/providers/ad/ad_gpo_child.c @@ -359,7 +359,7 @@ static errno_t gpo_cache_store_file(const char *smb_path, return ret; }
-static errno_t +errno_t parse_ini_file_with_libini(struct ini_cfgobj *ini_config, int *_gpt_version) {
LS _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
I will initialize gpt_version to -1, as that seems to remove the warning.
However, I am a little confused. My understanding of whyi the sysvol_gpt_version warning is occurring is that perform_smb_operations() might fail. Since I don't have a "goto fail" in the error path, prepare_response() might use an uninitialized value. Adding the "goto fail" makes this warning go away (as you mentioned).
However, I don't understand why the gpt_version warning is occuring. Even though gpt_version is uninitialized in ad_gpo_parse_ini_file, the value of gpt_version is only being used if ad_gpo_parse_ini_file returns EOK. By the time gpt_version is used in pack_buffer, it should definitely be initialized, no?
It have to be some hidden gcc optimization, because disabling inlining function parse_ini_file_with_libini will solve problem (either with -O2 or with removing static modifier). clang does not do this kind of optimization, because there isn't such warning. I checked code flow few times and it should never happen. You properly checked return codes (ret != 0).
On the other side, compiler never lies and sssd is built in koji with gcc and -O2
LS _______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
OK. I initialized gpt_version to -1 in the previous patch and it is compiling without warnings with -O1.
Yassir.
sssd-devel@lists.fedorahosted.org