From 97c5dc7b91f743fa81680c62f848c6fa40bcc9cb Mon Sep 17 00:00:00 2001 From: David Mulder Date: Tue, 10 Apr 2018 15:04:53 -0600 Subject: [PATCH 1/2] libgpo: Add python bindings for check_refresh_gpo_list Signed-off-by: David Mulder --- libgpo/pygpo.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/libgpo/pygpo.c b/libgpo/pygpo.c index db336021125..8890fbc6d01 100644 --- a/libgpo/pygpo.c +++ b/libgpo/pygpo.c @@ -289,6 +289,97 @@ static PyObject* py_ads_connect(ADS *self) /* Parameter mapping and functions for the GP_EXT struct */ void initgpo(void); +static PyTypeObject ads_ADSType; + +static PyObject *py_check_refresh_gpo_list(PyObject * self, + PyObject * args) +{ + TALLOC_CTX *frame = talloc_stackframe(); + ADS *ads = NULL; + const char *cache_dir = NULL; + struct GROUP_POLICY_OBJECT *gpo_front = NULL; + struct GROUP_POLICY_OBJECT *gpo_ptr = NULL; + PyObject *gpo_list = NULL; + PyObject *gpo_obj = NULL; + NTSTATUS status; + PyObject *ret = NULL; + Py_ssize_t gp_list_len = 0; + int success = 0; + int i; + + if (!PyArg_ParseTuple(args, "OO|s", &ads, &gpo_list, &cache_dir)) { + goto out; + } + success = PyObject_TypeCheck(gpo_list, &PyList_Type); + if (!success) { + PyErr_SetString(PyExc_TypeError, "A gpo list was expected"); + goto out; + } + gp_list_len = PyList_Size(gpo_list); + if (gp_list_len == 0) { + ret = Py_True; + goto out; + } + for (i = 0; i < gp_list_len; i++) { + struct GROUP_POLICY_OBJECT *gpo = NULL; + + gpo_obj = PyList_GetItem(gpo_list, i); + if (!gpo_obj) { + goto out; + } + + success = PyObject_TypeCheck(gpo_obj, &GPOType); + if (!success) { + PyErr_SetString(PyExc_TypeError, + "A gpo type was expected"); + goto out; + } + gpo = (struct GROUP_POLICY_OBJECT *)pytalloc_get_ptr(gpo_obj); + if (gpo_ptr) { + gpo_ptr->next = talloc_memdup(frame, gpo, + sizeof(struct GROUP_POLICY_OBJECT)); + gpo_ptr->next->prev = gpo_ptr; + gpo_ptr = gpo_ptr->next; + } else { + gpo_ptr = talloc_memdup(frame, gpo, + sizeof(struct GROUP_POLICY_OBJECT)); + gpo_front = gpo_ptr; + } + } + if (gpo_ptr != gpo_front) { + gpo_ptr->next = gpo_front; + } else { + gpo_ptr->next = NULL; + } + + success = PyObject_TypeCheck(ads, &ads_ADSType); + if (!success) { + PyErr_SetString(PyExc_TypeError, "An ADS type was expected"); + goto out; + } + + if (!cache_dir) { + cache_dir = cache_path(GPO_CACHE_DIR); + if (!cache_dir) { + PyErr_SetString(PyExc_MemoryError, + "Failed to determine gpo cache dir"); + goto out; + } + } + + status = check_refresh_gpo_list(ads->ads_ptr, frame, cache_dir, 0, + gpo_front); + if (!NT_STATUS_IS_OK(status)) { + PyErr_SetNTSTATUS(status); + goto out; + } + + ret = Py_True; +out: + TALLOC_FREE(frame); + return ret; +} + /* Global methods aka do not need a special pyobject type */ static PyObject *py_gpo_get_sysvol_gpt_version(PyObject * self, PyObject * args) @@ -496,6 +587,9 @@ static PyMethodDef py_gpo_methods[] = { {"gpo_get_sysvol_gpt_version", (PyCFunction)py_gpo_get_sysvol_gpt_version, METH_VARARGS, NULL}, + {"check_refresh_gpo_list", + (PyCFunction)py_check_refresh_gpo_list, + METH_VARARGS, NULL}, {NULL} }; From 8f4c4b98813ceb2cd7bef306a8948bdd99609cd2 Mon Sep 17 00:00:00 2001 From: David Mulder Date: Mon, 8 Jan 2018 07:17:29 -0700 Subject: [PATCH 2/2] gpo: Read GPO versions locally, not from sysvol This patch does not change current functionality for the kdc. Non-kdc clients cannot read directly from the sysvol, so we need to store the GPT.INI file locally to read each gpo version. Signed-off-by: David Mulder --- python/samba/gpclass.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/python/samba/gpclass.py b/python/samba/gpclass.py index 0966611b686..b9c376bf75a 100644 --- a/python/samba/gpclass.py +++ b/python/samba/gpclass.py @@ -421,7 +421,18 @@ def get_gpo_list(dc_hostname, creds, lp): ads = gpo.ADS_STRUCT(dc_hostname, lp, creds) if ads.connect(): gpos = ads.get_gpo_list(creds.get_username()) - return gpos + return (ads, gpos) + +def gpo_version(lp, path, sysvol): + # gpo.gpo_get_sysvol_gpt_version() reads the GPT.INI from a local file. + # If we don't have a sysvol path locally (if we're not a kdc), then + # read from the gpo client cache. + if sysvol: + local_path = os.path.join(sysvol, path, 'GPT.INI') + else: + gpt_path = lp.cache_path(os.path.join('gpo_cache', path)) + local_path = os.path.join(gpt_path, 'GPT.INI') + return int(gpo.gpo_get_sysvol_gpt_version(os.path.dirname(local_path))[1]) def apply_gp(lp, creds, test_ldb, logger, store, gp_extensions): gp_db = store.get_gplog(creds.get_username()) @@ -431,15 +442,17 @@ def apply_gp(lp, creds, test_ldb, logger, store, gp_extensions): except: logger.error('Error connecting to \'%s\' using SMB' % dc_hostname) raise - gpos = get_gpo_list(dc_hostname, creds, lp) + ads, gpos = get_gpo_list(dc_hostname, creds, lp) + sysvol = lp.get("path", "sysvol") + if not sysvol: + gpo.check_refresh_gpo_list(ads, gpos) for gpo_obj in gpos: guid = gpo_obj.name if guid == 'Local Policy': continue path = os.path.join(lp.get('realm').lower(), 'Policies', guid) - local_path = os.path.join(lp.get("path", "sysvol"), path) - version = int(gpo.gpo_get_sysvol_gpt_version(local_path)[1]) + version = gpo_version(lp, path, sysvol) if version != store.get_int(guid): logger.info('GPO %s has changed' % guid) gp_db.state(GPOSTATE.APPLY)