[PATCH 02/26] Initial commit for GPO work done by Luke Morrison

abartlet at samba.org abartlet at samba.org
Thu Jun 5 04:07:07 MDT 2014


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>
---
 lib/param/loadparm.c                  |    3 +-
 lib/param/param_table.c               |    9 +
 libgpo/gpo_filesync.c                 |  246 ++++++++
 libgpo/gpo_proto.h                    |  101 ++++
 libgpo/gpo_reg.c                      | 1039 +++++++++++++++++++++++++++++++++
 libgpo/pygpo.c                        |  638 ++++++++++++++++++++
 libgpo/pygpo.h                        |   54 ++
 libgpo/wscript_build                  |   16 +-
 pygpo.h                               |    9 +
 python/gpclass.py                     |  139 +++++
 python/samba/gpclass.py               |  333 +++++++++++
 python/samba/samdb.py                 |   18 +
 source3/libgpo/gpext/wscript_build    |    4 +
 source4/dsdb/gpo/gpo_update.c         |  182 ++++++
 source4/scripting/bin/informant       |   73 +++
 source4/scripting/bin/informant2      |  113 ++++
 source4/scripting/bin/informant3      |   77 +++
 source4/scripting/bin/samba_gpoupdate |  107 ++++
 source4/scripting/devel/testlibgpo.py |   21 +
 19 files changed, 3176 insertions(+), 6 deletions(-)
 create mode 100644 libgpo/gpo_filesync.c
 create mode 100644 libgpo/gpo_proto.h
 create mode 100644 libgpo/gpo_reg.c
 create mode 100644 libgpo/pygpo.c
 create mode 100644 libgpo/pygpo.h
 create mode 100644 pygpo.h
 create mode 100755 python/gpclass.py
 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/lib/param/loadparm.c b/lib/param/loadparm.c
index 5a0ef88..3cc8eae 100644
--- a/lib/param/loadparm.c
+++ b/lib/param/loadparm.c
@@ -2214,7 +2214,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 spoolss drsuapi dssetup unixinfo browser eventlog6 backupkey dnsserver");
-	lpcfg_do_global_parameter(lp_ctx, "server services", "s3fs rpc nbt wrepl ldap cldap kdc drepl winbind ntp_signd kcc dnsupdate dns");
+	lpcfg_do_global_parameter(lp_ctx, "server services", "s3fs rpc nbt wrepl ldap cldap kdc drepl winbind 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 */
@@ -2289,6 +2289,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/lib/param/param_table.c b/lib/param/param_table.c
index 578b154..d62d4e5 100644
--- a/lib/param/param_table.c
+++ b/lib/param/param_table.c
@@ -4247,6 +4247,15 @@ struct parm_struct parm_table[] = {
 		.flags		= FLAG_ADVANCED,
 	},
 	{
+		.label		= "gpo update command",
+		.type		= P_CMDLIST,
+		.p_class	= P_GLOBAL,
+		.offset		= GLOBAL_VAR(szGPOUpdateCommand),
+		.special	= NULL,
+		.enum_list	= NULL,
+		.flags		= FLAG_ADVANCED,
+	},
+	{
 		.label		= "dns update command",
 		.type		= P_CMDLIST,
 		.p_class	= P_GLOBAL,
diff --git a/libgpo/gpo_filesync.c b/libgpo/gpo_filesync.c
new file mode 100644
index 0000000..6e3efda
--- /dev/null
+++ b/libgpo/gpo_filesync.c
@@ -0,0 +1,246 @@
+/*
+ *  Unix SMB/CIFS implementation.
+ *  Group Policy Object Support
+ *  Copyright (C) Guenther Deschner 2006
+ *
+ *  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 "system/filesys.h"
+#include "libsmb/libsmb.h"
+#include "../libgpo/gpo.h"
+#include "libgpo/gpo_proto.h"
+
+struct sync_context {
+	TALLOC_CTX *mem_ctx;
+	struct cli_state *cli;
+	char *remote_path;
+	char *local_path;
+	char *mask;
+	uint16_t attribute;
+};
+
+static NTSTATUS gpo_sync_func(const char *mnt,
+			  struct file_info *info,
+			  const char *mask,
+			  void *state);
+
+NTSTATUS gpo_copy_file(TALLOC_CTX *mem_ctx,
+		       struct cli_state *cli,
+		       const char *nt_path,
+		       const char *unix_path)
+{
+	NTSTATUS result;
+	uint16_t fnum;
+	int fd = -1;
+	char *data = NULL;
+	static int io_bufsize = 64512;
+	int read_size = io_bufsize;
+	off_t nread = 0;
+
+	result = cli_openx(cli, nt_path, O_RDONLY, DENY_NONE, &fnum);
+	if (!NT_STATUS_IS_OK(result)) {
+		goto out;
+	}
+
+	if ((fd = open(unix_path, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) {
+		result = map_nt_error_from_unix(errno);
+		goto out;
+	}
+
+	if ((data = (char *)SMB_MALLOC(read_size)) == NULL) {
+		result = NT_STATUS_NO_MEMORY;
+		goto out;
+	}
+
+	while (1) {
+		size_t n = 0;
+
+		result = cli_read(cli, fnum, data, nread, read_size, &n);
+		if (!NT_STATUS_IS_OK(result)) {
+			goto out;
+		}
+
+		if (n == 0)
+			break;
+
+		if (write(fd, data, n) != n) {
+			break;
+		}
+
+		nread += n;
+	}
+
+	result = NT_STATUS_OK;
+
+ out:
+	SAFE_FREE(data);
+	if (fnum) {
+		cli_close(cli, fnum);
+	}
+	if (fd != -1) {
+		close(fd);
+	}
+
+	return result;
+}
+
+/****************************************************************
+ copy dir
+****************************************************************/
+
+static NTSTATUS gpo_copy_dir(const char *unix_path)
+{
+	if ((mkdir(unix_path, 0644)) < 0 && errno != EEXIST) {
+		return map_nt_error_from_unix(errno);
+	}
+
+	return NT_STATUS_OK;
+}
+
+/****************************************************************
+ sync files
+****************************************************************/
+
+static NTSTATUS gpo_sync_files(struct sync_context *ctx)
+{
+	NTSTATUS status;
+
+	DEBUG(3,("calling cli_list with mask: %s\n", ctx->mask));
+
+	status = cli_list(ctx->cli, ctx->mask, ctx->attribute, gpo_sync_func,
+			  ctx);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(1, ("listing [%s] failed with error: %s\n",
+			  ctx->mask, nt_errstr(status)));
+		return status;
+	}
+
+	return status;
+}
+
+/****************************************************************
+ syncronisation call back
+****************************************************************/
+
+static NTSTATUS gpo_sync_func(const char *mnt,
+			  struct file_info *info,
+			  const char *mask,
+			  void *state)
+{
+	NTSTATUS result;
+	struct sync_context *ctx;
+	fstring nt_filename, unix_filename;
+	fstring nt_dir, unix_dir;
+	char *old_nt_dir, *old_unix_dir;
+
+	ctx = (struct sync_context *)state;
+
+	if (strequal(info->name, ".") || strequal(info->name, "..")) {
+		return NT_STATUS_OK;
+	}
+
+	DEBUG(5,("gpo_sync_func: got mask: [%s], name: [%s]\n",
+		mask, info->name));
+
+	if (info->mode & FILE_ATTRIBUTE_DIRECTORY) {
+
+		DEBUG(3,("got dir: [%s]\n", info->name));
+
+		fstrcpy(nt_dir, ctx->remote_path);
+		fstrcat(nt_dir, "\\");
+		fstrcat(nt_dir, info->name);
+
+		fstrcpy(unix_dir, ctx->local_path);
+		fstrcat(unix_dir, "/");
+		fstrcat(unix_dir, info->name);
+
+		result = gpo_copy_dir(unix_dir);
+		if (!NT_STATUS_IS_OK(result)) {
+			DEBUG(1,("failed to copy dir: %s\n",
+				nt_errstr(result)));
+			return result;
+		}
+
+		old_nt_dir = ctx->remote_path;
+		ctx->remote_path = talloc_strdup(ctx->mem_ctx, nt_dir);
+
+		old_unix_dir = ctx->local_path;
+		ctx->local_path = talloc_strdup(ctx->mem_ctx, unix_dir);
+
+		ctx->mask = talloc_asprintf(ctx->mem_ctx,
+					"%s\\*",
+					nt_dir);
+		if (!ctx->local_path || !ctx->mask || !ctx->remote_path) {
+			DEBUG(0,("gpo_sync_func: ENOMEM\n"));
+			return NT_STATUS_NO_MEMORY;
+		}
+		result = gpo_sync_files(ctx);
+		if (!NT_STATUS_IS_OK(result)) {
+			DEBUG(0,("could not sync files\n"));
+			return result;
+		}
+
+		ctx->remote_path = old_nt_dir;
+		ctx->local_path = old_unix_dir;
+		return NT_STATUS_OK;
+	}
+
+	DEBUG(3,("got file: [%s]\n", info->name));
+
+	fstrcpy(nt_filename, ctx->remote_path);
+	fstrcat(nt_filename, "\\");
+	fstrcat(nt_filename, info->name);
+
+	fstrcpy(unix_filename, ctx->local_path);
+	fstrcat(unix_filename, "/");
+	fstrcat(unix_filename, info->name);
+
+	result = gpo_copy_file(ctx->mem_ctx, ctx->cli,
+			       nt_filename, unix_filename);
+	if (!NT_STATUS_IS_OK(result)) {
+		DEBUG(1,("failed to copy file: %s\n",
+			nt_errstr(result)));
+	}
+	return result;
+}
+
+
+/****************************************************************
+ list a remote directory and download recursivly
+****************************************************************/
+
+NTSTATUS gpo_sync_directories(TALLOC_CTX *mem_ctx,
+			      struct cli_state *cli,
+			      const char *nt_path,
+			      const char *local_path)
+{
+	struct sync_context ctx;
+
+	ctx.mem_ctx 	= mem_ctx;
+	ctx.cli 	= cli;
+	ctx.remote_path	= discard_const_p(char, nt_path);
+	ctx.local_path	= discard_const_p(char, local_path);
+	ctx.attribute 	= (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY);
+
+	ctx.mask = talloc_asprintf(mem_ctx,
+				"%s\\*",
+				nt_path);
+	if (!ctx.mask) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	return gpo_sync_files(&ctx);
+}
diff --git a/libgpo/gpo_proto.h b/libgpo/gpo_proto.h
new file mode 100644
index 0000000..fa2f873
--- /dev/null
+++ b/libgpo/gpo_proto.h
@@ -0,0 +1,101 @@
+/*
+ *  Unix SMB/CIFS implementation.
+ *  Group Policy Object Support
+ *
+ *  Copyright (C) Guenther Deschner 2006-2008
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _LIBGPO_GPO_PROTO_H_
+#define _LIBGPO_GPO_PROTO_H_
+
+/* The following definitions come from libgpo/gpo_filesync.c  */
+
+NTSTATUS gpo_copy_file(TALLOC_CTX *mem_ctx,
+		       struct cli_state *cli,
+		       const char *nt_path,
+		       const char *unix_path);
+NTSTATUS gpo_sync_directories(TALLOC_CTX *mem_ctx,
+			      struct cli_state *cli,
+			      const char *nt_path,
+			      const char *local_path);
+
+/* The following definitions come from libgpo/gpo_ini.c  */
+
+NTSTATUS parse_gpt_ini(TALLOC_CTX *mem_ctx,
+		       const char *filename,
+		       uint32_t *version,
+		       char **display_name);
+
+/* The following definitions come from libgpo/gpo_reg.c  */
+
+struct security_token *registry_create_system_token(TALLOC_CTX *mem_ctx);
+WERROR gp_init_reg_ctx(TALLOC_CTX *mem_ctx,
+		       const char *initial_path,
+		       uint32_t desired_access,
+		       const struct security_token *token,
+		       struct gp_registry_context **reg_ctx);
+void gp_free_reg_ctx(struct gp_registry_context *reg_ctx);
+WERROR gp_store_reg_subkey(TALLOC_CTX *mem_ctx,
+			   const char *subkeyname,
+			   struct registry_key *curr_key,
+			   struct registry_key **new_key);
+WERROR gp_read_reg_subkey(TALLOC_CTX *mem_ctx,
+			  struct gp_registry_context *reg_ctx,
+			  const char *subkeyname,
+			  struct registry_key **key);
+WERROR gp_store_reg_val_sz(TALLOC_CTX *mem_ctx,
+			   struct registry_key *key,
+			   const char *val_name,
+			   const char *val);
+WERROR gp_read_reg_val_sz(TALLOC_CTX *mem_ctx,
+			  struct registry_key *key,
+			  const char *val_name,
+			  const char **val);
+WERROR gp_reg_state_store(TALLOC_CTX *mem_ctx,
+			  uint32_t flags,
+			  const char *dn,
+			  const struct security_token *token,
+			  struct GROUP_POLICY_OBJECT *gpo_list);
+WERROR gp_reg_state_read(TALLOC_CTX *mem_ctx,
+			 uint32_t flags,
+			 const struct dom_sid *sid,
+			 struct GROUP_POLICY_OBJECT **gpo_list);
+WERROR gp_secure_key(TALLOC_CTX *mem_ctx,
+		     uint32_t flags,
+		     struct registry_key *key,
+		     const struct dom_sid *sid);
+void dump_reg_val(int lvl, const char *direction,
+		  const char *key, const char *subkey,
+		  struct registry_value *val);
+void dump_reg_entry(uint32_t flags,
+		    const char *dir,
+		    struct gp_registry_entry *entry);
+void dump_reg_entries(uint32_t flags,
+		      const char *dir,
+		      struct gp_registry_entry *entries,
+		      size_t num_entries);
+bool add_gp_registry_entry_to_array(TALLOC_CTX *mem_ctx,
+				    struct gp_registry_entry *entry,
+				    struct gp_registry_entry **entries,
+				    size_t *num);
+WERROR reg_apply_registry_entry(TALLOC_CTX *mem_ctx,
+				struct registry_key *root_key,
+				struct gp_registry_context *reg_ctx,
+				struct gp_registry_entry *entry,
+				const struct security_token *token,
+				uint32_t flags);
+
+#endif /* _LIBGPO_GPO_PROTO_H_ */
diff --git a/libgpo/gpo_reg.c b/libgpo/gpo_reg.c
new file mode 100644
index 0000000..aa15b69
--- /dev/null
+++ b/libgpo/gpo_reg.c
@@ -0,0 +1,1039 @@
+/*
+ *  Unix SMB/CIFS implementation.
+ *  Group Policy Object Support
+ *  Copyright (C) Guenther Deschner 2007-2008
+ *
+ *  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 "../libgpo/gpo.h"
+#include "libgpo/gpo_proto.h"
+#include "registry.h"
+#include "registry/reg_api.h"
+#include "registry/reg_backend_db.h"
+#include "registry/reg_api_util.h"
+#include "registry/reg_init_basic.h"
+#include "../libcli/security/security.h"
+#include "../libcli/registry/util_reg.h"
+
+
+/****************************************************************
+****************************************************************/
+
+struct security_token *registry_create_system_token(TALLOC_CTX *mem_ctx)
+{
+	struct security_token *token = NULL;
+
+	token = talloc_zero(mem_ctx, struct security_token);
+	if (!token) {
+		DEBUG(1,("talloc failed\n"));
+		return NULL;
+	}
+
+	token->privilege_mask = SE_ALL_PRIVS;
+
+	if (!NT_STATUS_IS_OK(add_sid_to_array(token, &global_sid_System,
+			 &token->sids, &token->num_sids))) {
+		DEBUG(1,("Error adding nt-authority system sid to token\n"));
+		return NULL;
+	}
+
+	return token;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR gp_init_reg_ctx(TALLOC_CTX *mem_ctx,
+		       const char *initial_path,
+		       uint32_t desired_access,
+		       const struct security_token *token,
+		       struct gp_registry_context **reg_ctx)
+{
+	struct gp_registry_context *tmp_ctx;
+	WERROR werr;
+
+	if (!reg_ctx) {
+		return WERR_INVALID_PARAM;
+	}
+
+	werr = registry_init_basic();
+	if (!W_ERROR_IS_OK(werr)) {
+		return werr;
+	}
+
+	tmp_ctx = talloc_zero(mem_ctx, struct gp_registry_context);
+	W_ERROR_HAVE_NO_MEMORY(tmp_ctx);
+
+	if (token) {
+		tmp_ctx->token = token;
+	} else {
+		tmp_ctx->token = registry_create_system_token(mem_ctx);
+	}
+	if (!tmp_ctx->token) {
+		TALLOC_FREE(tmp_ctx);
+		return WERR_NOMEM;
+	}
+
+	werr = regdb_open();
+	if (!W_ERROR_IS_OK(werr)) {
+		return werr;
+	}
+
+	if (initial_path) {
+		tmp_ctx->path = talloc_strdup(mem_ctx, initial_path);
+		if (!tmp_ctx->path) {
+			TALLOC_FREE(tmp_ctx);
+			return WERR_NOMEM;
+		}
+
+		werr = reg_open_path(mem_ctx, tmp_ctx->path, desired_access,
+				     tmp_ctx->token, &tmp_ctx->curr_key);
+		if (!W_ERROR_IS_OK(werr)) {
+			TALLOC_FREE(tmp_ctx);
+			return werr;
+		}
+	}
+
+	*reg_ctx = tmp_ctx;
+
+	return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+void gp_free_reg_ctx(struct gp_registry_context *reg_ctx)
+{
+	TALLOC_FREE(reg_ctx);
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR gp_store_reg_subkey(TALLOC_CTX *mem_ctx,
+			   const char *subkeyname,
+			   struct registry_key *curr_key,
+			   struct registry_key **new_key)
+{
+	enum winreg_CreateAction action = REG_ACTION_NONE;
+	WERROR werr;
+
+	werr = reg_createkey(mem_ctx, curr_key, subkeyname,
+			     REG_KEY_WRITE, new_key, &action);
+	if (W_ERROR_IS_OK(werr) && (action != REG_CREATED_NEW_KEY)) {
+		return WERR_OK;
+	}
+
+	return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR gp_read_reg_subkey(TALLOC_CTX *mem_ctx,
+			  struct gp_registry_context *reg_ctx,
+			  const char *subkeyname,
+			  struct registry_key **key)
+{
+	const char *tmp = NULL;
+
+	if (!reg_ctx || !subkeyname || !key) {
+		return WERR_INVALID_PARAM;
+	}
+
+	tmp = talloc_asprintf(mem_ctx, "%s\\%s", reg_ctx->path, subkeyname);
+	W_ERROR_HAVE_NO_MEMORY(tmp);
+
+	return reg_open_path(mem_ctx, tmp, REG_KEY_READ,
+			     reg_ctx->token, key);
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR gp_store_reg_val_sz(TALLOC_CTX *mem_ctx,
+			   struct registry_key *key,
+			   const char *val_name,
+			   const char *val)
+{
+	struct registry_value reg_val;
+
+	reg_val.type = REG_SZ;
+	if (!push_reg_sz(mem_ctx, &reg_val.data, val)) {
+		return WERR_NOMEM;
+	}
+
+	return reg_setvalue(key, val_name, &reg_val);
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR gp_store_reg_val_dword(TALLOC_CTX *mem_ctx,
+				     struct registry_key *key,
+				     const char *val_name,
+				     uint32_t val)
+{
+	struct registry_value reg_val;
+
+	reg_val.type = REG_DWORD;
+	reg_val.data = data_blob_talloc(mem_ctx, NULL, 4);
+	SIVAL(reg_val.data.data, 0, val);
+
+	return reg_setvalue(key, val_name, &reg_val);
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR gp_read_reg_val_sz(TALLOC_CTX *mem_ctx,
+			  struct registry_key *key,
+			  const char *val_name,
+			  const char **val)
+{
+	WERROR werr;
+	struct registry_value *reg_val = NULL;
+
+	werr = reg_queryvalue(mem_ctx, key, val_name, &reg_val);
+	W_ERROR_NOT_OK_RETURN(werr);
+
+	if (reg_val->type != REG_SZ) {
+		return WERR_INVALID_DATATYPE;
+	}
+
+	if (!pull_reg_sz(mem_ctx, &reg_val->data, val)) {
+		return WERR_NOMEM;
+	}
+
+	return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR gp_read_reg_val_dword(TALLOC_CTX *mem_ctx,
+				    struct registry_key *key,
+				    const char *val_name,
+				    uint32_t *val)
+{
+	WERROR werr;
+	struct registry_value *reg_val = NULL;
+
+	werr = reg_queryvalue(mem_ctx, key, val_name, &reg_val);
+	W_ERROR_NOT_OK_RETURN(werr);
+
+	if (reg_val->type != REG_DWORD) {
+		return WERR_INVALID_DATATYPE;
+	}
+
+	if (reg_val->data.length < 4) {
+		return WERR_INSUFFICIENT_BUFFER;
+	}
+	*val = IVAL(reg_val->data.data, 0);
+
+	return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR gp_store_reg_gpovals(TALLOC_CTX *mem_ctx,
+				   struct registry_key *key,
+				   struct GROUP_POLICY_OBJECT *gpo)
+{
+	WERROR werr;
+
+	if (!key || !gpo) {
+		return WERR_INVALID_PARAM;
+	}
+
+	werr = gp_store_reg_val_dword(mem_ctx, key, "Version",
+				      gpo->version);
+	W_ERROR_NOT_OK_RETURN(werr);
+
+	werr = gp_store_reg_val_dword(mem_ctx, key, "WQLFilterPass",
+				      true); /* fake */
+	W_ERROR_NOT_OK_RETURN(werr);
+
+	werr = gp_store_reg_val_dword(mem_ctx, key, "AccessDenied",
+				      false); /* fake */
+	W_ERROR_NOT_OK_RETURN(werr);
+
+	werr = gp_store_reg_val_dword(mem_ctx, key, "GPO-Disabled",
+				      (gpo->options & GPO_FLAG_DISABLE));
+	W_ERROR_NOT_OK_RETURN(werr);
+
+	werr = gp_store_reg_val_dword(mem_ctx, key, "Options",
+				      gpo->options);
+	W_ERROR_NOT_OK_RETURN(werr);
+
+	werr = gp_store_reg_val_sz(mem_ctx, key, "GPOID",
+				   gpo->name);
+	W_ERROR_NOT_OK_RETURN(werr);
+
+	werr = gp_store_reg_val_sz(mem_ctx, key, "SOM",
+				   gpo->link);
+	W_ERROR_NOT_OK_RETURN(werr);
+
+	werr = gp_store_reg_val_sz(mem_ctx, key, "DisplayName",
+				   gpo->display_name);
+	W_ERROR_NOT_OK_RETURN(werr);
+
+	werr = gp_store_reg_val_sz(mem_ctx, key, "WQL-Id",
+				   NULL);
+	W_ERROR_NOT_OK_RETURN(werr);
+
+	return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+static const char *gp_reg_groupmembership_path(TALLOC_CTX *mem_ctx,
+					       const struct dom_sid *sid,
+					       uint32_t flags)
+{
+	if (flags & GPO_LIST_FLAG_MACHINE) {
+		return "GroupMembership";
+	}
+
+	return talloc_asprintf(mem_ctx, "%s\\%s", sid_string_tos(sid),
+			       "GroupMembership");
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR gp_reg_del_groupmembership(TALLOC_CTX *mem_ctx,
+					 struct registry_key *key,
+					 const struct security_token *token,
+					 uint32_t flags)
+{
+	const char *path = NULL;
+
+	path = gp_reg_groupmembership_path(mem_ctx, &token->sids[0],
+					   flags);
+	W_ERROR_HAVE_NO_MEMORY(path);
+
+	return reg_deletekey_recursive(key, path);
+
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR gp_reg_store_groupmembership(TALLOC_CTX *mem_ctx,
+					   struct gp_registry_context *reg_ctx,
+					   const struct security_token *token,
+					   uint32_t flags)
+{
+	struct registry_key *key = NULL;
+	WERROR werr;
+	int i = 0;
+	const char *valname = NULL;
+	const char *path = NULL;
+	const char *val = NULL;
+	int count = 0;
+
+	path = gp_reg_groupmembership_path(mem_ctx, &token->sids[0],
+					   flags);
+	W_ERROR_HAVE_NO_MEMORY(path);
+
+	gp_reg_del_groupmembership(mem_ctx, reg_ctx->curr_key, token, flags);
+
+	werr = gp_store_reg_subkey(mem_ctx, path,
+				   reg_ctx->curr_key, &key);
+	W_ERROR_NOT_OK_RETURN(werr);
+
+	for (i=0; i<token->num_sids; i++) {
+
+		valname = talloc_asprintf(mem_ctx, "Group%d", count++);
+		W_ERROR_HAVE_NO_MEMORY(valname);
+
+		val = sid_string_talloc(mem_ctx, &token->sids[i]);
+		W_ERROR_HAVE_NO_MEMORY(val);
+		werr = gp_store_reg_val_sz(mem_ctx, key, valname, val);
+		W_ERROR_NOT_OK_RETURN(werr);
+	}
+
+	werr = gp_store_reg_val_dword(mem_ctx, key, "Count", count);
+	W_ERROR_NOT_OK_RETURN(werr);
+
+	return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+#if 0
+/* not used yet */
+static WERROR gp_reg_read_groupmembership(TALLOC_CTX *mem_ctx,
+					  struct gp_registry_context *reg_ctx,
+					  const struct dom_sid *object_sid,
+					  struct security_token **token,
+					  uint32_t flags)
+{
+	struct registry_key *key = NULL;
+	WERROR werr;
+	int i = 0;
+	const char *valname = NULL;
+	const char *val = NULL;
+	const char *path = NULL;
+	uint32_t count = 0;
+	int num_token_sids = 0;
+	struct security_token *tmp_token = NULL;
+
+	tmp_token = talloc_zero(mem_ctx, struct security_token);
+	W_ERROR_HAVE_NO_MEMORY(tmp_token);
+
+	path = gp_reg_groupmembership_path(mem_ctx, object_sid, flags);
+	W_ERROR_HAVE_NO_MEMORY(path);
+
+	werr = gp_read_reg_subkey(mem_ctx, reg_ctx, path, &key);
+	W_ERROR_NOT_OK_RETURN(werr);
+
+	werr = gp_read_reg_val_dword(mem_ctx, key, "Count", &count);
+	W_ERROR_NOT_OK_RETURN(werr);
+
+	for (i=0; i<count; i++) {
+
+		valname = talloc_asprintf(mem_ctx, "Group%d", i);
+		W_ERROR_HAVE_NO_MEMORY(valname);
+
+		werr = gp_read_reg_val_sz(mem_ctx, key, valname, &val);
+		W_ERROR_NOT_OK_RETURN(werr);
+
+		if (!string_to_sid(&tmp_token->sids[num_token_sids++],
+				   val)) {
+			return WERR_INSUFFICIENT_BUFFER;
+		}
+	}
+
+	tmp_token->num_sids = num_token_sids;
+
+	*token = tmp_token;
+
+	return WERR_OK;
+}
+#endif
+/****************************************************************
+****************************************************************/
+
+static const char *gp_req_state_path(TALLOC_CTX *mem_ctx,
+				     const struct dom_sid *sid,
+				     uint32_t flags)
+{
+	if (flags & GPO_LIST_FLAG_MACHINE) {
+		return GPO_REG_STATE_MACHINE;
+	}
+
+	return talloc_asprintf(mem_ctx, "%s\\%s", "State", sid_string_tos(sid));
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR gp_del_reg_state(TALLOC_CTX *mem_ctx,
+			       struct registry_key *key,
+			       const char *path)
+{
+	return reg_deletesubkeys_recursive(key, path);
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR gp_reg_state_store(TALLOC_CTX *mem_ctx,
+			  uint32_t flags,
+			  const char *dn,
+			  const struct security_token *token,
+			  struct GROUP_POLICY_OBJECT *gpo_list)
+{
+	struct gp_registry_context *reg_ctx = NULL;
+	WERROR werr = WERR_GENERAL_FAILURE;
+	const char *subkeyname = NULL;
+	struct GROUP_POLICY_OBJECT *gpo;
+	int count = 0;
+	struct registry_key *key;
+
+	werr = gp_init_reg_ctx(mem_ctx, KEY_GROUP_POLICY, REG_KEY_WRITE,
+			       token, &reg_ctx);
+	W_ERROR_NOT_OK_RETURN(werr);
+
+	werr = gp_secure_key(mem_ctx, flags, reg_ctx->curr_key,
+			     &token->sids[0]);
+	if (!W_ERROR_IS_OK(werr)) {
+		DEBUG(0,("failed to secure key: %s\n", win_errstr(werr)));
+		goto done;
+	}
+
+	werr = gp_reg_store_groupmembership(mem_ctx, reg_ctx, token, flags);
+	if (!W_ERROR_IS_OK(werr)) {
+		DEBUG(0,("failed to store group membership: %s\n", win_errstr(werr)));
+		goto done;
+	}
+
+	subkeyname = gp_req_state_path(mem_ctx, &token->sids[0], flags);
+	if (!subkeyname) {
+		werr = WERR_NOMEM;
+		goto done;
+	}
+
+	werr = gp_del_reg_state(mem_ctx, reg_ctx->curr_key, subkeyname);
+	if (!W_ERROR_IS_OK(werr)) {
+		DEBUG(0,("failed to delete old state: %s\n", win_errstr(werr)));
+		/* goto done; */
+	}
+
+	werr = gp_store_reg_subkey(mem_ctx, subkeyname,
+				   reg_ctx->curr_key, &reg_ctx->curr_key);
+	if (!W_ERROR_IS_OK(werr)) {
+		goto done;
+	}
+
+	werr = gp_store_reg_val_sz(mem_ctx, reg_ctx->curr_key,
+				   "Distinguished-Name", dn);
+	if (!W_ERROR_IS_OK(werr)) {
+		goto done;
+	}
+
+	/* store link list */
+
+	werr = gp_store_reg_subkey(mem_ctx, "GPLink-List",
+				   reg_ctx->curr_key, &key);
+	if (!W_ERROR_IS_OK(werr)) {
+		goto done;
+	}
+
+	/* store gpo list */
+
+	werr = gp_store_reg_subkey(mem_ctx, "GPO-List",
+				   reg_ctx->curr_key, &reg_ctx->curr_key);
+	if (!W_ERROR_IS_OK(werr)) {
+		goto done;
+	}
+
+	for (gpo = gpo_list; gpo; gpo = gpo->next) {
+
+		subkeyname = talloc_asprintf(mem_ctx, "%d", count++);
+		if (!subkeyname) {
+			werr = WERR_NOMEM;
+			goto done;
+		}
+
+		werr = gp_store_reg_subkey(mem_ctx, subkeyname,
+					   reg_ctx->curr_key, &key);
+		if (!W_ERROR_IS_OK(werr)) {
+			goto done;
+		}
+
+		werr = gp_store_reg_gpovals(mem_ctx, key, gpo);
+		if (!W_ERROR_IS_OK(werr)) {
+			DEBUG(0,("gp_reg_state_store: "
+				"gpo_store_reg_gpovals failed for %s: %s\n",
+				gpo->display_name, win_errstr(werr)));
+			goto done;
+		}
+	}
+ done:
+	gp_free_reg_ctx(reg_ctx);
+	return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR gp_read_reg_gpovals(TALLOC_CTX *mem_ctx,
+				  struct registry_key *key,
+				  struct GROUP_POLICY_OBJECT *gpo)
+{
+	WERROR werr;
+
+	if (!key || !gpo) {
+		return WERR_INVALID_PARAM;
+	}
+
+	werr = gp_read_reg_val_dword(mem_ctx, key, "Version",
+				     &gpo->version);
+	W_ERROR_NOT_OK_RETURN(werr);
+
+	werr = gp_read_reg_val_dword(mem_ctx, key, "Options",
+				     &gpo->options);
+	W_ERROR_NOT_OK_RETURN(werr);
+
+	werr = gp_read_reg_val_sz(mem_ctx, key, "GPOID",
+				  &gpo->name);
+	W_ERROR_NOT_OK_RETURN(werr);
+
+	werr = gp_read_reg_val_sz(mem_ctx, key, "SOM",
+				  &gpo->link);
+	W_ERROR_NOT_OK_RETURN(werr);
+
+	werr = gp_read_reg_val_sz(mem_ctx, key, "DisplayName",
+				  &gpo->display_name);
+	W_ERROR_NOT_OK_RETURN(werr);
+
+	return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR gp_read_reg_gpo(TALLOC_CTX *mem_ctx,
+			      struct registry_key *key,
+			      struct GROUP_POLICY_OBJECT **gpo_ret)
+{
+	struct GROUP_POLICY_OBJECT *gpo = NULL;
+	WERROR werr;
+
+	if (!gpo_ret || !key) {
+		return WERR_INVALID_PARAM;
+	}
+
+	gpo = talloc_zero(mem_ctx, struct GROUP_POLICY_OBJECT);
+	W_ERROR_HAVE_NO_MEMORY(gpo);
+
+	werr = gp_read_reg_gpovals(mem_ctx, key, gpo);
+	W_ERROR_NOT_OK_RETURN(werr);
+
+	*gpo_ret = gpo;
+
+	return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR gp_reg_state_read(TALLOC_CTX *mem_ctx,
+			 uint32_t flags,
+			 const struct dom_sid *sid,
+			 struct GROUP_POLICY_OBJECT **gpo_list)
+{
+	struct gp_registry_context *reg_ctx = NULL;
+	WERROR werr = WERR_GENERAL_FAILURE;
+	const char *subkeyname = NULL;
+	struct GROUP_POLICY_OBJECT *gpo = NULL;
+	int count = 0;
+	struct registry_key *key = NULL;
+	const char *path = NULL;
+	const char *gp_state_path = NULL;
+
+	if (!gpo_list) {
+		return WERR_INVALID_PARAM;
+	}
+
+	ZERO_STRUCTP(gpo_list);
+
+	gp_state_path = gp_req_state_path(mem_ctx, sid, flags);
+	if (!gp_state_path) {
+		werr = WERR_NOMEM;
+		goto done;
+	}
+
+	path = talloc_asprintf(mem_ctx, "%s\\%s\\%s",
+			       KEY_GROUP_POLICY,
+			       gp_state_path,
+			       "GPO-List");
+	if (!path) {
+		werr = WERR_NOMEM;
+		goto done;
+	}
+
+	werr = gp_init_reg_ctx(mem_ctx, path, REG_KEY_READ, NULL, &reg_ctx);
+	if (!W_ERROR_IS_OK(werr)) {
+		goto done;
+	}
+
+	while (1) {
+
+		subkeyname = talloc_asprintf(mem_ctx, "%d", count++);
+		if (!subkeyname) {
+			werr = WERR_NOMEM;
+			goto done;
+		}
+
+		werr = gp_read_reg_subkey(mem_ctx, reg_ctx, subkeyname, &key);
+		if (W_ERROR_EQUAL(werr, WERR_BADFILE)) {
+			werr = WERR_OK;
+			break;
+		}
+		if (!W_ERROR_IS_OK(werr)) {
+			DEBUG(0,("gp_reg_state_read: "
+				"gp_read_reg_subkey gave: %s\n",
+				win_errstr(werr)));
+			goto done;
+		}
+
+		werr = gp_read_reg_gpo(mem_ctx, key, &gpo);
+		if (!W_ERROR_IS_OK(werr)) {
+			goto done;
+		}
+
+		DLIST_ADD(*gpo_list, gpo);
+	}
+
+ done:
+	gp_free_reg_ctx(reg_ctx);
+	return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR gp_reg_generate_sd(TALLOC_CTX *mem_ctx,
+				 const struct dom_sid *sid,
+				 struct security_descriptor **sd,
+				 size_t *sd_size)
+{
+	struct security_ace ace[6];
+	uint32_t mask;
+
+	struct security_acl *theacl = NULL;
+
+	uint8_t inherit_flags;
+
+	mask = REG_KEY_ALL;
+	init_sec_ace(&ace[0],
+		     &global_sid_System,
+		     SEC_ACE_TYPE_ACCESS_ALLOWED,
+		     mask, 0);
+
+	mask = REG_KEY_ALL;
+	init_sec_ace(&ace[1],
+		     &global_sid_Builtin_Administrators,
+		     SEC_ACE_TYPE_ACCESS_ALLOWED,
+		     mask, 0);
+
+	mask = REG_KEY_READ;
+	init_sec_ace(&ace[2],
+		     sid ? sid : &global_sid_Authenticated_Users,
+		     SEC_ACE_TYPE_ACCESS_ALLOWED,
+		     mask, 0);
+
+	inherit_flags = SEC_ACE_FLAG_OBJECT_INHERIT |
+			SEC_ACE_FLAG_CONTAINER_INHERIT |
+			SEC_ACE_FLAG_INHERIT_ONLY;
+
+	mask = REG_KEY_ALL;
+	init_sec_ace(&ace[3],
+		     &global_sid_System,
+		     SEC_ACE_TYPE_ACCESS_ALLOWED,
+		     mask, inherit_flags);
+
+	mask = REG_KEY_ALL;
+	init_sec_ace(&ace[4],
+		     &global_sid_Builtin_Administrators,
+		     SEC_ACE_TYPE_ACCESS_ALLOWED,
+		     mask, inherit_flags);
+
+	mask = REG_KEY_READ;
+	init_sec_ace(&ace[5],
+		     sid ? sid : &global_sid_Authenticated_Users,
+		     SEC_ACE_TYPE_ACCESS_ALLOWED,
+		     mask, inherit_flags);
+
+	theacl = make_sec_acl(mem_ctx, NT4_ACL_REVISION, 6, ace);
+	W_ERROR_HAVE_NO_MEMORY(theacl);
+
+	*sd = make_sec_desc(mem_ctx, SD_REVISION,
+			    SEC_DESC_SELF_RELATIVE |
+			    SEC_DESC_DACL_AUTO_INHERITED | /* really ? */
+			    SEC_DESC_DACL_AUTO_INHERIT_REQ, /* really ? */
+			    NULL, NULL, NULL,
+			    theacl, sd_size);
+	W_ERROR_HAVE_NO_MEMORY(*sd);
+
+	return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR gp_secure_key(TALLOC_CTX *mem_ctx,
+		     uint32_t flags,
+		     struct registry_key *key,
+		     const struct dom_sid *sid)
+{
+	struct security_descriptor *sd = NULL;
+	size_t sd_size = 0;
+	const struct dom_sid *sd_sid = NULL;
+	WERROR werr;
+
+	if (!(flags & GPO_LIST_FLAG_MACHINE)) {
+		sd_sid = sid;
+	}
+
+	werr = gp_reg_generate_sd(mem_ctx, sd_sid, &sd, &sd_size);
+	W_ERROR_NOT_OK_RETURN(werr);
+
+	return reg_setkeysecurity(key, sd);
+}
+
+/****************************************************************
+****************************************************************/
+
+void dump_reg_val(int lvl, const char *direction,
+		  const char *key, const char *subkey,
+		  struct registry_value *val)
+{
+	int i = 0;
+	const char *type_str = NULL;
+
+	if (!val) {
+		DEBUG(lvl,("no val!\n"));
+		return;
+	}
+
+	type_str = str_regtype(val->type);
+
+	DEBUG(lvl,("\tdump_reg_val:\t%s '%s'\n\t\t\t'%s' %s: ",
+		direction, key, subkey, type_str));
+
+	switch (val->type) {
+		case REG_DWORD: {
+			uint32_t v;
+			if (val->data.length < 4) {
+				break;
+			}
+			v = IVAL(val->data.data, 0);
+			DEBUG(lvl,("%d (0x%08x)\n",
+				(int)v, v));
+			break;
+		}
+		case REG_QWORD: {
+			uint64_t v;
+			if (val->data.length < 8) {
+				break;
+			}
+			v = BVAL(val->data.data, 0);
+			DEBUG(lvl,("%d (0x%016llx)\n",
+				(int)v,
+				(unsigned long long)v));
+			break;
+		}
+		case REG_SZ: {
+			const char *s;
+			if (!pull_reg_sz(talloc_tos(), &val->data, &s)) {
+				break;
+			}
+			DEBUG(lvl,("%s (length: %d)\n",
+				   s, (int)strlen_m(s)));
+			break;
+		}
+		case REG_MULTI_SZ: {
+			const char **a;
+			if (!pull_reg_multi_sz(talloc_tos(), &val->data, &a)) {
+				break;
+			}
+			for (i=0; a[i] != NULL; i++) {
+				;;
+			}
+			DEBUG(lvl,("(num_strings: %d)\n", i));
+			for (i=0; a[i] != NULL; i++) {
+				DEBUGADD(lvl,("\t%s\n", a[i]));
+			}
+			break;
+		}
+		case REG_NONE:
+			DEBUG(lvl,("\n"));
+			break;
+		case REG_BINARY:
+			dump_data(lvl, val->data.data,
+				  val->data.length);
+			break;
+		default:
+			DEBUG(lvl,("unsupported type: %d\n", val->type));
+			break;
+	}
+}
+
+/****************************************************************
+****************************************************************/
+
+void dump_reg_entry(uint32_t flags,
+		    const char *dir,
+		    struct gp_registry_entry *entry)
+{
+	if (!(flags & GPO_INFO_FLAG_VERBOSE))
+		return;
+
+	dump_reg_val(1, dir,
+		     entry->key,
+		     entry->value,
+		     entry->data);
+}
+
+/****************************************************************
+****************************************************************/
+
+void dump_reg_entries(uint32_t flags,
+		      const char *dir,
+		      struct gp_registry_entry *entries,
+		      size_t num_entries)
+{
+	size_t i;
+
+	if (!(flags & GPO_INFO_FLAG_VERBOSE))
+		return;
+
+	for (i=0; i < num_entries; i++) {
+		dump_reg_entry(flags, dir, &entries[i]);
+	}
+}
+
+/****************************************************************
+****************************************************************/
+
+bool add_gp_registry_entry_to_array(TALLOC_CTX *mem_ctx,
+				    struct gp_registry_entry *entry,
+				    struct gp_registry_entry **entries,
+				    size_t *num)
+{
+	*entries = talloc_realloc(mem_ctx, *entries,
+					struct gp_registry_entry,
+					(*num)+1);
+
+	if (*entries == NULL) {
+		*num = 0;
+		return false;
+	}
+
+	(*entries)[*num].action = entry->action;
+	(*entries)[*num].key = entry->key;
+	(*entries)[*num].value = entry->value;
+	(*entries)[*num].data = entry->data;
+
+	*num += 1;
+	return true;
+}
+
+/****************************************************************
+****************************************************************/
+
+static const char *gp_reg_action_str(enum gp_reg_action action)
+{
+	switch (action) {
+		case GP_REG_ACTION_NONE:
+			return "GP_REG_ACTION_NONE";
+		case GP_REG_ACTION_ADD_VALUE:
+			return "GP_REG_ACTION_ADD_VALUE";
+		case GP_REG_ACTION_ADD_KEY:
+			return "GP_REG_ACTION_ADD_KEY";
+		case GP_REG_ACTION_DEL_VALUES:
+			return "GP_REG_ACTION_DEL_VALUES";
+		case GP_REG_ACTION_DEL_VALUE:
+			return "GP_REG_ACTION_DEL_VALUE";
+		case GP_REG_ACTION_DEL_ALL_VALUES:
+			return "GP_REG_ACTION_DEL_ALL_VALUES";
+		case GP_REG_ACTION_DEL_KEYS:
+			return "GP_REG_ACTION_DEL_KEYS";
+		case GP_REG_ACTION_SEC_KEY_SET:
+			return "GP_REG_ACTION_SEC_KEY_SET";
+		case GP_REG_ACTION_SEC_KEY_RESET:
+			return "GP_REG_ACTION_SEC_KEY_RESET";
+		default:
+			return "unknown";
+	}
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR reg_apply_registry_entry(TALLOC_CTX *mem_ctx,
+				struct registry_key *root_key,
+				struct gp_registry_context *reg_ctx,
+				struct gp_registry_entry *entry,
+				const struct security_token *token,
+				uint32_t flags)
+{
+	WERROR werr;
+	struct registry_key *key = NULL;
+
+	if (flags & GPO_INFO_FLAG_VERBOSE) {
+		printf("about to store key:    [%s]\n", entry->key);
+		printf("               value:  [%s]\n", entry->value);
+		printf("               data:   [%s]\n", str_regtype(entry->data->type));
+		printf("               action: [%s]\n", gp_reg_action_str(entry->action));
+	}
+
+	werr = gp_store_reg_subkey(mem_ctx, entry->key,
+				   root_key, &key);
+				   /* reg_ctx->curr_key, &key); */
+	if (!W_ERROR_IS_OK(werr)) {
+		DEBUG(0,("gp_store_reg_subkey failed: %s\n", win_errstr(werr)));
+		return werr;
+	}
+
+	switch (entry->action) {
+		case GP_REG_ACTION_NONE:
+		case GP_REG_ACTION_ADD_KEY:
+			return WERR_OK;
+
+		case GP_REG_ACTION_SEC_KEY_SET:
+			werr = gp_secure_key(mem_ctx, flags,
+					     key,
+					     &token->sids[0]);
+			if (!W_ERROR_IS_OK(werr)) {
+				DEBUG(0,("reg_apply_registry_entry: "
+					"gp_secure_key failed: %s\n",
+					win_errstr(werr)));
+				return werr;
+			}
+			break;
+		case GP_REG_ACTION_ADD_VALUE:
+			werr = reg_setvalue(key, entry->value, entry->data);
+			if (!W_ERROR_IS_OK(werr)) {
+				DEBUG(0,("reg_apply_registry_entry: "
+					"reg_setvalue failed: %s\n",
+					win_errstr(werr)));
+				dump_reg_entry(flags, "STORE", entry);
+				return werr;
+			}
+			break;
+		case GP_REG_ACTION_DEL_VALUE:
+			werr = reg_deletevalue(key, entry->value);
+			if (!W_ERROR_IS_OK(werr)) {
+				DEBUG(0,("reg_apply_registry_entry: "
+					"reg_deletevalue failed: %s\n",
+					win_errstr(werr)));
+				dump_reg_entry(flags, "STORE", entry);
+				return werr;
+			}
+			break;
+		case GP_REG_ACTION_DEL_ALL_VALUES:
+			werr = reg_deleteallvalues(key);
+			if (!W_ERROR_IS_OK(werr)) {
+				DEBUG(0,("reg_apply_registry_entry: "
+					"reg_deleteallvalues failed: %s\n",
+					win_errstr(werr)));
+				dump_reg_entry(flags, "STORE", entry);
+				return werr;
+			}
+			break;
+		case GP_REG_ACTION_DEL_VALUES:
+		case GP_REG_ACTION_DEL_KEYS:
+		case GP_REG_ACTION_SEC_KEY_RESET:
+			DEBUG(0,("reg_apply_registry_entry: "
+				"not yet supported: %s (%d)\n",
+				gp_reg_action_str(entry->action),
+				entry->action));
+			return WERR_NOT_SUPPORTED;
+		default:
+			DEBUG(0,("invalid action: %d\n", entry->action));
+			return WERR_INVALID_PARAM;
+	}
+
+	return werr;
+}
diff --git a/libgpo/pygpo.c b/libgpo/pygpo.c
new file mode 100644
index 0000000..6b7b1b2
--- /dev/null
+++ b/libgpo/pygpo.c
@@ -0,0 +1,638 @@
+/*
+   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;
+	TALLOC_CTX *tmp_ctx;
+	tmp_ctx = talloc_new(NULL);
+	const char *extension_raw;
+	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;
+	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
+	PyObject *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;
+	printf("Before the as content statement\n");
+	ADS_STRUCT *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..4b85597
--- /dev/null
+++ b/libgpo/pygpo.h
@@ -0,0 +1,54 @@
+#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 034af0a..598cfcb 100644
--- a/libgpo/wscript_build
+++ b/libgpo/wscript_build
@@ -1,7 +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..7fd588c
--- /dev/null
+++ b/pygpo.h
@@ -0,0 +1,9 @@
+#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/gpclass.py b/python/gpclass.py
new file mode 100755
index 0000000..3911f46
--- /dev/null
+++ b/python/gpclass.py
@@ -0,0 +1,139 @@
+#!/usr/bin/env python
+# Copyright Luke Morrison <luc785 at .hotmail.com> 2013
+
+'''Reads important GPO parameters and updates Samba'''
+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
+import codecs
+
+class gp_ext(object):
+	def list(self, rootpath):
+		return None
+
+	def __str__(self):
+		return "default_gp_ext"
+
+
+class inf_to_ldb(object):
+	def __init__(self, ldb, dn, attribute, val):
+		self.ldb = ldb
+		self.dn = dn
+		self.attribute = attribute
+		self.val = val
+
+	def ch_minPwdAge(self, val):
+		'''TODO change this to do only call prints on debugging option mode'''
+		print 'Old value of Minimum Password age = %s' % self.ldb.get_minPwdAge()
+
+		print 'New value of Minimum Password age = %s' % self.ldb.get_minPwdAge()
+
+
+	def ch_minPwdLength(self, val):
+		ldb = self.ldb
+		ldb.set_minPwdLength(val)
+
+	def nullstamp(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))
+
+	def mapper(self):
+		return { "minPwdAge" : (self.ch_minPwdAge, self.nttime2unix),
+			"maxPwdAge" : (self.ch_maxPwdAge, self.nttime2unix),
+			"minPwdLength" : (self.ch_minPwdLength, self.nullstamp)
+			}
+
+	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):
+	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":None
+					 }
+					 }
+
+	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:
+		#42
+			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()
+				print "key = %s" % key
+				if current_section.get(key):
+					print "ok I have to do something on with key %s" % key
+					(att, setter) = current_section.get(key)
+					value = value.encode('ascii', 'ignore')
+					setter(self.ldb, self.dn, att, value).update_samba()
+
+	def parse(self, afile, ldb):
+		self.ldb = ldb
+		self.dn = ldb.get_default_basedn()
+		print "Parsing file %s" % afile
+		if afile.endswith('inf'):
+			self.read_inf(afile)
+
+#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)
diff --git a/python/samba/gpclass.py b/python/samba/gpclass.py
new file mode 100755
index 0000000..14fc3ea
--- /dev/null
+++ b/python/samba/gpclass.py
@@ -0,0 +1,333 @@
+#!/usr/bin/env python
+# Copyright Luke Morrison <luc785 at .hotmail.com> 2013
+
+'''Reads important GPO parameters and updates Samba'''
+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"
+
+'''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. Non Registry oriented whatsoever'''
+class inf_to_ldb(object):
+    def __init__(self, ldb, dn, attribute, val):
+        self.ldb = ldb
+        self.dn = dn
+        self.attribute = attribute
+        self.val = val
+
+    def ch_minPwdAge(self, val):
+        #print 'Old value of Minimum Password age = %s' % self.ldb.get_minPwdAge()
+        self.ldb.set_minPwdAge(val)
+        #print 'New value of Minimum Password age = %s' % self.ldb.get_minPwdAge()
+
+    def ch_maxPwdAge(self, val):
+        #print 'Old value of Maximum Password age = %s' % self.ldb.get_maxPwdAge()
+        self.ldb.set_maxPwdAge(val)
+        #print 'New value of Maximum Password age = %s' % self.ldb.get_maxPwdAge()
+
+    def ch_minPwdLength(self, val):
+        #print 'Password Min length before is %s ' % ldb.get_minPwdLength()
+        self.ldb.set_minPwdLength(val)
+        #print 'Password Min length after is %s ' % ldb.get_minPwdLength()
+
+    def ch_pwdProperties(self, val):
+        #print 'Old value of Minimum Password age = %s' % self.ldb.get_minPwdAge()
+        self.ldb.set_pwdProperties(val)
+        #print 'New value of Minimum Password age = %s' % self.ldb.get_minPwdAge()
+
+    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))
+
+    '''ldap value : samba setter'''
+    def mapper(self):
+        return { "minPwdAge" : (self.ch_minPwdAge, self.nttime2unix),
+             "maxPwdAge" : (self.ch_maxPwdAge, self.nttime2unix),
+             "minPwdLength" : (self.ch_minPwdLength, self.explicit), #Could be none, but I like the method assignment in update_samba
+             "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)
+
+
+'''This class does 2 things. 1) Identifies the GPO if it has a certain kind of filepath, 2) Finally parses it. '''
+class gp_sec_ext(gp_ext):
+    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):
+        #: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
+
+    def __init__(self, SamDB, sysvol_guid_list, DC_OU, GLOBAL_DN):
+	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 that show the explicit returned hierarchically sorted list'''
+
+
+'''So the original list will need to be seperated into containers. Returns indexed list of when the container changes after hierarchy'''
+def container_indexes(GUID_LIST):
+    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
+
+'''So GPO in same level need to have link level. This takes a container and sorts it'''
+def sort_linked(SAMDB, guid_list, start, end):
+    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
+    print 'the container is %s' % (right_container.get('dn'))
+    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
+
+'''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'''
+def establish_hierarchy(SamDB, GUID_LIST, DC_OU, global_dn):
+    final_list = []
+    count_unapplied_GPO = 0
+    for GUID in GUID_LIST:
+        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
+	'''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 2dfc839..b5a1230 100644
--- a/python/samba/samdb.py
+++ b/python/samba/samdb.py
@@ -786,6 +786,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..c08cf52
--- /dev/null
+++ b/source4/dsdb/gpo/gpo_update.c
@@ -0,0 +1,182 @@
+/*
+   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"
+
+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..9aef989
--- /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"), '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..f2e8ed5
--- /dev/null
+++ b/source4/scripting/bin/samba_gpoupdate
@@ -0,0 +1,107 @@
+#!/usr/bin/env python
+# Copyright Luke Morrison <luc785 at .hotmail.com> 2013
+'''This script reads log file of previous GPO, gets all GPO from sysvol, sorts them by container, and applies the ones that have not 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"), '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
-- 
1.9.3



More information about the samba-technical mailing list