[PATCH 2/8] Initial commit for GPO work done by Luke Morrison

David Mulder dmulder at suse.com
Mon Jan 30 15:17:14 UTC 2017


From: Luke Morrison <luc785 at hotmail.com>

Enclosed is my Summer of Code 2013 patch to have vital password GPO always applied to the Samba4 Domain Controller using a GPO update service.

To try it out "make -j" your samba with the patch, apply a security password GPO and see the difference in ~20 seconds. It also takes GPO hierarchy into account. Informant is a test script, run like :

sudo python ./source4/scripting/bin/informant -s /home/...smb.conf

Can someone please do a review on this please and get back to me at either this email or luc785 at hotmail.com?

Thanks again to everyone who helped me with my project last summer. And Matthieu, great mentoring!

Kind Regards

Luke Morrison
Software Engineering
Carleton University

Signed-off-by: Garming Sam <garming at catalyst.net.nz>
Signed-off-by: Luke Morrison <luke at hubtrek.com>
---
 docs-xml/smbdotconf/domain/gpoupdatecommand.xml |  14 +
 lib/param/loadparm.c                            |   3 +-
 {source3/libgpo => libgpo}/gpo_filesync.c       |   0
 {source3/libgpo => libgpo}/gpo_proto.h          |   0
 {source3/libgpo => libgpo}/gpo_reg.c            |   0
 libgpo/pygpo.c                                  | 644 ++++++++++++++++++++++++
 libgpo/pygpo.h                                  |  53 ++
 libgpo/wscript_build                            |  15 +-
 pygpo.h                                         |   8 +
 python/samba/gpclass.py                         | 374 ++++++++++++++
 python/samba/samdb.py                           |  18 +
 source3/libgpo/gpext/wscript_build              |   4 +
 source4/dsdb/gpo/gpo_update.c                   | 191 +++++++
 source4/scripting/bin/informant                 |  73 +++
 source4/scripting/bin/informant2                | 113 +++++
 source4/scripting/bin/informant3                |  77 +++
 source4/scripting/bin/samba_gpoupdate           | 113 +++++
 source4/scripting/devel/testlibgpo.py           |  21 +
 18 files changed, 1715 insertions(+), 6 deletions(-)
 create mode 100644 docs-xml/smbdotconf/domain/gpoupdatecommand.xml
 rename {source3/libgpo => libgpo}/gpo_filesync.c (100%)
 rename {source3/libgpo => libgpo}/gpo_proto.h (100%)
 rename {source3/libgpo => libgpo}/gpo_reg.c (100%)
 create mode 100644 libgpo/pygpo.c
 create mode 100644 libgpo/pygpo.h
 create mode 100644 pygpo.h
 create mode 100755 python/samba/gpclass.py
 create mode 100644 source4/dsdb/gpo/gpo_update.c
 create mode 100644 source4/scripting/bin/informant
 create mode 100644 source4/scripting/bin/informant2
 create mode 100644 source4/scripting/bin/informant3
 create mode 100755 source4/scripting/bin/samba_gpoupdate
 create mode 100644 source4/scripting/devel/testlibgpo.py

diff --git a/docs-xml/smbdotconf/domain/gpoupdatecommand.xml b/docs-xml/smbdotconf/domain/gpoupdatecommand.xml
new file mode 100644
index 0000000..cbfd662
--- /dev/null
+++ b/docs-xml/smbdotconf/domain/gpoupdatecommand.xml
@@ -0,0 +1,14 @@
+<samba:parameter name="gpo update command"
+                 context="G"
+                 type="list"
+                 advanced="1"
+                 xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
+<description>
+	<para>This option sets the command that is called when there are
+		GPO updates.
+	</para>
+</description>
+
+<value type="default">&pathconfig.SCRIPTSBINDIR;/samba_gpoupdate</value>
+<value type="example">/usr/local/sbin/gpoupdate</value>
+</samba:parameter>
diff --git a/lib/param/loadparm.c b/lib/param/loadparm.c
index 6aa757f..fdddc6c 100644
--- a/lib/param/loadparm.c
+++ b/lib/param/loadparm.c
@@ -2570,7 +2570,7 @@ struct loadparm_context *loadparm_init(TALLOC_CTX *mem_ctx)
 	lpcfg_do_global_parameter(lp_ctx, "max connections", "0");
 
 	lpcfg_do_global_parameter(lp_ctx, "dcerpc endpoint servers", "epmapper wkssvc rpcecho samr netlogon lsarpc drsuapi dssetup unixinfo browser eventlog6 backupkey dnsserver");
-	lpcfg_do_global_parameter(lp_ctx, "server services", "s3fs rpc nbt wrepl ldap cldap kdc drepl winbindd ntp_signd kcc dnsupdate dns");
+	lpcfg_do_global_parameter(lp_ctx, "server services", "s3fs rpc nbt wrepl ldap cldap kdc drepl winbindd ntp_signd kcc dnsupdate dns gpoupdate");
 	lpcfg_do_global_parameter(lp_ctx, "kccsrv:samba_kcc", "true");
 	/* the winbind method for domain controllers is for both RODC
 	   auth forwarding and for trusted domains */
@@ -2650,6 +2650,7 @@ struct loadparm_context *loadparm_init(TALLOC_CTX *mem_ctx)
 	lpcfg_do_global_parameter(lp_ctx, "winbindd socket directory", dyn_WINBINDD_SOCKET_DIR);
 	lpcfg_do_global_parameter(lp_ctx, "winbindd privileged socket directory", dyn_WINBINDD_PRIVILEGED_SOCKET_DIR);
 	lpcfg_do_global_parameter(lp_ctx, "ntp signd socket directory", dyn_NTP_SIGND_SOCKET_DIR);
+	lpcfg_do_global_parameter_var(lp_ctx, "gpo update command", "%s/samba_gpoupdate", dyn_SCRIPTSBINDIR);
 	lpcfg_do_global_parameter_var(lp_ctx, "dns update command", "%s/samba_dnsupdate", dyn_SCRIPTSBINDIR);
 	lpcfg_do_global_parameter_var(lp_ctx, "spn update command", "%s/samba_spnupdate", dyn_SCRIPTSBINDIR);
 	lpcfg_do_global_parameter_var(lp_ctx, "samba kcc command",
diff --git a/source3/libgpo/gpo_filesync.c b/libgpo/gpo_filesync.c
similarity index 100%
rename from source3/libgpo/gpo_filesync.c
rename to libgpo/gpo_filesync.c
diff --git a/source3/libgpo/gpo_proto.h b/libgpo/gpo_proto.h
similarity index 100%
rename from source3/libgpo/gpo_proto.h
rename to libgpo/gpo_proto.h
diff --git a/source3/libgpo/gpo_reg.c b/libgpo/gpo_reg.c
similarity index 100%
rename from source3/libgpo/gpo_reg.c
rename to libgpo/gpo_reg.c
diff --git a/libgpo/pygpo.c b/libgpo/pygpo.c
new file mode 100644
index 0000000..3f9d339
--- /dev/null
+++ b/libgpo/pygpo.c
@@ -0,0 +1,644 @@
+/*
+   Unix SMB/CIFS implementation.
+   Copyright (C) Luke Morrison <luc785 at hotmail.com> 2013
+
+   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 <Python.h>
+#include "includes.h"
+#include "version.h"
+#include "param/pyparam.h"
+#include "pygpo.h"
+#include "ads.h"
+
+/*A Python C API module to use LIBGPO*/
+
+#ifndef Py_RETURN_NONE
+#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
+#endif
+
+staticforward PyTypeObject PyGpExt;
+staticforward PyTypeObject PyGpO;
+staticforward PyTypeObject PyGpIni;
+staticforward PyTypeObject PyGpLink;
+
+/******************************************************************************************************************
+*******************************************************************************************************************/
+
+/* Parameter mapping and functions for the GP_EXT struct */
+void initgpo(void);
+
+/* Parse raw extension string to GP_EXT structure */
+static PyObject *py_ads_parse_gp_ext(PyGpExtObject * self, PyObject * args)
+{
+	struct GP_EXT *gp_ext = pygp_ext_AsgpextContext((PyObject *) self);
+	bool verify;
+	const char *extension_raw;
+	TALLOC_CTX *tmp_ctx;
+
+	tmp_ctx = talloc_new(NULL);
+	if (!PyArg_ParseTuple(args, "s", &extension_raw)) {
+		return NULL;
+	}
+	verify = ads_parse_gp_ext(tmp_ctx, extension_raw, &gp_ext);
+	if (!verify) {
+		talloc_free(tmp_ctx);
+		Py_RETURN_NONE;
+	}
+	return (PyObject *) gp_ext;
+}
+
+/* Functions here */
+static PyMethodDef py_gp_ext_methods[] = {
+	{"ads_parse_gp_ext", (PyCFunction) py_ads_parse_gp_ext, METH_VARARGS,
+	 NULL},
+	{NULL}
+};
+
+/* Mapping here */
+
+static PyObject *py_gp_ext_get_extensions(PyGpExtObject * self)
+{
+	return PyString_FromString((*self->gp_ext->extensions));
+}
+
+static PyObject *py_gp_ext_get_extensions_guid(PyGpExtObject * self)
+{
+	return PyString_FromString((*self->gp_ext->extensions_guid));
+}
+
+static PyObject *py_gp_ext_get_snapins(PyGpExtObject * self)
+{
+	return PyString_FromString((*self->gp_ext->snapins));
+}
+
+static PyObject *py_gp_ext_get_snapins_guid(PyGpExtObject * self)
+{
+	return PyString_FromString((*self->gp_ext->snapins_guid));
+}
+
+static PyGetSetDef py_gp_ext_getset[] = {
+	{discard_const_p(char, "keyval_count"),
+	 (getter) py_gp_ext_get_extensions, NULL, NULL},
+	{discard_const_p(char, "current_section"),
+	 (getter) py_gp_ext_get_extensions_guid, NULL, NULL},
+	{discard_const_p(char, "generated_filename"),
+	 (getter) py_gp_ext_get_snapins, NULL, NULL},
+	{discard_const_p(char, "snapins_guid"),
+	 (getter) py_gp_ext_get_snapins_guid, NULL, NULL},
+	{NULL}
+};
+
+static PyObject *py_gp_ext_new(PyTypeObject * type, PyObject * args,
+			       PyObject * kwargs)
+{
+	struct GP_EXT *gp;
+	TALLOC_CTX *mem_ctx;
+	PyGpExtObject *py_ret;
+
+	mem_ctx = talloc_new(NULL);
+	if (mem_ctx == NULL) {
+		PyErr_NoMemory();
+		return NULL;
+	}
+
+	gp = talloc(mem_ctx, struct GP_EXT);
+	if (!gp) {
+		talloc_free(mem_ctx);
+		PyErr_SetString(PyExc_ValueError, "unable to allocate gp");
+		return NULL;
+	}
+
+	py_ret = (PyGpExtObject *) type->tp_alloc(type, 0);
+	if (py_ret == NULL) {
+		talloc_free(mem_ctx);
+		PyErr_NoMemory();
+		return NULL;
+	}
+	py_ret->mem_ctx = mem_ctx;
+	py_ret->gp_ext = gp;
+	return (PyObject *) py_ret;
+}
+
+static void py_gp_ext_dealloc(PyGpExtObject * self)
+{
+	if (self->mem_ctx != NULL) {
+		talloc_free(self->mem_ctx);
+	}
+	self->ob_type->tp_free(self);
+}
+
+static PyTypeObject PyGpExt = {
+	.tp_name = "gpo.ext",
+	.tp_dealloc = (destructor) py_gp_ext_dealloc,
+	.tp_new = py_gp_ext_new,
+	.tp_basicsize = sizeof(PyGpExtObject),
+	.tp_getset = py_gp_ext_getset,
+	.tp_methods = py_gp_ext_methods,
+};
+
+/*******************************************************************************************************************
+*******************************************************************************************************************/
+
+/* Parameter mapping and methods for the gpi_inifile_context Struct. */
+
+/* Functions here */
+
+/*static PyObject *py_gp_inifile_get_string(PyGpIniObject* self)*/
+
+static PyObject *py_parse_gpt_ini(PyObject * self, PyObject * args)
+{
+	TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+	char *filename;
+	uint32_t *version = 0;
+	NTSTATUS status;
+	char **display_name = NULL;
+	PyObject *result = NULL;
+
+	if (!PyArg_ParseTuple(args, "s", &filename)) {
+		return NULL;
+	}
+	status = parse_gpt_ini(tmp_ctx, filename, version, display_name);
+	if (!NT_STATUS_IS_OK(status)) {
+		return NULL;
+	}
+	/* Do not need to check for display name because it might not have one.
+	   Zero cases will be handled in python. */
+	result = Py_BuildValue("[s,i]", display_name, version);
+	return result;
+
+}
+
+static PyMethodDef py_gp_inifile_methods[] = {
+	{"parse_gpt_ini", (PyCFunction) py_parse_gpt_ini, METH_VARARGS,
+	 "Pase the local gp.ini file"},
+	{NULL}
+};
+
+/* Mapping Here */
+static PyObject *py_gp_inifile_keyval_count(PyGpIniObject * self)
+{
+	return PyInt_FromLong(self->gp_ini->keyval_count);
+}
+
+static PyObject *py_gp_inifile_get_current_section(PyGpIniObject * self)
+{
+	return PyString_FromString(self->gp_ini->current_section);
+}
+
+static PyObject *py_gp_inifile_generated_filename(PyGpIniObject * self)
+{
+	return PyString_FromString(self->gp_ini->generated_filename);
+}
+
+static PyGetSetDef py_gp_inifile_getset[] = {
+	{discard_const_p(char, "keyval_count"),
+	 (getter) py_gp_inifile_keyval_count, NULL, NULL},
+	{discard_const_p(char, "current_section"),
+	 (getter) py_gp_inifile_get_current_section, NULL, NULL},
+	{discard_const_p(char, "generated_filename"),
+	 (getter) py_gp_inifile_generated_filename, NULL, NULL},
+	{NULL}
+};
+
+static PyObject *py_gp_inifile_new(PyTypeObject * type, PyObject * args,
+				   PyObject * kwargs)
+{
+	struct gp_inifile_context *gp;
+	TALLOC_CTX *mem_ctx;
+	PyGpIniObject *py_ret;
+
+	mem_ctx = talloc_new(NULL);
+	if (mem_ctx == NULL) {
+		PyErr_NoMemory();
+		return NULL;
+	}
+
+	gp = talloc(mem_ctx, struct gp_inifile_context);
+	if (!gp) {
+		talloc_free(mem_ctx);
+		PyErr_SetString(PyExc_ValueError, "unable to allocate gp");
+		return NULL;
+	}
+
+	py_ret = (PyGpIniObject *) type->tp_alloc(type, 0);
+	if (py_ret == NULL) {
+		talloc_free(mem_ctx);
+		PyErr_NoMemory();
+		return NULL;
+	}
+	py_ret->mem_ctx = mem_ctx;
+	py_ret->gp_ini = gp;
+	return (PyObject *) py_ret;
+}
+
+static void py_gp_inifile_dealloc(PyGpIniObject * self)
+{
+	if (self->mem_ctx != NULL) {
+		talloc_free(self->mem_ctx);
+	}
+	self->ob_type->tp_free(self);
+}
+
+static PyTypeObject PyGpIni = {
+	.tp_name = "pygpo",
+	.tp_methods = py_gp_inifile_methods,
+	.tp_getset = py_gp_inifile_getset,
+	.tp_doc = "GPO for gp_inifile_context.",
+	.tp_new = py_gp_inifile_new,
+	.tp_dealloc = (destructor) py_gp_inifile_dealloc,
+	.tp_basicsize = sizeof(PyGpIniObject),
+};
+
+/****************************************************************************************/
+/* Parameter mapping and methods for the GROUP POLICY OBJECT Struct. */
+
+/* Functions here */
+
+static PyObject *py_gpo_get_unix_path(PyGpObject * self, PyObject * args)
+{
+	TALLOC_CTX *mem_ctx;
+	struct GROUP_POLICY_OBJECT *gpo = pygpo_AsgpoContext((PyObject *) self);
+	const char *cache_dir = NULL;
+	char **unix_path = NULL;
+	NTSTATUS status;
+	if (!PyArg_ParseTuple(args, "s", &cache_dir)) {
+		return NULL;
+	}
+	mem_ctx = talloc_new(NULL);
+	status = gpo_get_unix_path(mem_ctx, cache_dir, gpo, unix_path);
+	if (!NT_STATUS_IS_OK(status)) {
+		return NULL;
+	}
+	if (!unix_path) {
+		return NULL;
+	}
+	return PyString_FromString(*unix_path);
+}
+
+static PyMethodDef py_gpo_local_methods[] = {
+	{"gpo_get_unix_path", (PyCFunction) py_gpo_get_unix_path, METH_VARARGS,
+	 NULL},
+	{NULL}
+};
+
+/* Mapping here */
+static PyObject *py_options(PyGpObject * self)
+{
+	return PyInt_FromLong(self->gpo->options);
+}
+
+static PyObject *py_version(PyGpObject * self)
+{
+	return PyInt_FromLong(self->gpo->version);
+}
+
+static PyObject *py_ds_path(PyGpObject * self)
+{
+	return PyString_FromString(self->gpo->ds_path);
+}
+
+static PyObject *py_file_sys_path(PyGpObject * self)
+{
+	return PyString_FromString(self->gpo->file_sys_path);
+}
+
+static PyObject *py_name(PyGpObject * self)
+{
+	return PyString_FromString(self->gpo->name);
+}
+
+static PyObject *py_link(PyGpObject * self)
+{
+	return PyString_FromString(self->gpo->link);
+}
+
+static PyObject *py_user_extensions(PyGpObject * self)
+{
+	return PyString_FromString(self->gpo->user_extensions);
+}
+
+static PyObject *py_machine_extensions(PyGpObject * self)
+{
+	return PyString_FromString(self->gpo->machine_extensions);
+}
+
+static PyGetSetDef py_gpo_getset[] = {
+	{discard_const_p(char, "options"), (getter) py_options, NULL, NULL},
+	{discard_const_p(char, "version"), (getter) py_version, NULL, NULL},
+	{discard_const_p(char, "ds_path"), (getter) py_ds_path, NULL, NULL},
+	{discard_const_p(char, "file_sys_path"), (getter) py_file_sys_path,
+	 NULL, NULL},
+	{discard_const_p(char, "name"), (getter) py_name, NULL, NULL},
+	{discard_const_p(char, "link"), (getter) py_link, NULL, NULL},
+	{discard_const_p(char, "user_extensions"), (getter) py_user_extensions,
+	 NULL, NULL},
+	{discard_const_p(char, "machine_extensions"),
+	 (getter) py_machine_extensions, NULL, NULL},
+	{NULL}
+};
+
+static PyObject *py_gpo_local_new(PyTypeObject * type, PyObject * args,
+				  PyObject * kwargs)
+{
+	struct GROUP_POLICY_OBJECT *gpo;
+	TALLOC_CTX *mem_ctx;
+	PyGpObject *py_ret;
+	mem_ctx = talloc_new(NULL);
+	if (mem_ctx == NULL) {
+		PyErr_NoMemory();
+		return NULL;
+	}
+	gpo = talloc(mem_ctx, struct GROUP_POLICY_OBJECT);
+	if (!gpo) {
+		talloc_free(mem_ctx);
+		PyErr_SetString(PyExc_ValueError, "unable to allocate gp");
+		return NULL;
+	}
+	py_ret = (PyGpObject *) type->tp_alloc(type, 0);
+	if (py_ret == NULL) {
+		talloc_free(mem_ctx);
+		PyErr_NoMemory();
+		return NULL;
+	}
+	py_ret->mem_ctx = mem_ctx;
+
+	return (PyObject *) py_ret;
+}
+
+static void py_gpo_local_dealloc(PyGpObject * self)
+{
+	if (self->mem_ctx != NULL) {
+		talloc_free(self->mem_ctx);
+	}
+	self->ob_type->tp_free(self);
+}
+
+static PyTypeObject PyGpO = {
+	.tp_name = "pygpo",
+	.tp_methods = py_gpo_local_methods,
+	.tp_getset = py_gpo_getset,
+	.tp_doc = "GPO mapping",
+	.tp_new = py_gpo_local_new,
+	.tp_dealloc = (destructor) py_gpo_local_dealloc,
+	.tp_basicsize = sizeof(PyGpObject),
+};
+
+/******************************************************************************************************************************
+******************************************************************************************************************************/
+/* Parameter mapping and methods for the GP_LINK Struct. */
+
+/* Gets a GP_LINK structure from a linkdn */
+
+/*What is a linkdn?
+how do I initialize the AD structure*/
+/*
+static PyObject *py_ads_get_gpo_link(PyGpLinkObject *self, PyObject* args)
+{
+	struct GP_LINK *gp_link = pygp_link_AsgplinkContext((PyObject*)self->gp_link);//I Think this should just be self not self->gp_link
+	struct ADS_STRUCT *ads;
+	PyObject *py_obj;
+	TALLOC_CTX *mem_ctx;
+	mem_ctx = talloc_new(NULL);
+	uint32_t options;
+	char *link_dn;
+	PyObject *result;
+	ADS_STATUS status;
+
+	if (!PyArg_ParseTuple(args, "sO" , &link_dn, &py_obj)) {
+		return NULL;
+		}
+	if (!link_dn){
+		talloc_free(mem_ctx);
+		Py_RETURN_NONE;
+		}
+	ads = pygpoads_AsgpoadsContext(py_obj);
+	status = ads_get_gpo_link(ads, mem_ctx, link_dn, gp_link);
+	if (!ADS_ERR_OK(status)) {
+		printf("Status not ok, aborting!");
+		Py_RETURN_NONE;
+		}
+
+	if (!gp_link){
+		talloc_free(mem_ctx);
+		Py_RETURN_NONE;
+		printf("GP_LINK unitialized. Verify the string is valid and try again!\n");
+		}
+	result = Py_BuildValue("O", gp_link);
+	return result;
+
+}
+*/
+/*helper call to add a gp link
+static PyObject py_ads_add_gpo_link(PyGpLinkObject *self, PyObject *args)
+{
+	ADS_STRUCT *ads;
+	ads = pygplinkads_AsgplinkadsContext(self);
+	TALLOC_CTX *mem_ctx;
+	mem_ctx = talloc_new
+	ADS_STATUS status;
+	(!ADS_ERR_OK(status))
+
+	*/
+
+static PyMethodDef py_gp_link_methods[] = {
+/*{"ads_get_gpo_link", (PyCFunction)py_ads_get_gpo_link, METH_VARARGS, NULL},*/
+	{NULL}
+};
+
+static PyObject *py_gp_link(PyGpLinkObject * self)
+{
+	return PyString_FromString(self->gp_link->gp_link);
+}
+
+static PyObject *py_gp_opts(PyGpLinkObject * self)
+{
+	return PyInt_FromLong(self->gp_link->gp_opts);
+}
+
+static PyObject *py_num_links(PyGpLinkObject * self)
+{
+	return PyInt_FromLong(self->gp_link->num_links);
+}
+
+static PyObject *py_link_names(PyGpLinkObject * self)
+{
+	return PyString_FromString((*self->gp_link->link_names));
+}
+
+static PyObject *py_link_opts(PyGpLinkObject * self)
+{
+	return PyInt_FromLong((*self->gp_link->link_opts));
+}
+
+static PyGetSetDef py_gp_link_getset[] = {
+
+	{discard_const_p(char, "gp_link"), (getter) py_gp_link, NULL, NULL},
+	{discard_const_p(char, "gp_opts"), (getter) py_gp_opts, NULL, NULL},
+	{discard_const_p(char, "num_links"), (getter) py_num_links, NULL, NULL},
+	{discard_const_p(char, "link_names"), (getter) py_link_names, NULL,
+	 NULL},
+	{discard_const_p(char, "link_opts"), (getter) py_link_opts, NULL, NULL},
+
+	{NULL}
+};
+
+static PyObject *py_gp_link_new(PyTypeObject * type, PyObject * args,
+				PyObject * kwargs)
+{
+	struct GP_LINK *gplink;
+	TALLOC_CTX *mem_ctx;
+	PyGpLinkObject *py_ret;
+	mem_ctx = talloc_new(NULL);
+	if (mem_ctx == NULL) {
+		PyErr_NoMemory();
+		return NULL;
+	}
+	gplink = talloc(mem_ctx, struct GP_LINK);
+	if (!gplink) {
+		talloc_free(mem_ctx);
+		PyErr_SetString(PyExc_ValueError, "unable to allocate gp");
+		return NULL;
+	}
+	py_ret = (PyGpLinkObject *) type->tp_alloc(type, 0);
+	if (py_ret == NULL) {
+		talloc_free(mem_ctx);
+		PyErr_NoMemory();
+		return NULL;
+	}
+	py_ret->mem_ctx = mem_ctx;
+
+	return (PyObject *) py_ret;
+}
+
+static void py_gpo_link_dealloc(PyGpLinkObject * self)
+{
+	if (self->mem_ctx != NULL) {
+		talloc_free(self->mem_ctx);
+	}
+	self->ob_type->tp_free(self);
+}
+
+static PyTypeObject PyGpLink = {
+	.tp_name = "pygpo",
+	.tp_methods = py_gp_link_methods,
+	.tp_getset = py_gp_link_getset,
+	.tp_doc = "GPO mapping",
+	.tp_new = py_gp_link_new,
+	.tp_dealloc = (destructor) py_gpo_link_dealloc,
+	.tp_basicsize = sizeof(PyGpLinkObject),
+};
+
+/*****************************************************************************************************************************/
+/* Global methods aka do not need a special pyobject type */
+
+static PyObject *py_gpo_get_sysvol_gpt_version(PyObject * self, PyObject * args)
+{
+	TALLOC_CTX *tmp_ctx = NULL;
+	NTSTATUS status;
+	char *unix_path;
+	char *display_name = NULL;
+	uint32_t sysvol_version = 0;
+	PyObject *result;
+
+	tmp_ctx = talloc_new(NULL);
+
+	if (!PyArg_ParseTuple(args, "s", &unix_path)) {
+		return NULL;
+	}
+	status =
+	    gpo_get_sysvol_gpt_version(tmp_ctx, unix_path, &sysvol_version,
+				       &display_name);
+	talloc_free(tmp_ctx);
+	result = Py_BuildValue("[s,i]", display_name, sysvol_version);
+	return result;
+}
+
+/* Verify that the GUID is not a client side extension */
+static PyObject *py_cse_gpo_name_to_guid_string(PyObject * self,
+						PyObject * args)
+{
+	char *name = NULL;
+	char *ret = NULL;
+
+	if (!PyArg_ParseTuple(args, "s", &name)) {
+		return NULL;
+	}
+
+	ret = cse_gpo_name_to_guid_string(name);
+
+	return PyString_FromString(ret);
+}
+
+static PyObject *py_ads_init(PyGpObject * self, PyObject * args)
+{
+	const char *realm = NULL;
+	const char *workgroup = NULL;
+	const char *ldap_server = NULL;
+	ADS_STRUCT *ads = NULL;
+
+	printf("Before the as content statement\n");
+	ads = pygpoads_AsgpoadsContext(self->ads);
+
+	if (!PyArg_ParseTuple(args, "ss", &realm, &workgroup)) {
+		return NULL;
+	}
+	printf("After the content statement before function \n");
+	ads = ads_init(realm, workgroup, ldap_server);
+	printf("After function before returning");
+	if (!ads) {
+		printf("did this work");
+	}
+
+	return (PyObject *) ads;
+}
+
+static PyMethodDef py_gpo_methods[] = {
+	{"cse_gpo_name_to_guid_string",
+	 (PyCFunction) py_cse_gpo_name_to_guid_string, METH_VARARGS, NULL},
+	{"gpo_get_sysvol_gpt_version",
+	 (PyCFunction) py_gpo_get_sysvol_gpt_version, METH_VARARGS, NULL},
+	{"ads_init", (PyCFunction) py_ads_init, METH_VARARGS,
+	 "initializing the ads structure"},
+	{NULL}
+};
+
+/* will be called by python when loading this module */
+void initgpo(void)
+{
+	PyObject *m;
+
+	debug_setup_talloc_log();
+	/* Instanciate the types */
+	m = Py_InitModule3("gpo", py_gpo_methods, "libgpo python bindings");
+	if (m == NULL)
+		return;
+	PyModule_AddObject(m, "version",
+			   PyString_FromString(SAMBA_VERSION_STRING));
+	if (PyType_Ready(&PyGpO) < 0)
+		return;
+	if (PyType_Ready(&PyGpIni) < 0)
+		return;
+	if (PyType_Ready(&PyGpExt) < 0)
+		return;
+	if (PyType_Ready(&PyGpLink) < 0)
+		return;
+
+	Py_INCREF(&PyGpO);
+	Py_INCREF(&PyGpIni);
+	Py_INCREF(&PyGpExt);
+	Py_INCREF(&PyGpLink);
+
+}
diff --git a/libgpo/pygpo.h b/libgpo/pygpo.h
new file mode 100644
index 0000000..8beada1
--- /dev/null
+++ b/libgpo/pygpo.h
@@ -0,0 +1,53 @@
+#include <Python.h>
+#include <talloc.h>
+#include "ads.h"
+#include "gpo.h"
+#include "gpo_ini.h"
+#include "gpo_proto.h"
+
+//#ifndef _GPEXT_H_
+//#define _GPEXT_H_
+
+typedef struct {
+	PyObject_HEAD
+	TALLOC_CTX * mem_ctx;
+	struct GP_EXT *gp_ext;
+} PyGpExtObject;
+#define pygp_ext_AsgpextContext(pyobj) ((PyGpExtObject *)pyobj)->gp_ext
+
+typedef struct {
+	PyObject_HEAD
+	TALLOC_CTX * mem_ctx;
+	struct GROUP_POLICY_OBJECT *gpo;
+	struct ADS_STRUCT *ads;
+} PyGpObject;
+#define pygpo_AsgpoContext(pyobj) ((PyGpObject *)pyobj)->gpo
+#define pygpoads_AsgpoadsContext(pyobj) ((PyGpObject *)pyobj)->ads
+typedef struct {
+	PyObject_HEAD
+	TALLOC_CTX * mem_ctx;
+	struct GP_LINK *gp_link;
+	struct ADS_STRUCT *ads;
+} PyGpLinkObject;
+#define pygp_link_AsgplinkContext(pyobj) ((PyGpLinkObject *)pyobj)->gp_link
+#define pygplinkads_AsgplinkadsContext(pyobj) ((PyGpLinkObject *)->ads
+typedef struct {
+	PyObject_HEAD
+	TALLOC_CTX * mem_ctx;
+	struct gp_registry_entry *gp_reg;
+} PyRegObject;
+#define pygp_reg_AsgpregContext(pyobj) ((PyRegObject *)pyobj)->gp_reg
+typedef struct {
+	PyObject_HEAD
+	TALLOC_CTX * mem_ctx;
+	struct gp_registry_value *gp_reg_value;
+} PyRegvalobject;
+#define pygp_regval_AsgpregvalContext(pyobj) ((PyRegvalObject *)pyobj)->gp_reg_value
+
+typedef struct {
+	PyObject_HEAD
+	TALLOC_CTX * mem_ctx;
+	struct gp_inifile_context *gp_ini;
+} PyGpIniObject;
+
+#define pygp_ini_AsgpiniContext(pyobj) ((PyGpIniObject *)pyobj)->gp_ini
diff --git a/libgpo/wscript_build b/libgpo/wscript_build
index f182b44..598cfcb 100644
--- a/libgpo/wscript_build
+++ b/libgpo/wscript_build
@@ -1,8 +1,13 @@
 #!/usr/bin/env python
 
-bld.SAMBA_SUBSYSTEM('LIBGPO',
-	source='gpo_util.c gpo_sec.c ../libgpo/gpext/gpext.c gpo_fetch.c gpo_ini.c ../source4/libgpo/ads_convenience.c ../source3/libgpo/gpo_filesync.c ../source4/libgpo/gpo_filesync.c',
-	deps='ldb samba-net samba-util',
-	enabled=False
-	)
+LIBGPO_SRC = '''gpo_ldap.c gpo_ini.c gpo_util.c gpo_fetch.c gpo_filesync.c
+                gpo_sec.c gpo_reg.c gpext/gpext.c'''
 
+bld.SAMBA3_LIBRARY('gpo',
+                   source='${LIBGPO_SRC}',
+                   deps='talloc ads TOKEN_UTIL auth',
+                   vars=locals(),
+                   private_library=True)
+bld.SAMBA3_PYTHON('python_samba_libgpo', 'pygpo.c',
+                 deps='pyparam_util gpo talloc ads TOKEN_UTIL auth',
+                 realname='samba/gpo.so')
diff --git a/pygpo.h b/pygpo.h
new file mode 100644
index 0000000..c517bd8
--- /dev/null
+++ b/pygpo.h
@@ -0,0 +1,8 @@
+#include <talloc.h>
+#include "gpo_ini.h"
+typedef struct {
+	PyObject_HEAD Talloc_CTX * mem_ctx;
+	struct gp_inifile_context *gp_ctx;
+} PyGpiniObject;
+
+#define pygpo_inifile_AsGpContext(pyobj) ((PyGpiniObect *) probj ) -> gp_ctx
diff --git a/python/samba/gpclass.py b/python/samba/gpclass.py
new file mode 100755
index 0000000..f88e3d3
--- /dev/null
+++ b/python/samba/gpclass.py
@@ -0,0 +1,374 @@
+#!/usr/bin/env python
+#
+# Reads important GPO parameters and updates Samba
+# Copyright (C) Luke Morrison <luc785 at .hotmail.com> 2013
+#
+# 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/>.
+
+
+import sys
+import os
+sys.path.insert(0, "bin/python")
+import samba.gpo as gpo
+import optparse
+import ldb
+from samba.auth import system_session
+import samba.getopt as options
+from samba.samdb import SamDB
+from samba.netcmd import gpo as gpo_user
+import codecs
+
+class gp_ext(object):
+    def list(self, rootpath):
+        return None
+
+    def __str__(self):
+        return "default_gp_ext"
+
+
+class inf_to_ldb(object):
+    '''This class takes the .inf file parameter (essentially a GPO file mapped to a GUID),
+    hashmaps it to the Samba parameter, which then uses an ldb object to update the
+    parameter to Samba4. Not registry oriented whatsoever.
+    '''
+
+    def __init__(self, ldb, dn, attribute, val):
+        self.ldb = ldb
+        self.dn = dn
+        self.attribute = attribute
+        self.val = val
+
+    def ch_minPwdAge(self, val):
+        self.ldb.set_minPwdAge(val)
+
+    def ch_maxPwdAge(self, val):
+        self.ldb.set_maxPwdAge(val)
+
+    def ch_minPwdLength(self, val):
+        self.ldb.set_minPwdLength(val)
+
+    def ch_pwdProperties(self, val):
+        self.ldb.set_pwdProperties(val)
+
+    def explicit(self):
+        return self.val
+
+    def nttime2unix(self):
+        seconds = 60
+        minutes = 60
+        hours = 24
+        sam_add = 10000000
+        val = (self.val)
+        val = int(val)
+        return  str(-(val * seconds * minutes * hours * sam_add))
+
+    def mapper(self):
+        '''ldap value : samba setter'''
+        return { "minPwdAge" : (self.ch_minPwdAge, self.nttime2unix),
+                 "maxPwdAge" : (self.ch_maxPwdAge, self.nttime2unix),
+                 # Could be none, but I like the method assignment in update_samba
+                 "minPwdLength" : (self.ch_minPwdLength, self.explicit),
+                 "pwdProperties" : (self.ch_pwdProperties, self.explicit),
+
+               }
+
+    def update_samba(self):
+        (upd_sam, value) = self.mapper().get(self.attribute)
+        upd_sam(value())     # or val = value() then update(val)
+
+
+class gp_sec_ext(gp_ext):
+    '''This class does the following two things:
+        1) Identifies the GPO if it has a certain kind of filepath,
+        2) Finally parses it.
+    '''
+
+    count = 0
+
+    def __str__(self):
+        return "Security GPO extension"
+
+    def list(self, rootpath):
+        path = "%s/%s" % (rootpath, "/Machine/Microsoft/Windows NT/SecEdit/GptTmpl.inf")
+        if os.path.exists(path):
+                return path
+
+    def listmachpol(self, rootpath):
+        path = "%s/%s" % (rootpath, "Machine/Registry.pol")
+        if os.path.exists(path):
+            return path
+
+    def listuserpol(self, rootpath):
+        path = "%s/%s" % (rootpath, "User/Registry.pol")
+        if os.path.exists(path):
+            return path
+
+    def populate_inf(self):
+        return {"System Access": {"MinimumPasswordAge": ("minPwdAge", inf_to_ldb),
+                                  "MaximumPasswordAge": ("maxPwdAge", inf_to_ldb),
+                                  "MinimumPasswordLength": ("minPwdLength", inf_to_ldb),
+                                  "PasswordComplexity": ("pwdProperties", inf_to_ldb),
+                                 }
+               }
+#FIXME. EACH gpo should have a parser, and a creater. Essentially a gpo is just a file. Possibly a method and class to link it to organization unit (if that already does not exist) so that GPO's can be created arithmetically, possibly with a hashtable for certain GPO, then linked if desired. Also could store a backup folder of gpo's and then configure them without necessarily deploying it.
+
+    def read_inf(self, path):
+        inftable = self.populate_inf()
+        '''The inf file to be mapped'''
+        policy = codecs.open(path, encoding='utf-16')
+        if not policy:
+            return None
+        current_section = None
+        for line in policy.readlines():
+            line = line.strip()
+            if line[0] == '[':
+                section = line[1: -1]
+                current_section = inftable.get(section.encode('ascii', 'ignore'))
+
+            else:
+                # We must be in a section
+                if not current_section:
+                    continue
+                (key, value) = line.split("=")
+                key = key.strip()
+                if current_section.get(key):
+                    (att, setter) = current_section.get(key)
+                    value = value.encode('ascii', 'ignore')
+                    setter(self.ldb, self.dn, att, value).update_samba()
+    #FIXME read registry files (.pol). Can they ever apply? Define read_registry():
+
+    def parse(self, afile, ldb):
+        self.ldb = ldb
+        self.dn = ldb.get_default_basedn()
+        if afile.endswith('inf'):
+            self.read_inf(afile)
+
+class samba4_gpo_hierarchy(object):
+
+    def __init__(self, SamDB, sysvol_guid_list, DC_OU, GLOBAL_DN):
+        """
+        :param SamDB: An instance of the live samba database
+        :param sysvol_guid_list: The complete list of all GPO GUID's listed in sysvol folder
+        :param DC_OU: The respective distinguished name of the Domain Controller
+        :param GLOBAL_DN: The Domain DN that Samba is a part of
+        """
+        self.SamDB = SamDB
+        self.GUID_L = sysvol_guid_list
+        self.DC_OU = DC_OU
+        self.GL_DN = GLOBAL_DN
+        self.sorted_containers = []
+        self.sorted_full = []
+        self.indexed_places = []
+        self.unapplied_gpo = 0
+
+    def update_unapplied_gpo(self):
+        self.update_unapplied_gpo += 1
+
+    '''Returns list of int indexes to where the dn changes'''
+    def container_indexes(self):
+        count = 0
+        container_indexes = []
+        while count < (len(self.GUID_L)-1):
+            if self.sorted_containers[count][2] != self.sorted_containers[count+1][2]:
+                container_indexes.append(count+1)
+            count += 1
+        container_indexes.append(len(self.sorted_containers))
+        return container_indexes
+
+
+    def establish_hierarchy(self):
+        final_list = []
+        count_unapplied_GPO = 0
+        for GUID in self.GUID_L:
+            container_iteration = 0
+            applied = False # Assume first it is not applied
+            gpo_realm = False # Realm only written on last call, if the GPO is linked to multiple places
+            '''Get all of the linked information'''
+            GPO_CONTAINERS = gpo_user.get_gpo_containers(self.SamDB, GUID)
+            for GPO_CONTAINER in GPO_CONTAINERS:
+
+                container_iteration +=1
+
+                if self.DC_OU == str(GPO_CONTAINER.get('dn')):
+                    applied = True
+                    insert_gpo = [GUID, applied, str(GPO_CONTAINER.get('dn'))]
+                    self.sorted_containers.append(insert_gpo)
+                    break
+
+                if self.GL_DN == str(GPO_CONTAINER.get('dn')) and (len(GPO_CONTAINERS) == 1):
+                    gpo_realm = True
+                    applied = True
+                    #REALM_GPO = [GUID, applied, str(GPO_CONTAINER.get('dn'))]
+                    #final_list.insert(count_unapplied_GPO, REALM_GPO)
+
+                if self.GL_DN == str(GPO_CONTAINER.get('dn')) and (len(GPO_CONTAINERS) > 1):
+                    gpo_realm = True
+                    applied = True
+
+                if container_iteration == len(GPO_CONTAINERS):
+                    if gpo_realm == False:
+                        insert_dud = [GUID, applied, str(GPO_CONTAINER.get('dn'))]
+                        self.sorted_containers.insert(0, insert_dud)
+                        self.count_unapplied_GPO()
+                    else :
+                        REALM_GPO = [GUID, applied, str(GPO_CONTAINER.get('dn'))]
+                        self.sorted_containers.insert(count_unapplied_GPO, REALM_GPO)
+
+        '''After GPO are sorted into containers, sort the containers themselves. But first append non-applicable GPO.'''
+        self.indexed_places = self.container_indexes()
+        count = 0
+        unapplied_gpo = []
+        self.sorted_full = []
+        '''Append all empties to final from first change of container'''
+        while count < self.indexed_places[0]:
+            unapplied_gpo.append(self.sorted_containers[count])
+            count += 1
+
+        count = 0
+        self.sorted_full += unapplied_gpo
+        while count < (len(self.indexed_places)-1): # Already accounted for one in empties
+            self.sorted_full += (sort_linked(self.SamDB, self.sorted_containers, self.indexed_places[count], self.indexed_places[count + 1]))
+            count += 1
+
+
+def scan_log(sysvol_path):
+    a = open(sysvol_path, "r")
+    data = {}
+    for line in a.readlines():
+        line = line.strip()
+        (guid, version) = line.split(" ")
+        data[guid] = int(version)
+    return data
+
+# The hierarchy is as per MS http://msdn.microsoft.com/en-us/library/windows/desktop/aa374155%28v=vs.85%29.aspx
+#
+# It does not care about local GPO, because GPO and snap-ins are not made in Linux yet.
+# It follows the linking order and children GPO are last written format.
+#
+# Also, couple further testing with call scripts entitled informant and informant2.
+# They explicitly show the returned hierarchically sorted list.
+
+
+def container_indexes(GUID_LIST):
+    '''So the original list will need to be seperated into containers.
+    Returns indexed list of when the container changes after hierarchy
+    '''
+    count = 0
+    container_indexes = []
+    while count < (len(GUID_LIST)-1):
+        if GUID_LIST[count][2] != GUID_LIST[count+1][2]:
+            container_indexes.append(count+1)
+        count += 1
+    container_indexes.append(len(GUID_LIST))
+    return container_indexes
+
+
+def sort_linked(SAMDB, guid_list, start, end):
+    '''So GPO in same level need to have link level.
+    This takes a container and sorts it.
+
+    TODO:  Small small problem, it is backwards
+    '''
+    containers = gpo_user.get_gpo_containers(SAMDB, guid_list[start][0])
+    for right_container in containers:
+        if right_container.get('dn') == guid_list[start][2]:
+            break
+    gplink = str(right_container.get('gPLink'))
+    gplink_split = gplink.split('[')
+    linked_order = []
+    ret_list = []
+    for ldap_guid in gplink_split:
+        linked_order.append(str(ldap_guid[10:48]))
+    count = len(linked_order) - 1
+    while count > 0:
+        ret_list.append([linked_order[count], True, guid_list[start][2]])
+        count -= 1
+    return ret_list
+
+   # Accepts sysvol parameters to return a hierarchically sorted list, with application flag indicators.
+
+
+#A GPO may have a single or multiple links. Get all of the containers (OU, SITE, etc..) and return them'''
+    #def get_gpo_containers( ) :
+    #   return gpo_netcmd_user.get_gpo_containers(self.SamDB, self.GUID)
+
+  #  def
+
+def establish_hierarchy(SamDB, GUID_LIST, DC_OU, global_dn):
+    '''Takes a list of GUID from gpo, and sorts them based on OU, and realm.
+    See http://msdn.microsoft.com/en-us/library/windows/desktop/aa374155%28v=vs.85%29.aspx
+    '''
+    final_list = []
+    count_unapplied_GPO = 0
+    for GUID in GUID_LIST:
+
+        container_iteration = 0
+        # Assume first it is not applied
+        applied = False
+        # Realm only written on last call, if the GPO is linked to multiple places
+        gpo_realm = False
+
+        # A very important call. This gets all of the linked information.
+        GPO_CONTAINERS = gpo_user.get_gpo_containers(SamDB, GUID)
+        for GPO_CONTAINER in GPO_CONTAINERS:
+
+            container_iteration += 1
+
+            if DC_OU == str(GPO_CONTAINER.get('dn')):
+                applied = True
+                insert_gpo = [GUID, applied, str(GPO_CONTAINER.get('dn'))]
+                final_list.append(insert_gpo)
+                break
+
+            if global_dn == str(GPO_CONTAINER.get('dn')) and (len(GPO_CONTAINERS) == 1):
+                gpo_realm = True
+                applied = True
+                #REALM_GPO = [GUID, applied, str(GPO_CONTAINER.get('dn'))]
+                #final_list.insert(count_unapplied_GPO, REALM_GPO)
+
+
+            if global_dn == str(GPO_CONTAINER.get('dn')) and (len(GPO_CONTAINERS) > 1):
+                gpo_realm = True
+                applied = True
+
+
+            if container_iteration == len(GPO_CONTAINERS):
+                if gpo_realm == False:
+                    insert_dud = [GUID, applied, str(GPO_CONTAINER.get('dn'))]
+                    final_list.insert(0, insert_dud)
+                    count_unapplied_GPO += 1
+                else:
+                    REALM_GPO = [GUID, applied, str(GPO_CONTAINER.get('dn'))]
+                    final_list.insert(count_unapplied_GPO, REALM_GPO)
+
+    # After GPO are sorted into containers, let's sort the containers themselves.
+    # But first we can get the GPO that we don't care about, out of the way.
+    indexed_places = container_indexes(final_list)
+    count = 0
+    unapplied_gpo = []
+    # Sorted by container
+    sorted_gpo_list = []
+    '''Since the unapplied GPO are put at the front of the list, just once again append them to the linked container sorted list'''
+    while count < indexed_places[0]:
+        unapplied_gpo.append(final_list[count])
+        count += 1
+    count = 0
+    sorted_gpo_list += unapplied_gpo
+
+    # A single container call gets the linked order for all GPO in container.
+    # So we need one call per container - > index of the Original list
+    while count < (len(indexed_places)-1):
+        sorted_gpo_list += (sort_linked(SamDB, final_list, indexed_places[count], indexed_places[count+1]))
+        count += 1
+    return sorted_gpo_list
diff --git a/python/samba/samdb.py b/python/samba/samdb.py
index eabe363..c82209d 100644
--- a/python/samba/samdb.py
+++ b/python/samba/samdb.py
@@ -828,6 +828,24 @@ accountExpires: %u
         else:
             return res[0]["minPwdAge"][0]
 
+    def set_maxPwdAge(self, value):
+        m = ldb.Message()
+        m.dn = ldb.Dn(self, self.domain_dn())
+        m["maxPwdAge"] = ldb.MessageElement(value, ldb.FLAG_MOD_REPLACE, "maxPwdAge")
+        self.modify(m)
+
+
+    def get_maxPwdAge(self):
+        res = self.search(self.domain_dn(), scope=ldb.SCOPE_BASE, attrs=["maxPwdAge"])
+        if len(res) == 0:
+            return None
+        elif not "maxPwdAge" in res[0]:
+            return None
+        else:
+            return res[0]["maxPwdAge"][0]
+
+
+
     def set_minPwdLength(self, value):
         m = ldb.Message()
         m.dn = ldb.Dn(self, self.domain_dn())
diff --git a/source3/libgpo/gpext/wscript_build b/source3/libgpo/gpext/wscript_build
index 3a120a5..68b39de 100644
--- a/source3/libgpo/gpext/wscript_build
+++ b/source3/libgpo/gpext/wscript_build
@@ -4,6 +4,10 @@ bld.SAMBA3_SUBSYSTEM('gpext',
                     source='../../../libgpo/gpext/gpext.c',
                     deps='samba-util samba3core gpo')
 
+GPEXT_REGISTRY_SRC = 'registry.c'
+GPEXT_SCRIPTS_SRC = 'scripts.c'
+GPEXT_SECURITY_SRC = 'security.c'
+
 bld.SAMBA3_MODULE('gpext_registry',
                  subsystem='gpext',
                  source='registry.c',
diff --git a/source4/dsdb/gpo/gpo_update.c b/source4/dsdb/gpo/gpo_update.c
new file mode 100644
index 0000000..5abc109
--- /dev/null
+++ b/source4/dsdb/gpo/gpo_update.c
@@ -0,0 +1,191 @@
+/*
+   Unix SMB/CIFS mplementation.
+   GPO update service
+
+   Copyright (C) Luke Morrison 2013
+
+   Inspired by dns_updates.c written by Andrew Trigell 2009
+
+   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 "includes.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/auth.h"
+#include "smbd/service.h"
+#include "lib/messaging/irpc.h"
+#include "param/param.h"
+#include "system/filesys.h"
+#include "dsdb/common/util.h"
+#include "libcli/composite/composite.h"
+#include "libcli/security/dom_sid.h"
+#include "librpc/gen_ndr/ndr_irpc.h"
+#include "libds/common/roles.h"
+
+NTSTATUS server_service_gpoupdate_init(void);
+
+struct gpoupdate_service {
+	struct auth_session_info *system_session_info;
+	struct task_server *task;
+	struct ldb_context *samdb;
+
+	/* status for periodic sysvol/GPO scan update - >sysvscan */
+	struct {
+		uint32_t interval;
+		struct tevent_timer *te;
+		struct tevent_req *subreq;
+		NTSTATUS status;
+	} sysvscan;
+};
+
+/*
+Called when the sysvol scan has finished
+*/
+static void gpoupdate_sysvscan_done(struct tevent_req *subreq)
+{
+	struct gpoupdate_service *service = tevent_req_callback_data(subreq,
+								     struct
+								     gpoupdate_service);
+	int ret;
+	int sys_errno;
+
+	service->sysvscan.subreq = NULL;
+
+	ret = samba_runcmd_recv(subreq, &sys_errno);
+	TALLOC_FREE(subreq);
+	if (ret != 0) {
+		service->sysvscan.status =
+		    map_nt_error_from_unix_common(sys_errno);
+	} else {
+		service->sysvscan.status = NT_STATUS_OK;
+	}
+
+	if (!NT_STATUS_IS_OK(service->sysvscan.status)) {
+		DEBUG(0, (__location__ ": Failed GPO update - %s\n",
+			  nt_errstr(service->sysvscan.status)));
+	} else {
+		DEBUG(3, ("Completed GPO update check OK\n"));
+	}
+}
+
+static NTSTATUS gpoupdate_sysvscan_schedule(struct gpoupdate_service *service);
+
+static void gpoupdate_scan_apply(struct gpoupdate_service *service);
+
+static void gpoupdate_sysvscan_handler_te(struct tevent_context *ev,
+					  struct tevent_timer *te,
+					  struct timeval t, void *ptr)
+{
+	struct gpoupdate_service *service =
+	    talloc_get_type(ptr, struct gpoupdate_service);
+
+	gpoupdate_scan_apply(service);
+	gpoupdate_sysvscan_schedule(service);
+}
+
+static NTSTATUS gpoupdate_sysvscan_schedule(struct gpoupdate_service *service)
+{
+	/* For the moment the interval is hard coded to 5 sec */
+	DEBUG(0,
+	      ("calling %s interval = %d\n", __FUNCTION__,
+	       service->sysvscan.interval));
+	service->sysvscan.te =
+	    tevent_add_timer(service->task->event_ctx, service,
+			     timeval_current_ofs(service->sysvscan.interval, 0),
+			     gpoupdate_sysvscan_handler_te, service);
+	NT_STATUS_HAVE_NO_MEMORY(service->sysvscan.te);
+	return NT_STATUS_OK;
+}
+
+static void gpoupdate_scan_apply(struct gpoupdate_service *service)
+{
+	const char *const *gpo_update_command =
+	    lpcfg_gpo_update_command(service->task->lp_ctx);
+	const char *smbconf = lpcfg_configfile(service->task->lp_ctx);
+	/* /home/john/samba/samba/source4/scripting/bin/gpoupdate */
+	TALLOC_FREE(service->sysvscan.subreq);
+	DEBUG(3, ("Calling GPO update script\n"));
+	service->sysvscan.subreq = samba_runcmd_send(service,
+						     service->task->event_ctx,
+						     timeval_current_ofs(20, 0),
+						     2, 0,
+						     gpo_update_command,
+						     smbconf, NULL);
+	if (service->sysvscan.subreq == NULL) {
+		DEBUG(0,
+		      (__location__
+		       ": samba_runcmd_send() failed with no memory\n"));
+		return;
+	}
+	tevent_req_set_callback(service->sysvscan.subreq,
+				gpoupdate_sysvscan_done, service);
+}
+
+static void gpoupdate_task_init(struct task_server *task)
+{
+	NTSTATUS status;
+	struct gpoupdate_service *service;
+
+	if (lpcfg_server_role(task->lp_ctx) != ROLE_ACTIVE_DIRECTORY_DC) {
+		/* not useful for non-DC */
+		return;
+	}
+
+	task_server_set_title(task, "task[gpoupdate]");
+
+	service = talloc_zero(task, struct gpoupdate_service);
+	if (!service) {
+		task_server_terminate(task,
+				      "gpoupdate_task_init: out of memory",
+				      true);
+		return;
+	}
+	service->task = task;
+	task->private_data = service;
+
+	service->system_session_info = system_session(service->task->lp_ctx);
+	if (!service->system_session_info) {
+		task_server_terminate(task,
+				      "gpoupdate: Failed to obtain server credentials\n",
+				      true);
+		return;
+	}
+
+	/*FIXME maybe I should remove this if I don't need to do queries in C */
+	service->samdb =
+	    samdb_connect(service, service->task->event_ctx, task->lp_ctx,
+			  service->system_session_info, 0);
+	if (!service->samdb) {
+		task_server_terminate(task,
+				      "gpoupdate: Failed to connect to local samdb\n",
+				      true);
+		return;
+	}
+
+	service->sysvscan.interval = lpcfg_parm_int(task->lp_ctx, NULL, "gpoupdate", "config interval", 30);	/* in seconds */
+	status = gpoupdate_sysvscan_schedule(service);
+	if (!NT_STATUS_IS_OK(status)) {
+		task_server_terminate(task, talloc_asprintf(task,
+							    "gpoupdate: Failed to update sysvol scan schedule: %s\n",
+							    nt_errstr(status)),
+				      true);
+		return;
+	}
+}
+
+NTSTATUS server_service_gpoupdate_init(void)
+{
+	return register_server_service("gpoupdate", gpoupdate_task_init);
+}
diff --git a/source4/scripting/bin/informant b/source4/scripting/bin/informant
new file mode 100644
index 0000000..d23b411
--- /dev/null
+++ b/source4/scripting/bin/informant
@@ -0,0 +1,73 @@
+#!/usr/bin/env python
+# Copyright Luke Morrison <luc785 at .hotmail.com> 2013
+
+
+import os
+import fcntl
+import sys
+import tempfile
+import subprocess
+
+sys.path.insert(0, "bin/python")
+from samba.dcerpc import security
+import samba
+import optparse
+from samba import getopt as options
+from samba.gpclass import *
+from samba.netcmd import gpo as gpo_user
+
+
+#Finds all GPO Files ending in inf
+def gp_path_list(path):
+
+	GPO_LIST = []
+	for ext in gp_extensions:
+		GPO_LIST.append((ext, ext.list(path)))
+
+	return GPO_LIST
+
+#Reads the GPOs and sends them to their proper handlers
+def gpo_parser(GPO_LIST, ldb):
+	for entry in GPO_LIST:
+		(ext, thefile) = entry
+		ext.parse(thefile, ldb)
+parser = optparse.OptionParser("testsearchdn [options]")
+
+sambaopts = options.SambaOptions(parser)
+
+parser.add_option_group(sambaopts)
+parser.add_option_group(options.VersionOptions(parser))
+
+credopts = options.CredentialsOptions(parser)
+
+parser.add_option("-H", dest = "url", help="URL for the samdb")
+
+parser.add_option_group(credopts)
+
+opts, args = parser.parse_args()
+lp = sambaopts.get_loadparm()
+
+smbconf = lp.configfile
+creds = credopts.get_credentials(lp)
+
+session = system_session()
+
+if not opts.url:
+    url = lp.samdb_url()
+else:
+    url = opts.url
+
+#########################
+#Inialize Samba Database#
+#########################
+test_ldb = SamDB(url, session_info=session,
+ credentials=creds,lp=lp)
+
+schemadn = test_ldb.get_schema_basedn()
+
+basedn = test_ldb.get_default_basedn()
+
+print 'The current value of the Min password Age is %s' %  test_ldb.get_minPwdAge()
+print 'and Max age is %s' % test_ldb.get_maxPwdAge()
+print 'and Min Password length is %s' % test_ldb.get_minPwdLength()
+print 'and Password complexity is %s' % test_ldb.get_pwdProperties()
diff --git a/source4/scripting/bin/informant2 b/source4/scripting/bin/informant2
new file mode 100644
index 0000000..5bf9aeb
--- /dev/null
+++ b/source4/scripting/bin/informant2
@@ -0,0 +1,113 @@
+#!/usr/bin/env python
+# Copyright Luke Morrison <luc785 at .hotmail.com> 2013
+
+
+import os
+import fcntl
+import sys
+import tempfile
+import subprocess
+import re
+sys.path.insert(0, "bin/python")
+from samba.dcerpc import security
+from samba.provision import (provision_paths_from_lp)
+from samba import Ldb
+import samba
+import optparse
+from samba import getopt as options
+from samba.gpclass import *
+from samba.netcmd import gpo as gpo_user
+
+
+#Finds all GPO Files ending in inf
+def gp_path_list(path):
+
+	GPO_LIST = []
+	for ext in gp_extensions:
+		GPO_LIST.append((ext, ext.list(path)))
+
+	return GPO_LIST
+
+#Reads the GPOs and sends them to their proper handlers
+def gpo_parser(GPO_LIST, ldb):
+	for entry in GPO_LIST:
+		(ext, thefile) = entry
+		ext.parse(thefile, ldb)
+parser = optparse.OptionParser("testsearchdn [options]")
+
+sambaopts = options.SambaOptions(parser)
+
+parser.add_option_group(sambaopts)
+parser.add_option_group(options.VersionOptions(parser))
+
+credopts = options.CredentialsOptions(parser)
+
+parser.add_option("-H", dest = "url", help="URL for the samdb")
+
+parser.add_option_group(credopts)
+
+opts, args = parser.parse_args()
+lp = sambaopts.get_loadparm()
+
+smbconf = lp.configfile
+creds = credopts.get_credentials(lp)
+
+session = system_session()
+
+if not opts.url:
+    url = lp.samdb_url()
+else:
+    url = opts.url
+
+#########################
+#Inialize Samba Database#
+#########################
+paths = provision_paths_from_lp(lp, lp.get("realm"))
+privilegedb = Ldb(paths.privilege, session_info=session, credentials=creds, lp=lp)
+res = privilegedb.search(expression='(objectclass=*)')
+for l in res:
+    print l.dn
+    print l
+test_ldb = SamDB(url, session_info=session,
+ credentials=creds,lp=lp)
+
+schemadn = test_ldb.get_schema_basedn()
+
+basedn = test_ldb.get_default_basedn()
+print 'This is the base dn %s' %test_ldb.get_default_basedn()
+seconds = 60
+minutes = 60
+hours = 24
+sam_add = 10000000
+days1= -int(test_ldb.get_minPwdAge())/(seconds*minutes*hours*sam_add)
+print 'The current value of the Min password Age is %s' % days1
+
+days2= -int(test_ldb.get_maxPwdAge())/(seconds*minutes*hours*sam_add)
+print 'and Max age is %s' % days2
+print 'the min password length is %s' % test_ldb.get_minPwdLength()
+a = test_ldb.get_domain_sid()
+print a
+a = test_ldb.get_serverName()
+print a
+print 'The domain name is ' + test_ldb.domain_dn()
+print 'The properties of DC PWd is : ' + test_ldb.get_pwdProperties()
+print 'The dns name of the host is' + test_ldb.domain_dns_name() + 'The dns name of the domain is ' + test_ldb.host_dns_name()
+#print test_ldb.get_nc_root(test_ldb.domaini
+'''
+container = gpo_user.get_gpo_containers(test_ldb, '{26C47ABE-688A-43F7-8BF0-4B2B4207CA77}')
+b = container.get('gPLink')
+print 'The domain gpLink is %s' % b
+b = str(b)
+b.replace('LDAP://cn=', 'v')
+b.replace('LDAP://CN=', 'v')
+print type(b)
+
+splitted = b.split(']')
+#splitted = splitted.split('[LDAP://cn=')
+#print splitted
+for i in splitted:
+	print i[11:49]
+	if i[11:49] == '{4768660C-5529-4713-82D9-8EEB704862D0}':
+		print 'carla'
+print test_ldb.get_domain_sid()
+'''
diff --git a/source4/scripting/bin/informant3 b/source4/scripting/bin/informant3
new file mode 100644
index 0000000..5ceacd4
--- /dev/null
+++ b/source4/scripting/bin/informant3
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+# Copyright Luke Morrison <luc785 at .hotmail.com> 2013
+
+
+import os
+import fcntl
+import sys
+import tempfile
+import subprocess
+import re
+sys.path.insert(0, "bin/python")
+from samba.dcerpc import security
+from samba.provision import (provision_paths_from_lp)
+from samba import Ldb
+import samba
+import optparse
+from samba import getopt as options
+from samba.gpclass import *
+from samba.netcmd import gpo as gpo_user
+
+
+#Finds all GPO Files ending in inf
+def gp_path_list(path):
+
+	GPO_LIST = []
+	for ext in gp_extensions:
+		GPO_LIST.append((ext, ext.list(path)))
+
+	return GPO_LIST
+
+#Reads the GPOs and sends them to their proper handlers
+def gpo_parser(GPO_LIST, ldb):
+	for entry in GPO_LIST:
+		(ext, thefile) = entry
+		ext.parse(thefile, ldb)
+parser = optparse.OptionParser("testsearchdn [options]")
+
+sambaopts = options.SambaOptions(parser)
+
+parser.add_option_group(sambaopts)
+parser.add_option_group(options.VersionOptions(parser))
+
+credopts = options.CredentialsOptions(parser)
+
+parser.add_option("-H", dest = "url", help="URL for the samdb")
+
+parser.add_option_group(credopts)
+
+opts, args = parser.parse_args()
+lp = sambaopts.get_loadparm()
+
+smbconf = lp.configfile
+creds = credopts.get_credentials(lp)
+
+session = system_session()
+
+if not opts.url:
+    url = lp.samdb_url()
+else:
+    url = opts.url
+
+#########################
+#Inialize Samba Database#
+#########################
+test_ldb = SamDB(url, session_info=session,
+ credentials=creds,lp=lp)
+
+schemadn = test_ldb.get_schema_basedn()
+
+basedn = test_ldb.get_default_basedn()
+
+path = '%s/%s/%s' % (lp.get("path", "sysvol"), lp.get("realm").lower(), 'Policies')
+guid_list = os.listdir(path)
+msg = gpo_user.get_gpo_containers(test_ldb, '{26C47ABE-688A-43F7-8BF0-4B2B4207CA77}')
+for i in msg:
+	print '\n'
+	print i.get('dn')
diff --git a/source4/scripting/bin/samba_gpoupdate b/source4/scripting/bin/samba_gpoupdate
new file mode 100755
index 0000000..4f5c692
--- /dev/null
+++ b/source4/scripting/bin/samba_gpoupdate
@@ -0,0 +1,113 @@
+#!/usr/bin/env python
+# Copyright Luke Morrison <luc785 at .hotmail.com> 2013
+
+'''This script reads a log file of previous GPO, gets all GPO from sysvol
+and sorts them by container. Then, it applies the ones that haven't been
+applied, have changed, or is in the right container'''
+
+import os
+import fcntl
+import sys
+import tempfile
+import subprocess
+
+sys.path.insert(0, "bin/python")
+
+import samba
+import optparse
+from samba import getopt as options
+from samba.gpclass import *
+
+# Finds all GPO Files ending in inf
+def gp_path_list(path):
+
+    GPO_LIST = []
+    for ext in gp_extensions:
+        GPO_LIST.append((ext, ext.list(path)))
+
+    return GPO_LIST
+
+# Reads the GPOs and sends them to their proper handlers
+def gpo_parser(GPO_LIST, ldb):
+    for entry in GPO_LIST:
+        (ext, thefile) = entry
+        ext.parse(thefile, ldb)
+
+
+parser = optparse.OptionParser("testsearchdn [options]")
+
+sambaopts = options.SambaOptions(parser)
+
+parser.add_option_group(sambaopts)
+parser.add_option_group(options.VersionOptions(parser))
+
+credopts = options.CredentialsOptions(parser)
+
+parser.add_option("-H", dest = "url", help="URL for the samdb")
+
+parser.add_option_group(credopts)
+
+opts, args = parser.parse_args()
+lp = sambaopts.get_loadparm()
+
+smbconf = lp.configfile
+creds = credopts.get_credentials(lp)
+
+session = system_session()
+
+if not opts.url:
+    url = lp.samdb_url()
+else:
+    url = opts.url
+
+#########################
+#Inialize Samba Database#
+#########################
+
+test_ldb = SamDB(url, session_info=session,
+                 credentials=creds,lp=lp)
+
+schemadn = test_ldb.get_schema_basedn()
+
+basedn = test_ldb.get_default_basedn()
+
+'''Will need sysvol to write a basic GUID version dynamic log file'''
+path = '%s/%s/%s' % (lp.get("path", "sysvol"), lp.get("realm").lower(), 'Policies')
+sys_log = '%s/%s' % (lp.get("path", "sysvol"), 'syslog.txt')
+
+'''Returns dict from previous logfile, then scraps the logfile '''
+previous_scanned_version = {'a' : 4}
+if os.path.isfile(sys_log):
+    previous_scanned_version = scan_log(sys_log)
+sys_log = open(sys_log, "w")
+
+'''Establishes the hierarchy TODO - insert the link fom Microsoft and vouch why we dont care about site or local'''
+specific_ou = "OU=Domain Controllers"
+'''TODO Definitely get DC from Samba'''
+global_dn = test_ldb.domain_dn()
+print 'The global DN for this domain is ' + global_dn
+DC_OU = specific_ou + ',' + global_dn
+guid_list = os.listdir(path)
+#guid_list = establish_hierarchy(test_ldb, guid_list, DC_OU, global_dn)
+
+hierarchy_gpos = samba4_gpo_hierarchy(test_ldb, guid_list, DC_OU, global_dn)
+hierarchy_gpos.establish_hierarchy()
+
+
+for guid_eval in hierarchy_gpos.sorted_full:
+    guid = guid_eval[0]
+    gp_extensions = [gp_sec_ext()]
+    local_path = path + '/' + guid + '/'
+    version = gpo.gpo_get_sysvol_gpt_version(local_path)[1]
+
+    gpolist = gp_path_list(local_path)
+    print local_path
+
+    '''If an important GPO parse it. Will not parse if it has not changed, is empty, or is not in the right container'''
+    if guid_eval[1]:
+        if gpolist[0][1]:
+            if (version != previous_scanned_version.get(guid)) and (version != 0):
+                print ('GPO %s has changed' % guid)
+                gpo_parser(gpolist, test_ldb)
+
+    sys_log.write('%s %i\n' % (guid,version))
diff --git a/source4/scripting/devel/testlibgpo.py b/source4/scripting/devel/testlibgpo.py
new file mode 100644
index 0000000..69ffe3b
--- /dev/null
+++ b/source4/scripting/devel/testlibgpo.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+# Copyright Matthieu Patou <mat at matws.net> 2013
+
+import sys
+
+sys.path.insert(0, "bin/python")
+
+import samba.gpo as gpo
+#get unix path
+#Send LDAP Request - Have a place to receive it
+#Use the information to fill up some structures. Get the info.
+name_version = gpo.gpo_get_sysvol_gpt_version("/home/lukem/sambas/gsoc.samba.org/state/sysvol/gsoc.samba.org/Policies/{31B2F340-016D-11D2-945F-00C04FB984F9}")
+print name_version
+p = ads_parse_gp_ext("string")
+#Have info, then use that information to fill up a group policy structure,
+#use that structure to then identify importance
+#after some basic arithmetic evaluation is done (C wrapper for *importance_to_samba(ADS_STRUCT *ads, GROUP_POLICY_OBJECT *gpo) returns true or false
+#if it is NOT ignore here, continue next iteration, get the next GPO
+#if it is important, let us update the samba database, in terms of importance.
+#The rest will be inotify because it will maybe use the bash $ terminal to just call this script repeatedly
-- 
2.10.2




More information about the samba-technical mailing list