summaryrefslogtreecommitdiffstats
path: root/source4/lib
diff options
context:
space:
mode:
Diffstat (limited to 'source4/lib')
-rw-r--r--source4/lib/com/README9
-rw-r--r--source4/lib/com/classes/simple.c137
-rw-r--r--source4/lib/com/com.h53
-rw-r--r--source4/lib/com/dcom/dcom.h85
-rw-r--r--source4/lib/com/dcom/main.c706
-rw-r--r--source4/lib/com/dcom/tables.c94
-rw-r--r--source4/lib/com/main.c90
-rw-r--r--source4/lib/com/rot.c35
-rw-r--r--source4/lib/com/tables.c112
-rw-r--r--source4/lib/com/wscript_build28
-rw-r--r--source4/lib/events/events.h6
-rw-r--r--source4/lib/events/tevent_s4.c41
-rw-r--r--source4/lib/events/wscript_build9
-rw-r--r--source4/lib/messaging/irpc.h86
-rw-r--r--source4/lib/messaging/messaging.c1485
-rw-r--r--source4/lib/messaging/messaging.h70
-rw-r--r--source4/lib/messaging/messaging_handlers.c135
-rw-r--r--source4/lib/messaging/messaging_internal.h50
-rw-r--r--source4/lib/messaging/messaging_send.c115
-rw-r--r--source4/lib/messaging/pymessaging.c551
-rw-r--r--source4/lib/messaging/tests/irpc.c308
-rw-r--r--source4/lib/messaging/tests/messaging.c694
-rw-r--r--source4/lib/messaging/wscript_build33
-rw-r--r--source4/lib/policy/gp_filesys.c698
-rw-r--r--source4/lib/policy/gp_ini.c133
-rw-r--r--source4/lib/policy/gp_ldap.c1130
-rw-r--r--source4/lib/policy/gp_manage.c329
-rw-r--r--source4/lib/policy/policy.h125
-rw-r--r--source4/lib/policy/pypolicy.c174
-rw-r--r--source4/lib/policy/samba-policy.pc.in12
-rw-r--r--source4/lib/policy/wscript_build22
-rw-r--r--source4/lib/registry/Doxyfile24
-rw-r--r--source4/lib/registry/README42
-rw-r--r--source4/lib/registry/TODO5
-rw-r--r--source4/lib/registry/hive.c176
-rw-r--r--source4/lib/registry/interface.c298
-rw-r--r--source4/lib/registry/ldb.c1018
-rw-r--r--source4/lib/registry/local.c408
-rw-r--r--source4/lib/registry/man/regdiff.1.xml100
-rw-r--r--source4/lib/registry/man/regpatch.1.xml89
-rw-r--r--source4/lib/registry/man/regshell.1.xml189
-rw-r--r--source4/lib/registry/man/regtree.1.xml101
-rw-r--r--source4/lib/registry/patchfile.c543
-rw-r--r--source4/lib/registry/patchfile_dotreg.c435
-rw-r--r--source4/lib/registry/patchfile_preg.c387
-rw-r--r--source4/lib/registry/pyregistry.c494
-rw-r--r--source4/lib/registry/regf.c2319
-rw-r--r--source4/lib/registry/regf.idl167
-rw-r--r--source4/lib/registry/registry.h532
-rw-r--r--source4/lib/registry/rpc.c579
-rw-r--r--source4/lib/registry/samba.c100
-rw-r--r--source4/lib/registry/tests/diff.c291
-rw-r--r--source4/lib/registry/tests/generic.c179
-rw-r--r--source4/lib/registry/tests/hive.c440
-rw-r--r--source4/lib/registry/tests/registry.c645
-rw-r--r--source4/lib/registry/tools/common.c88
-rw-r--r--source4/lib/registry/tools/regdiff.c183
-rw-r--r--source4/lib/registry/tools/regpatch.c119
-rw-r--r--source4/lib/registry/tools/regshell.c708
-rw-r--r--source4/lib/registry/tools/regtree.c209
-rw-r--r--source4/lib/registry/util.c302
-rw-r--r--source4/lib/registry/wine.c45
-rw-r--r--source4/lib/registry/wscript_build69
-rw-r--r--source4/lib/samba3/README5
-rw-r--r--source4/lib/samba3/samba3.h29
-rw-r--r--source4/lib/samba3/smbpasswd.c111
-rw-r--r--source4/lib/samba3/wscript_build9
-rw-r--r--source4/lib/socket/access.c129
-rw-r--r--source4/lib/socket/connect.c158
-rw-r--r--source4/lib/socket/connect_multi.c392
-rw-r--r--source4/lib/socket/interface.c525
-rw-r--r--source4/lib/socket/netif.h24
-rw-r--r--source4/lib/socket/socket.c640
-rw-r--r--source4/lib/socket/socket.h256
-rw-r--r--source4/lib/socket/socket_ip.c1033
-rw-r--r--source4/lib/socket/socket_unix.c436
-rw-r--r--source4/lib/socket/testsuite.c194
-rw-r--r--source4/lib/socket/wscript_build29
-rw-r--r--source4/lib/stream/packet.c626
-rw-r--r--source4/lib/stream/packet.h66
-rw-r--r--source4/lib/stream/wscript_build8
-rw-r--r--source4/lib/tls/tls.h105
-rw-r--r--source4/lib/tls/tls_tstream.c1467
-rw-r--r--source4/lib/tls/tlscert.c159
-rw-r--r--source4/lib/tls/wscript_build15
-rw-r--r--source4/lib/wmi/tools/wmic.c221
-rw-r--r--source4/lib/wmi/tools/wmis.c222
-rw-r--r--source4/lib/wmi/wbemdata.c451
-rw-r--r--source4/lib/wmi/wmi.h48
-rw-r--r--source4/lib/wmi/wmi.i352
-rw-r--r--source4/lib/wmi/wmi.py120
-rw-r--r--source4/lib/wmi/wmi_wrap.c4304
-rw-r--r--source4/lib/wmi/wmicore.c253
-rw-r--r--source4/lib/wmi/wscript_build26
94 files changed, 31252 insertions, 0 deletions
diff --git a/source4/lib/com/README b/source4/lib/com/README
new file mode 100644
index 0000000..361024e
--- /dev/null
+++ b/source4/lib/com/README
@@ -0,0 +1,9 @@
+This directory contains Samba's very simple COM implementation.
+It is by no means finished yet.
+
+The main aim of this implementation is for use by our DCOM implementation,
+which lives in the dcom subdirectory. The local version is used mostly for
+testing.
+
+More information on this effort can be found in the DCOM whitepaper in
+the lorikeet repository.
diff --git a/source4/lib/com/classes/simple.c b/source4/lib/com/classes/simple.c
new file mode 100644
index 0000000..54c74ce
--- /dev/null
+++ b/source4/lib/com/classes/simple.c
@@ -0,0 +1,137 @@
+/*
+ Unix SMB/CIFS implementation.
+ Simple class
+ Copyright (C) 2004-2005 Jelmer Vernooij <jelmer@samba.org>
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "lib/com/com.h"
+#include "librpc/gen_ndr/com_dcom.h"
+
+NTSTATUS com_simple_init(TALLOC_CTX *);
+
+static struct IClassFactory_vtable simple_classobject_vtable;
+static struct IStream_vtable simple_IStream_vtable;
+
+static WERROR simple_IUnknown_QueryInterface (struct IUnknown *d, TALLOC_CTX *mem_ctx, struct GUID *iid, struct IUnknown **iun)
+{
+ *iun = d;
+ return WERR_OK;
+}
+
+static uint32_t simple_IUnknown_AddRef (struct IUnknown *d, TALLOC_CTX *mem_ctx)
+{
+ return 1;
+}
+
+static uint32_t simple_IUnknown_Release (struct IUnknown *d, TALLOC_CTX *mem_ctx)
+{
+ return 1;
+}
+
+static WERROR simple_IStream_Read(struct IStream *d,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *pv,
+ uint32_t num_requested,
+ uint32_t *num_readx,
+ uint32_t *num_read)
+{
+ printf("%d bytes are being read\n", *num_read);
+ return WERR_OK;
+}
+
+static WERROR simple_IStream_Write(struct IStream *d,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *data,
+ uint32_t num_requested,
+ uint32_t *num_written)
+{
+ printf("%d bytes are being written\n", num_requested);
+ return WERR_OK;
+}
+
+static WERROR simpleclass_IUnknown_QueryInterface (struct IUnknown *d, TALLOC_CTX *mem_ctx, struct GUID *iid, struct IUnknown **iun)
+{
+ /* FIXME: Return WERR_IFACE_NOT_SUPPORTED if IID != IID_IUNKNOWN and IID != IID_CLASSFACTORY */
+ *iun = d;
+ return WERR_OK;
+}
+
+static WERROR simpleclass_IClassFactory_CreateInstance(struct IClassFactory *d,
+ TALLOC_CTX *mem_ctx,
+ struct MInterfacePointer *pUnknown,
+ struct GUID *iid,
+ struct MInterfacePointer **ppv)
+{
+ struct IStream *ret;
+ /* FIXME: Check whether IID == ISTREAM_IID */
+ ret = talloc(mem_ctx, struct IStream);
+ ret->ctx = NULL;
+ ret->vtable = &simple_IStream_vtable;
+ ret->object_data = NULL;
+
+ *ppv = (struct MInterfacePointer *)ret;
+
+ return WERR_OK;
+}
+
+static uint32_t simpleclass_IUnknown_AddRef (struct IUnknown *d, TALLOC_CTX *mem_ctx)
+{
+ return 1;
+}
+
+static uint32_t simpleclass_IUnknown_Release (struct IUnknown *d, TALLOC_CTX *mem_ctx)
+{
+ return 1;
+}
+
+/* Everything below this line should be autogenerated later on */
+static struct IClassFactory_vtable simple_classobject_vtable = {
+ { 0, 0, 0, { 0, 0 }, { 0, 0, 0, 0, 0, 0 } },
+ simpleclass_IUnknown_QueryInterface,
+ simpleclass_IUnknown_AddRef,
+ simpleclass_IUnknown_Release,
+ simpleclass_IClassFactory_CreateInstance,
+ NULL,
+ NULL,
+ NULL
+};
+
+static struct IStream_vtable simple_IStream_vtable = {
+ { 0, 0, 0, { 0, 0 }, { 0, 0, 0, 0, 0, 0 } },
+ simple_IUnknown_QueryInterface,
+ simple_IUnknown_AddRef,
+ simple_IUnknown_Release,
+ simple_IStream_Read,
+ simple_IStream_Write
+};
+
+NTSTATUS com_simple_init(TALLOC_CTX *ctx)
+{
+ struct GUID clsid;
+ struct IUnknown *class_object = talloc(ctx, struct IUnknown);
+
+ class_object->ctx = NULL;
+ class_object->object_data = NULL;
+ class_object->vtable = (struct IUnknown_vtable *)&simple_classobject_vtable;
+
+ GUID_from_string(CLSID_SIMPLE, &clsid);
+ GUID_from_string(COM_ICLASSFACTORY_UUID, &simple_classobject_vtable.iid);
+ GUID_from_string(COM_ISTREAM_UUID, &simple_IStream_vtable.iid);
+
+ return com_register_running_class(ctx, &clsid, PROGID_SIMPLE, class_object);
+}
diff --git a/source4/lib/com/com.h b/source4/lib/com/com.h
new file mode 100644
index 0000000..56cd210
--- /dev/null
+++ b/source4/lib/com/com.h
@@ -0,0 +1,53 @@
+/*
+ Unix SMB/CIFS implementation.
+ Utility functions for Samba
+ Copyright (C) Jelmer Vernooij 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 __SAMBA_COM_H__
+#define __SAMBA_COM_H__
+
+#include <talloc.h>
+#include "librpc/gen_ndr/misc.h"
+
+struct com_context;
+struct tevent_context;
+
+struct com_context
+{
+ struct dcom_client_context *dcom;
+ struct tevent_context *event_ctx;
+ struct com_extension {
+ uint32_t id;
+ void *data;
+ struct com_extension *prev, *next;
+ } *extensions;
+ struct loadparm_context *lp_ctx;
+};
+
+struct IUnknown *com_class_by_clsid(struct com_context *ctx, const struct GUID *clsid);
+NTSTATUS com_register_running_class(TALLOC_CTX *ctx, struct GUID *clsid, const char *progid, struct IUnknown *p);
+
+struct dcom_interface_p *dcom_get_local_iface_p(struct GUID *ipid);
+
+WERROR com_init_ctx(struct com_context **ctx, struct tevent_context *event_ctx);
+WERROR com_create_object(struct com_context *ctx, struct GUID *clsid, int num_ifaces, struct GUID *iid, struct IUnknown **ip, WERROR *results);
+WERROR com_get_class_object(struct com_context *ctx, struct GUID *clsid, struct GUID *iid, struct IUnknown **ip);
+NTSTATUS com_init(void);
+
+typedef struct IUnknown *(*get_class_object_function) (const struct GUID *clsid);
+
+#endif /* __SAMBA_COM_H__ */
diff --git a/source4/lib/com/dcom/dcom.h b/source4/lib/com/dcom/dcom.h
new file mode 100644
index 0000000..cb549b1
--- /dev/null
+++ b/source4/lib/com/dcom/dcom.h
@@ -0,0 +1,85 @@
+/*
+ Unix SMB/CIFS implementation.
+ COM standard objects
+ Copyright (C) Jelmer Vernooij 2004-2005.
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _DCOM_H /* _DCOM_H */
+#define _DCOM_H
+
+struct cli_credentials;
+struct dcerpc_pipe;
+
+#include "lib/com/com.h"
+#include "librpc/gen_ndr/orpc.h"
+
+struct dcom_client_context {
+ struct dcom_server_credentials {
+ const char *server;
+ struct cli_credentials *credentials;
+ struct dcom_server_credentials *prev, *next;
+ } *credentials;
+ struct dcom_object_exporter {
+ uint64_t oxid;
+ char *host;
+ struct IRemUnknown *rem_unknown;
+ struct DUALSTRINGARRAY *bindings;
+ struct dcerpc_pipe *pipe;
+ struct dcom_object_exporter *prev, *next;
+ } *object_exporters;
+};
+
+typedef enum ndr_err_code (*marshal_fn)(TALLOC_CTX *mem_ctx, struct IUnknown *pv, struct OBJREF *o);
+typedef enum ndr_err_code (*unmarshal_fn)(TALLOC_CTX *mem_ctx, struct OBJREF *o, struct IUnknown **pv);
+
+
+struct dcom_client_context *dcom_client_init(struct com_context *ctx, struct cli_credentials *credentials);
+struct dcom_object_exporter *object_exporter_by_oxid(struct com_context *ctx, uint64_t oxid);
+struct dcom_object_exporter *object_exporter_by_ip(struct com_context *ctx, struct IUnknown *ip);
+WERROR dcom_create_object(struct com_context *ctx, struct GUID *clsid, const char *server, int num_ifaces, struct GUID *iid, struct IUnknown ***ip, HRESULT *results);
+WERROR dcom_get_class_object(struct com_context *ctx, struct GUID *clsid, const char *server, struct GUID *iid, struct IUnknown **ip);
+NTSTATUS dcom_get_pipe(struct IUnknown *iface, struct dcerpc_pipe **pp);
+NTSTATUS dcom_OBJREF_from_IUnknown(struct OBJREF *o, struct IUnknown *p);
+NTSTATUS dcom_IUnknown_from_OBJREF(TALLOC_CTX *mem_ctx, struct com_context *ctx, struct IUnknown **_p, struct OBJREF *o);
+uint64_t dcom_get_current_oxid(void);
+void dcom_add_server_credentials(struct com_context *ctx, const char *server, struct cli_credentials *credentials);
+WERROR dcom_query_interface(struct IUnknown *d, uint32_t cRefs, uint16_t cIids, struct GUID *iids, struct IUnknown **ip, WERROR *results);
+
+#include "librpc/gen_ndr/com_dcom.h"
+
+NTSTATUS dcom_register_proxy(struct IUnknown_vtable *proxy_vtable);
+struct IUnknown_vtable *dcom_proxy_vtable_by_iid(struct GUID *iid);
+NTSTATUS dcom_register_marshal(struct GUID *clsid, marshal_fn marshal, unmarshal_fn unmarshal);
+
+#include "libcli/composite/composite.h"
+void dcom_release_continue(struct composite_context *cr);
+#define IUnknown_ipid(d) ((d)->obj.u_objref.u_standard.std.ipid)
+struct composite_context *dcom_release_send(struct IUnknown *d, TALLOC_CTX *mem_ctx);
+marshal_fn dcom_marshal_by_clsid(struct GUID *clsid);
+unmarshal_fn dcom_unmarshal_by_clsid(struct GUID *clsid);
+
+struct dcom_proxy_async_call_state {
+ struct IUnknown *d;
+ const struct ndr_interface_table *table;
+ uint32_t opnum;
+ void (*continuation)(struct rpc_request *);
+ TALLOC_CTX *mem_ctx;
+ void *r;
+};
+
+
+#endif /* _DCOM_H */
diff --git a/source4/lib/com/dcom/main.c b/source4/lib/com/dcom/main.c
new file mode 100644
index 0000000..0e9eff1
--- /dev/null
+++ b/source4/lib/com/dcom/main.c
@@ -0,0 +1,706 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main DCOM functionality
+ Copyright (C) 2004 Jelmer Vernooij <jelmer@samba.org>
+ Copyright (C) 2006 Andrzej Hajda <andrzej.hajda@wp.pl>
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "librpc/gen_ndr/epmapper.h"
+#include "librpc/gen_ndr/ndr_remact_c.h"
+#include "librpc/gen_ndr/com_dcom.h"
+#include "librpc/gen_ndr/dcom.h"
+#include "librpc/rpc/dcerpc.h"
+#include "lib/com/dcom/dcom.h"
+#include "librpc/ndr/ndr_table.h"
+#include "../lib/util/dlinklist.h"
+#include "auth/credentials/credentials.h"
+#include "libcli/composite/composite.h"
+
+#undef strncasecmp
+
+#define DCOM_NEGOTIATED_PROTOCOLS { EPM_PROTOCOL_TCP, EPM_PROTOCOL_SMB, EPM_PROTOCOL_NCALRPC }
+
+static NTSTATUS dcerpc_binding_from_STRINGBINDING(TALLOC_CTX *mem_ctx, struct dcerpc_binding **b_out, struct STRINGBINDING *bd)
+{
+ char *tstr;
+ char *bstr;
+ enum dcerpc_transport_t transport;
+ struct dcerpc_binding *b;
+
+ transport = dcerpc_transport_by_endpoint_protocol(bd->wTowerId);
+ if (transport == NCA_UNKNOWN) {
+ DEBUG(1, ("Can't find transport match endpoint protocol %d\n", bd->wTowerId));
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ tstr = derpc_transport_string_by_transport(transport);
+ bstr = talloc_asprintf(mem_ctx, "%s:%s", tstr, bd->NetworkAddr);
+ if (bstr == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dcerpc_parse_binding(mem_ctx, bstr, &b);
+ TALLOC_FREE(bstr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ *b_out = b;
+ return NT_STATUS_OK;
+}
+
+struct cli_credentials *dcom_get_server_credentials(struct com_context *ctx, const char *server)
+{
+ struct dcom_server_credentials *c;
+ struct cli_credentials *d;
+
+ d = NULL;
+ for (c = ctx->dcom->credentials; c; c = c->next) {
+ if (c->server == NULL) {
+ d = c->credentials;
+ continue;
+ }
+ if (server && !strcmp(c->server, server)) return c->credentials;
+ }
+ return d;
+}
+
+/**
+ * Register credentials for a specific server.
+ *
+ * @param ctx COM context
+ * @param server Name of server, can be NULL
+ * @param credentials Credentials object
+ */
+void dcom_add_server_credentials(struct com_context *ctx, const char *server,
+ struct cli_credentials *credentials)
+{
+ struct dcom_server_credentials *c;
+
+ /* FIXME: Don't use talloc_find_parent_bytype */
+ for (c = ctx->dcom->credentials; c; c = c->next) {
+ if ((server == NULL && c->server == NULL) ||
+ (server != NULL && c->server != NULL &&
+ !strcmp(c->server, server))) {
+ if (c->credentials && c->credentials != credentials) {
+ talloc_unlink(c, c->credentials);
+ c->credentials = credentials;
+ if (talloc_find_parent_bytype(c->credentials, struct dcom_server_credentials))
+ (void)talloc_reference(c, c->credentials);
+ else
+ talloc_steal(c, c->credentials);
+ }
+
+ return;
+ }
+ }
+
+ c = talloc(ctx->event_ctx, struct dcom_server_credentials);
+ c->server = talloc_strdup(c, server);
+ c->credentials = credentials;
+ if (talloc_find_parent_bytype(c->credentials, struct dcom_server_credentials))
+ (void)talloc_reference(c, c->credentials);
+ else
+ talloc_steal(c, c->credentials);
+
+ DLIST_ADD(ctx->dcom->credentials, c);
+}
+
+void dcom_update_credentials_for_aliases(struct com_context *ctx,
+ const char *server,
+ struct DUALSTRINGARRAY *pds)
+{
+ struct cli_credentials *cc;
+ struct dcerpc_binding *b;
+ uint32_t i;
+ NTSTATUS status;
+
+ cc = dcom_get_server_credentials(ctx, server);
+ for (i = 0; pds->stringbindings[i]; ++i) {
+ if (pds->stringbindings[i]->wTowerId != EPM_PROTOCOL_TCP)
+ continue;
+ status = dcerpc_binding_from_STRINGBINDING(ctx, &b, pds->stringbindings[i]);
+ if (!NT_STATUS_IS_OK(status))
+ continue;
+ dcom_add_server_credentials(ctx, b->host, cc);
+ talloc_free(b);
+ }
+}
+
+struct dcom_client_context *dcom_client_init(struct com_context *ctx, struct cli_credentials *credentials)
+{
+ ctx->dcom = talloc_zero(ctx, struct dcom_client_context);
+ if (!credentials) {
+ credentials = cli_credentials_init(ctx);
+ cli_credentials_set_conf(credentials, ctx->lp_ctx);
+ cli_credentials_parse_string(credentials, "%", CRED_SPECIFIED);
+ }
+ dcom_add_server_credentials(ctx, NULL, credentials);
+ return ctx->dcom;
+}
+
+static NTSTATUS dcom_connect_host(struct com_context *ctx,
+ struct dcerpc_pipe **p, const char *server)
+{
+ struct dcerpc_binding *bd;
+ const char * available_transports[] = { "ncacn_ip_tcp", "ncacn_np" };
+ int i;
+ NTSTATUS status;
+ TALLOC_CTX *loc_ctx;
+
+ if (server == NULL) {
+ return dcerpc_pipe_connect(ctx->event_ctx, p, "ncalrpc",
+ &ndr_table_IRemoteActivation,
+ dcom_get_server_credentials(ctx, NULL), ctx->event_ctx, ctx->lp_ctx);
+ }
+ loc_ctx = talloc_new(ctx);
+
+ /* Allow server name to contain a binding string */
+ if (strchr(server, ':') &&
+ NT_STATUS_IS_OK(dcerpc_parse_binding(loc_ctx, server, &bd))) {
+ if (DEBUGLVL(11))
+ bd->flags |= DCERPC_DEBUG_PRINT_BOTH;
+ status = dcerpc_pipe_connect_b(ctx->event_ctx, p, bd,
+ &ndr_table_IRemoteActivation,
+ dcom_get_server_credentials(ctx, bd->host), ctx->event_ctx, ctx->lp_ctx);
+ goto end;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(available_transports); i++)
+ {
+ char *binding = talloc_asprintf(loc_ctx, "%s:%s", available_transports[i], server);
+ if (!binding) {
+ status = NT_STATUS_NO_MEMORY;
+ goto end;
+ }
+ status = dcerpc_pipe_connect(ctx->event_ctx, p, binding,
+ &ndr_table_IRemoteActivation,
+ dcom_get_server_credentials(ctx, server),
+ ctx->event_ctx, ctx->lp_ctx);
+
+ if (NT_STATUS_IS_OK(status)) {
+ if (DEBUGLVL(11))
+ (*p)->conn->flags |= DCERPC_DEBUG_PRINT_BOTH;
+ goto end;
+ } else {
+ DEBUG(1,(__location__": dcom_connect_host : %s\n", get_friendly_nt_error_msg(status)));
+ }
+ }
+
+end:
+ talloc_free(loc_ctx);
+ return status;
+}
+
+struct dcom_object_exporter *object_exporter_by_oxid(struct com_context *ctx,
+ uint64_t oxid)
+{
+ struct dcom_object_exporter *ox;
+ for (ox = ctx->dcom->object_exporters; ox; ox = ox->next) {
+ if (ox->oxid == oxid) {
+ return ox;
+ }
+ }
+
+ return NULL;
+}
+
+struct dcom_object_exporter *object_exporter_update_oxid(struct com_context *ctx, uint64_t oxid, struct DUALSTRINGARRAY *bindings)
+{
+ struct dcom_object_exporter *ox;
+ ox = object_exporter_by_oxid(ctx, oxid);
+ if (!ox) {
+ ox = talloc_zero(ctx, struct dcom_object_exporter);
+ DLIST_ADD(ctx->dcom->object_exporters, ox);
+ ox->oxid = oxid;
+ } else {
+ talloc_free(ox->bindings);
+ }
+ ox->bindings = bindings;
+ talloc_steal(ox, bindings);
+ return ox;
+}
+
+struct dcom_object_exporter *object_exporter_by_ip(struct com_context *ctx, struct IUnknown *ip)
+{
+ return object_exporter_by_oxid(ctx, ip->obj.u_objref.u_standard.std.oxid);
+}
+
+WERROR dcom_create_object(struct com_context *ctx, struct GUID *clsid, const char *server, int num_ifaces, struct GUID *iid, struct IUnknown ***ip, HRESULT *results)
+{
+ uint16_t protseq[] = DCOM_NEGOTIATED_PROTOCOLS;
+ struct dcerpc_pipe *p;
+ struct dcom_object_exporter *m;
+ NTSTATUS status;
+ struct RemoteActivation r;
+ struct DUALSTRINGARRAY *pds;
+ int i;
+ HRESULT hr;
+ uint64_t oxid;
+ struct GUID ipidRemUnknown;
+ struct IUnknown *ru_template;
+ struct ORPCTHAT that;
+ uint32_t AuthnHint;
+ struct COMVERSION ServerVersion;
+ struct MInterfacePointer **ifaces;
+ TALLOC_CTX *loc_ctx;
+
+ status = dcom_connect_host(ctx, &p, server);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(1, ("Unable to connect to %s - %s\n", server, get_friendly_nt_error_msg(status)));
+ return ntstatus_to_werror(status);
+ }
+ loc_ctx = talloc_new(ctx);
+
+ ifaces = talloc_array(loc_ctx, struct MInterfacePointer *, num_ifaces);
+
+ ZERO_STRUCT(r.in);
+ r.in.this.version.MajorVersion = COM_MAJOR_VERSION;
+ r.in.this.version.MinorVersion = COM_MINOR_VERSION;
+ r.in.this.cid = GUID_random();
+ r.in.Clsid = *clsid;
+ r.in.ClientImpLevel = RPC_C_IMP_LEVEL_IDENTIFY;
+ r.in.num_protseqs = ARRAY_SIZE(protseq);
+ r.in.protseq = protseq;
+ r.in.Interfaces = num_ifaces;
+ r.in.pIIDs = iid;
+ r.out.that = &that;
+ r.out.pOxid = &oxid;
+ r.out.pdsaOxidBindings = &pds;
+ r.out.ipidRemUnknown = &ipidRemUnknown;
+ r.out.AuthnHint = &AuthnHint;
+ r.out.ServerVersion = &ServerVersion;
+ r.out.hr = &hr;
+ r.out.ifaces = ifaces;
+ r.out.results = results;
+
+ status = dcerpc_RemoteActivation(p, loc_ctx, &r);
+ talloc_free(p);
+
+ if(NT_STATUS_IS_ERR(status)) {
+ DEBUG(1, ("Error while running RemoteActivation %s\n", nt_errstr(status)));
+ hr = ntstatus_to_werror(status);
+ goto end;
+ }
+
+ if(!W_ERROR_IS_OK(r.out.result)) {
+ hr = r.out.result;
+ goto end;
+ }
+
+ if(!HRES_IS_OK(hr)) {
+ goto end;
+ }
+
+ m = object_exporter_update_oxid(ctx, oxid, pds);
+
+ ru_template = NULL;
+ *ip = talloc_array(ctx, struct IUnknown *, num_ifaces);
+ for (i = 0; i < num_ifaces; i++) {
+ (*ip)[i] = NULL;
+ if (W_ERROR_IS_OK(results[i])) {
+ status = dcom_IUnknown_from_OBJREF(ctx, &(*ip)[i], &r.out.ifaces[i]->obj);
+ if (!NT_STATUS_IS_OK(status)) {
+ results[i] = ntstatus_to_werror(status);
+ } else if (!ru_template)
+ ru_template = (*ip)[i];
+ }
+ }
+
+ /* TODO:avg check when exactly oxid should be updated,its lifetime etc */
+ if (m->rem_unknown && memcmp(&m->rem_unknown->obj.u_objref.u_standard.std.ipid, &ipidRemUnknown, sizeof(ipidRemUnknown))) {
+ talloc_free(m->rem_unknown);
+ m->rem_unknown = NULL;
+ }
+ if (!m->rem_unknown) {
+ if (!ru_template) {
+ DEBUG(1,("dcom_create_object: Cannot Create IRemUnknown - template interface not available\n"));
+ hr = WERR_GEN_FAILURE;
+ }
+ m->rem_unknown = talloc_zero(m, struct IRemUnknown);
+ memcpy(m->rem_unknown, ru_template, sizeof(struct IUnknown));
+ GUID_from_string(COM_IREMUNKNOWN_UUID, &m->rem_unknown->obj.iid);
+ m->rem_unknown->obj.u_objref.u_standard.std.ipid = ipidRemUnknown;
+ m->rem_unknown->vtable = (struct IRemUnknown_vtable *)dcom_proxy_vtable_by_iid(&m->rem_unknown->obj.iid);
+ /* TODO:avg copy stringbindigs?? */
+ }
+
+ dcom_update_credentials_for_aliases(ctx, server, pds);
+ {
+ char *c;
+ c = strchr(server, '[');
+ if (m->host) talloc_free(m->host);
+ m->host = c ? talloc_strndup(m, server, c - server) : talloc_strdup(m, server);
+ }
+ hr = WERR_OK;
+end:
+ talloc_free(loc_ctx);
+ return hr;
+}
+
+int find_similar_binding(struct STRINGBINDING **sb, const char *host)
+{
+ int i, l;
+ l = strlen(host);
+ for (i = 0; sb[i]; ++i) {
+ if ((sb[i]->wTowerId == EPM_PROTOCOL_TCP) && !strncasecmp(host, sb[i]->NetworkAddr, l) && (sb[i]->NetworkAddr[l] == '['))
+ break;
+ }
+ return i;
+}
+
+WERROR dcom_query_interface(struct IUnknown *d, uint32_t cRefs, uint16_t cIids, struct GUID *iids, struct IUnknown **ip, WERROR *results)
+{
+ struct dcom_object_exporter *ox;
+ struct REMQIRESULT *rqir;
+ WERROR result;
+ NTSTATUS status;
+ int i;
+ TALLOC_CTX *loc_ctx;
+ struct IUnknown ru;
+
+ loc_ctx = talloc_new(d);
+ ox = object_exporter_by_ip(d->ctx, d);
+
+ result = IRemUnknown_RemQueryInterface(ox->rem_unknown, loc_ctx, &IUnknown_ipid(d), cRefs, cIids, iids, &rqir);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(1, ("dcom_query_interface failed: %08X\n", W_ERROR_V(result)));
+ talloc_free(loc_ctx);
+ return result;
+ }
+ ru = *(struct IUnknown *)ox->rem_unknown;
+ for (i = 0; i < cIids; ++i) {
+ ip[i] = NULL;
+ results[i] = rqir[i].hResult;
+ if (W_ERROR_IS_OK(results[i])) {
+ ru.obj.iid = iids[i];
+ ru.obj.u_objref.u_standard.std = rqir[i].std;
+ status = dcom_IUnknown_from_OBJREF(d->ctx, &ip[i], &ru.obj);
+ if (!NT_STATUS_IS_OK(status)) {
+ results[i] = ntstatus_to_werror(status);
+ }
+ }
+ }
+
+ talloc_free(loc_ctx);
+ return WERR_OK;
+}
+
+int is_ip_binding(const char* s)
+{
+ while (*s && (*s != '[')) {
+ if (((*s >= '0') && (*s <= '9')) || *s == '.')
+ ++s;
+ else
+ return 0;
+ }
+ return 1;
+}
+
+NTSTATUS dcom_get_pipe(struct IUnknown *iface, struct dcerpc_pipe **pp)
+{
+ struct dcerpc_binding *binding;
+ struct GUID iid;
+ uint64_t oxid;
+ NTSTATUS status;
+ int i, j, isimilar;
+ struct dcerpc_pipe *p;
+ struct dcom_object_exporter *ox;
+ const struct ndr_interface_table *table;
+
+ ox = object_exporter_by_oxid(iface->ctx, iface->obj.u_objref.u_standard.std.oxid);
+ if (!ox) {
+ DEBUG(0, ("dcom_get_pipe: OXID not found\n"));
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ p = ox->pipe;
+
+ iid = iface->vtable->iid;
+ table = ndr_table_by_uuid(&iid);
+ if (table == NULL) {
+ char *guid_str;
+ guid_str = GUID_string(NULL, &iid);
+ DEBUG(0,(__location__": dcom_get_pipe - unrecognized interface{%s}\n", guid_str));
+ talloc_free(guid_str);
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ if (p && p->last_fault_code) {
+ talloc_free(p);
+ ox->pipe = p = NULL;
+ }
+
+ if (p) {
+ if (!GUID_equal(&p->syntax.uuid, &iid)) {
+ ox->pipe->syntax.uuid = iid;
+
+ /* interface will always be present, so
+ * idl_iface_by_uuid can't return NULL */
+ /* status = dcerpc_secondary_context(p, &p2, idl_iface_by_uuid(&iid)); */
+ status = dcerpc_alter_context(p, p, &ndr_table_by_uuid(&iid)->syntax_id, &p->transfer_syntax);
+ } else
+ status = NT_STATUS_OK;
+ *pp = p;
+ return status;
+ }
+
+ status = NT_STATUS_NO_MORE_ENTRIES;
+
+ /* To avoid delays whe connecting nonroutable bindings we 1st check binding starting with hostname */
+ /* FIX:low create concurrent connections to all bindings, fastest wins - Win2k and newer does this way???? */
+ isimilar = find_similar_binding(ox->bindings->stringbindings, ox->host);
+ DEBUG(1, (__location__": dcom_get_pipe: host=%s, similar=%s\n", ox->host, ox->bindings->stringbindings[isimilar] ? ox->bindings->stringbindings[isimilar]->NetworkAddr : "None"));
+ j = isimilar - 1;
+ for (i = 0; ox->bindings->stringbindings[i]; ++i) {
+ if (!ox->bindings->stringbindings[++j]) j = 0;
+ /* FIXME:LOW Use also other transports if possible */
+ if ((j != isimilar) && (ox->bindings->stringbindings[j]->wTowerId != EPM_PROTOCOL_TCP || !is_ip_binding(ox->bindings->stringbindings[j]->NetworkAddr))) {
+ DEBUG(9, ("dcom_get_pipe: Skipping stringbinding %24.24s\n", ox->bindings->stringbindings[j]->NetworkAddr));
+ continue;
+ }
+ DEBUG(9, ("dcom_get_pipe: Trying stringbinding %s\n", ox->bindings->stringbindings[j]->NetworkAddr));
+ status = dcerpc_binding_from_STRINGBINDING(iface->ctx, &binding,
+ ox->bindings->stringbindings[j]);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Error parsing string binding"));
+ } else {
+ /* FIXME:LOW Make flags more flexible */
+ binding->flags |= DCERPC_AUTH_NTLM | DCERPC_SIGN;
+ if (DEBUGLVL(11))
+ binding->flags |= DCERPC_DEBUG_PRINT_BOTH;
+ status = dcerpc_pipe_connect_b(iface->ctx->event_ctx, &p, binding,
+ ndr_table_by_uuid(&iid),
+ dcom_get_server_credentials(iface->ctx, binding->host),
+ iface->ctx->event_ctx, iface->ctx->lp_ctx);
+ talloc_unlink(iface->ctx, binding);
+ }
+ if (NT_STATUS_IS_OK(status)) break;
+ }
+
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(0, ("Unable to connect to remote host - %s\n", nt_errstr(status)));
+ return status;
+ }
+
+ DEBUG(2, ("Successfully connected to OXID %llx\n", (long long)oxid));
+
+ ox->pipe = *pp = p;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS dcom_OBJREF_from_IUnknown(TALLLOC_CTX *mem_ctx, struct OBJREF *o, struct IUnknown *p)
+{
+ /* FIXME: Cache generated objref objects? */
+ ZERO_STRUCTP(o);
+
+ if (!p) {
+ o->signature = OBJREF_SIGNATURE;
+ o->flags = OBJREF_NULL;
+ } else {
+ *o = p->obj;
+ switch(o->flags) {
+ case OBJREF_CUSTOM: {
+ marshal_fn marshal;
+
+ marshal = dcom_marshal_by_clsid(&o->u_objref.u_custom.clsid);
+ if (marshal) {
+ return marshal(mem_ctx, p, o);
+ } else {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+ }
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+enum ndr_err_code dcom_IUnknown_from_OBJREF(struct com_context *ctx, struct IUnknown **_p, struct OBJREF *o)
+{
+ struct IUnknown *p;
+ struct dcom_object_exporter *ox;
+ unmarshal_fn unmarshal;
+
+ switch(o->flags) {
+ case OBJREF_NULL:
+ *_p = NULL;
+ return NDR_ERR_SUCCESS;
+
+ case OBJREF_STANDARD:
+ p = talloc_zero(ctx, struct IUnknown);
+ p->ctx = ctx;
+ p->obj = *o;
+ p->vtable = dcom_proxy_vtable_by_iid(&o->iid);
+
+ if (!p->vtable) {
+ DEBUG(0, ("Unable to find proxy class for interface with IID %s\n", GUID_string(ctx, &o->iid)));
+ return NDR_ERR_INVALID_POINTER;
+ }
+
+ p->vtable->Release_send = dcom_release_send;
+
+ ox = object_exporter_by_oxid(ctx, o->u_objref.u_standard.std.oxid);
+ /* FIXME: Add object to list of objects to ping */
+ *_p = p;
+ return NDR_ERR_SUCCESS;
+
+ case OBJREF_HANDLER:
+ p = talloc_zero(ctx, struct IUnknown);
+ p->ctx = ctx;
+ p->obj = *o;
+ ox = object_exporter_by_oxid(ctx, o->u_objref.u_handler.std.oxid );
+ /* FIXME: Add object to list of objects to ping */
+/*FIXME p->vtable = dcom_vtable_by_clsid(&o->u_objref.u_handler.clsid);*/
+ /* FIXME: Do the custom unmarshaling call */
+
+ *_p = p;
+ return NDR_ERR_BAD_SWITCH;
+
+ case OBJREF_CUSTOM:
+ p = talloc_zero(ctx, struct IUnknown);
+ p->ctx = ctx;
+ p->vtable = NULL;
+ p->obj = *o;
+ unmarshal = dcom_unmarshal_by_clsid(&o->u_objref.u_custom.clsid);
+ *_p = p;
+ if (unmarshal) {
+ return unmarshal(ctx, o, _p);
+ } else {
+ return NDR_ERR_BAD_SWITCH;
+ }
+ }
+
+ return NDR_ERR_BAD_SWITCH;
+}
+
+uint64_t dcom_get_current_oxid(void)
+{
+ return getpid();
+}
+
+/* FIXME:Fake async dcom_get_pipe_* */
+struct composite_context *dcom_get_pipe_send(struct IUnknown *d, TALLOC_CTX *mem_ctx)
+{
+ struct composite_context *c;
+
+ c = composite_create(0, d->ctx->event_ctx);
+ if (c == NULL) return NULL;
+ c->private_data = d;
+ /* composite_done(c); bugged - callback is triggered twice by composite_continue and composite_done */
+ c->state = COMPOSITE_STATE_DONE; /* this is workaround */
+
+ return c;
+}
+
+NTSTATUS dcom_get_pipe_recv(struct composite_context *c, struct dcerpc_pipe **pp)
+{
+ NTSTATUS status;
+
+ status = dcom_get_pipe((struct IUnknown *)c->private_data, pp);
+ talloc_free(c);
+
+ return status;
+}
+
+/* FIXME:avg put IUnknown_Release_out into header */
+struct IUnknown_Release_out {
+ uint32_t result;
+};
+
+void dcom_release_continue(struct composite_context *cr)
+{
+ struct composite_context *c;
+ struct IUnknown *d;
+ struct IUnknown_Release_out *out;
+ WERROR r;
+
+ c = talloc_get_type(cr->async.private_data, struct composite_context);
+ d = c->private_data;
+ r = IRemUnknown_RemRelease_recv(cr);
+ talloc_free(d);
+ out = talloc_zero(c, struct IUnknown_Release_out);
+ out->result = W_ERROR_V(r);
+ c->private_data = out;
+ composite_done(c);
+}
+
+struct composite_context *dcom_release_send(struct IUnknown *d, TALLOC_CTX *mem_ctx)
+{
+ struct composite_context *c, *cr;
+ struct REMINTERFACEREF iref;
+ struct dcom_object_exporter *ox;
+
+ c = composite_create(d->ctx, d->ctx->event_ctx);
+ if (c == NULL) return NULL;
+ c->private_data = d;
+
+ ox = object_exporter_by_ip(d->ctx, d);
+ iref.ipid = IUnknown_ipid(d);
+ iref.cPublicRefs = 5;
+ iref.cPrivateRefs = 0;
+ cr = IRemUnknown_RemRelease_send(ox->rem_unknown, mem_ctx, 1, &iref);
+
+ composite_continue(c, cr, dcom_release_continue, c);
+ return c;
+}
+
+uint32_t dcom_release_recv(struct composite_context *c)
+{
+ NTSTATUS status;
+ WERROR r;
+
+ status = composite_wait(c);
+ if (!NT_STATUS_IS_OK(status))
+ r = ntstatus_to_werror(status);
+ else
+ W_ERROR_V(r) = ((struct IUnknown_Release_out *)c->private_data)->result;
+ talloc_free(c);
+ return W_ERROR_IS_OK(r) ? 0 : W_ERROR_V(r);
+}
+
+uint32_t dcom_release(void *interface, TALLOC_CTX *mem_ctx)
+{
+ struct composite_context *c;
+
+ c = dcom_release_send(interface, mem_ctx);
+ return dcom_release_recv(c);
+}
+
+void dcom_proxy_async_call_recv_pipe_send_rpc(struct composite_context *c_pipe)
+{
+ struct composite_context *c;
+ struct dcom_proxy_async_call_state *s;
+ struct dcerpc_pipe *p;
+ struct rpc_request *req;
+ NTSTATUS status;
+
+ c = c_pipe->async.private_data;
+ s = talloc_get_type(c->private_data, struct dcom_proxy_async_call_state);
+
+ status = dcom_get_pipe_recv(c_pipe, &p);
+ if (!NT_STATUS_IS_OK(status)) {
+ composite_error(c, NT_STATUS_RPC_NT_CALL_FAILED);
+ return;
+ }
+/*TODO: FIXME - for now this unused anyway */
+ req = dcerpc_ndr_request_send(p, &s->d->obj.u_objref.u_standard.std.ipid, s->table, s->opnum, s, s->r);
+ composite_continue_rpc(c, req, s->continuation, c);
+}
diff --git a/source4/lib/com/dcom/tables.c b/source4/lib/com/dcom/tables.c
new file mode 100644
index 0000000..7f745c1
--- /dev/null
+++ b/source4/lib/com/dcom/tables.c
@@ -0,0 +1,94 @@
+/*
+ Unix SMB/CIFS implementation.
+ DCOM proxy tables functionality
+ Copyright (C) 2005 Jelmer Vernooij <jelmer@samba.org>
+ Copyright (C) 2006 Andrzej Hajda <andrzej.hajda@wp.pl>
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "librpc/gen_ndr/com_dcom.h"
+#include "lib/com/dcom/dcom.h"
+
+static struct dcom_proxy {
+ struct IUnknown_vtable *vtable;
+ struct dcom_proxy *prev, *next;
+} *proxies = NULL;
+
+NTSTATUS dcom_register_proxy(TALLOC_CTX *ctx,
+ struct IUnknown_vtable *proxy_vtable)
+{
+ struct dcom_proxy *proxy = talloc(ctx, struct dcom_proxy);
+
+ proxy->vtable = proxy_vtable;
+ DLIST_ADD(proxies, proxy);
+
+ return NT_STATUS_OK;
+}
+
+struct IUnknown_vtable *dcom_proxy_vtable_by_iid(struct GUID *iid)
+{
+ struct dcom_proxy *p;
+ for (p = proxies; p; p = p->next) {
+ if (GUID_equal(&p->vtable->iid, iid)) {
+ return p->vtable;
+ }
+ }
+ return NULL;
+}
+
+static struct dcom_marshal {
+ struct GUID clsid;
+ marshal_fn marshal;
+ unmarshal_fn unmarshal;
+ struct dcom_marshal *prev, *next;
+} *marshals = NULL;
+
+NTSTATUS dcom_register_marshal(TALLOC_CTX *ctx,
+ struct GUID *clsid, marshal_fn marshal, unmarshal_fn unmarshal)
+{
+ struct dcom_marshal *p = talloc(ctx, struct dcom_marshal);
+
+ p->clsid = *clsid;
+ p->marshal = marshal;
+ p->unmarshal = unmarshal;
+ DLIST_ADD(marshals, p);
+ return NT_STATUS_OK;
+}
+
+_PUBLIC_ marshal_fn dcom_marshal_by_clsid(struct GUID *clsid)
+{
+ struct dcom_marshal *p;
+ for (p = marshals; p; p = p->next) {
+ if (GUID_equal(&p->clsid, clsid)) {
+ return p->marshal;
+ }
+ }
+ return NULL;
+}
+
+_PUBLIC_ unmarshal_fn dcom_unmarshal_by_clsid(struct GUID *clsid)
+{
+ struct dcom_marshal *p;
+ for (p = marshals; p; p = p->next) {
+ if (GUID_equal(&p->clsid, clsid)) {
+ return p->unmarshal;
+ }
+ }
+ return NULL;
+}
+
diff --git a/source4/lib/com/main.c b/source4/lib/com/main.c
new file mode 100644
index 0000000..647f323
--- /dev/null
+++ b/source4/lib/com/main.c
@@ -0,0 +1,90 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main COM functionality
+ Copyright (C) 2004 Jelmer Vernooij <jelmer@samba.org>
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/com/com.h"
+#include "lib/events/events.h"
+#include "librpc/gen_ndr/com_dcom.h"
+
+WERROR com_init_ctx(struct com_context **ctx, struct tevent_context *event_ctx)
+{
+ *ctx = talloc(NULL, struct com_context);
+ if (event_ctx == NULL) {
+ event_ctx = samba_tevent_context_init(*ctx);
+ }
+ (*ctx)->event_ctx = event_ctx;
+ return WERR_OK;
+}
+
+WERROR com_create_object(struct com_context *ctx, struct GUID *clsid, int num_ifaces, struct GUID *iid, struct IUnknown **ip, WERROR *results)
+{
+ struct IUnknown *iunk = NULL;
+ struct IClassFactory *factory;
+ WERROR error;
+ int i;
+ struct GUID classfact_iid;
+
+ GUID_from_string(NDR_ICLASSFACTORY_UUID, &classfact_iid);
+
+ /* Obtain class object */
+ error = com_get_class_object(ctx, clsid, &classfact_iid, (struct IUnknown **)&factory);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(3, ("Unable to obtain class object for %s\n", GUID_string(NULL, clsid)));
+ return error;
+ }
+
+ /* Run IClassFactory::CreateInstance() */
+ error = IClassFactory_CreateInstance(factory, ctx, NULL, &classfact_iid, (struct MInterfacePointer **)&iunk);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(3, ("Error while calling IClassFactory::CreateInstance : %s\n", win_errstr(error)));
+ return error;
+ }
+
+ if (!iunk) {
+ DEBUG(0, ("IClassFactory_CreateInstance returned success but result pointer is still NULL!\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ /* Release class object */
+ IUnknown_Release((struct IUnknown *)factory, ctx);
+
+ error = WERR_OK;
+
+ /* Do one or more QueryInterface calls */
+ for (i = 0; i < num_ifaces; i++) {
+ results[i] = IUnknown_QueryInterface(iunk, ctx, &iid[i], &ip[i]);
+ if (!W_ERROR_IS_OK(results[i])) error = results[i];
+ }
+
+ return error;
+}
+
+WERROR com_get_class_object(struct com_context *ctx, struct GUID *clsid, struct GUID *iid, struct IUnknown **ip)
+{
+ struct IUnknown *iu;
+
+ iu = com_class_by_clsid(ctx, clsid);
+ if (!iu) {
+ return W_ERROR(HRES_ERROR_V(HRES_REGDB_E_CLASSNOTREG));
+ }
+
+ return IUnknown_QueryInterface(iu, ctx, iid, ip);
+}
diff --git a/source4/lib/com/rot.c b/source4/lib/com/rot.c
new file mode 100644
index 0000000..0180a92
--- /dev/null
+++ b/source4/lib/com/rot.c
@@ -0,0 +1,35 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Running object table functions
+
+ Copyright (C) Jelmer Vernooij 2004-2005
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "lib/com/com.h"
+
+struct dcom_interface_p *dcom_get_local_iface_p(struct GUID *ipid)
+{
+ /* FIXME: Call the local ROT and do a
+ * rot_get_interface_pointer call */
+
+ /* FIXME: Perhaps have a local (thread-local) table with
+ * local DCOM objects so that not every DCOM call requires a lookup
+ * to the ROT? */
+ return NULL;
+}
diff --git a/source4/lib/com/tables.c b/source4/lib/com/tables.c
new file mode 100644
index 0000000..e1f93bc
--- /dev/null
+++ b/source4/lib/com/tables.c
@@ -0,0 +1,112 @@
+/*
+ Unix SMB/CIFS implementation.
+ COM class tables
+ Copyright (C) 2004 Jelmer Vernooij <jelmer@samba.org>
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/com/com.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+
+/* Specific implementation of one or more interfaces */
+struct com_class
+{
+ const char *progid;
+ struct GUID clsid;
+
+ struct IUnknown *class_object;
+ struct com_class *prev, *next;
+} * running_classes = NULL;
+
+static struct IUnknown *get_com_class_running(const struct GUID *clsid)
+{
+ struct com_class *c = running_classes;
+
+ while(c) {
+
+ if (GUID_equal(clsid, &c->clsid)) {
+ return c->class_object;
+ }
+
+ c = c->next;
+ }
+
+ return NULL;
+}
+
+static struct IUnknown *get_com_class_so(TALLOC_CTX *mem_ctx, const struct GUID *clsid)
+{
+ char *module_name;
+ char *clsid_str;
+ void *mod;
+ get_class_object_function f;
+
+ clsid_str = GUID_string(mem_ctx, clsid);
+ module_name = talloc_asprintf(mem_ctx, "%s.so", clsid_str);
+ talloc_free(clsid_str);
+
+ mod = dlopen(module_name, 0);
+
+ if (!mod) {
+ return NULL;
+ }
+
+ f = dlsym(mod, "get_class_object");
+
+ if (!f) {
+ dlclose(mod);
+ return NULL;
+ }
+
+ return f(clsid);
+}
+
+struct IUnknown *com_class_by_clsid(struct com_context *ctx, const struct GUID *clsid)
+{
+ struct IUnknown *c;
+
+ /* Check list of running COM classes first */
+ c = get_com_class_running(clsid);
+
+ if (c != NULL) {
+ return c;
+ }
+
+ c = get_com_class_so(ctx, clsid);
+
+ if (c != NULL) {
+ return c;
+ }
+
+ return NULL;
+}
+
+NTSTATUS com_register_running_class(TALLOC_CTX *ctx,
+ struct GUID *clsid, const char *progid, struct IUnknown *p)
+{
+ struct com_class *l = talloc_zero(running_classes?
+ running_classes : ctx, struct com_class);
+
+ l->clsid = *clsid;
+ l->progid = talloc_strdup(l, progid);
+ l->class_object = p;
+
+ DLIST_ADD(running_classes, l);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/lib/com/wscript_build b/source4/lib/com/wscript_build
new file mode 100644
index 0000000..b96f39f
--- /dev/null
+++ b/source4/lib/com/wscript_build
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+
+bld.SAMBA_SUBSYSTEM('COM',
+ source='tables.c rot.c main.c',
+ allow_warnings=True,
+ deps='samba-util samba-hostconfig events ndr NDR_DCOM'
+ )
+
+
+bld.SAMBA_SUBSYSTEM('DCOM',
+ source='dcom/main.c dcom/tables.c',
+ public_deps='COM DCOM_PROXY_DCOM RPC_NDR_REMACT RPC_NDR_OXIDRESOLVER',
+ # ORPCTHIS maps to this.*, which hits the "No C++ keywords" define
+ # unless we force off developer mode
+ cflags_end = '-UDEVELOPER',
+ # even with the above hack this still doesn't compile - disable it for now
+ enabled=False
+ )
+
+
+bld.SAMBA_MODULE('com_simple',
+ source='classes/simple.c',
+ allow_warnings=True,
+ deps='talloc',
+ subsystem='COM',
+ init_function='com_simple_init'
+ )
+
diff --git a/source4/lib/events/events.h b/source4/lib/events/events.h
new file mode 100644
index 0000000..e9f5f4c
--- /dev/null
+++ b/source4/lib/events/events.h
@@ -0,0 +1,6 @@
+#ifndef __LIB_EVENTS_H__
+#define __LIB_EVENTS_H__
+#include <tevent.h>
+struct tevent_context *s4_event_context_init(TALLOC_CTX *mem_ctx);
+void s4_event_context_set_default(struct tevent_context *ev);
+#endif /* __LIB_EVENTS_H__ */
diff --git a/source4/lib/events/tevent_s4.c b/source4/lib/events/tevent_s4.c
new file mode 100644
index 0000000..f80424f
--- /dev/null
+++ b/source4/lib/events/tevent_s4.c
@@ -0,0 +1,41 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) Andrew Tridgell 2003
+
+ 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"
+#define TEVENT_DEPRECATED 1
+#include "lib/events/events.h"
+
+/*
+ create a event_context structure. This must be the first events
+ call, and all subsequent calls pass this event_context as the first
+ element. Event handlers also receive this as their first argument.
+
+ This samba4 specific call sets the samba4 debug handler.
+*/
+struct tevent_context *s4_event_context_init(TALLOC_CTX *mem_ctx)
+{
+ struct tevent_context *ev;
+
+ ev = tevent_context_init_byname(mem_ctx, NULL);
+ if (ev) {
+ samba_tevent_set_debug(ev, "s4_tevent");
+ tevent_loop_allow_nesting(ev);
+ }
+ return ev;
+}
+
diff --git a/source4/lib/events/wscript_build b/source4/lib/events/wscript_build
new file mode 100644
index 0000000..d08d5dd
--- /dev/null
+++ b/source4/lib/events/wscript_build
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+
+bld.SAMBA_LIBRARY('events',
+ source='tevent_s4.c',
+ deps='samba-util',
+ public_deps='tevent',
+ private_library=True
+ )
diff --git a/source4/lib/messaging/irpc.h b/source4/lib/messaging/irpc.h
new file mode 100644
index 0000000..ebf30af
--- /dev/null
+++ b/source4/lib/messaging/irpc.h
@@ -0,0 +1,86 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Samba internal rpc code - header
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 IRPC_H
+#define IRPC_H
+
+#include "lib/messaging/messaging.h"
+#include "librpc/gen_ndr/irpc.h"
+
+/*
+ an incoming irpc message
+*/
+struct irpc_message {
+ struct server_id from;
+ void *private_data;
+ struct irpc_header header;
+ struct ndr_pull *ndr;
+ bool defer_reply;
+ bool no_reply;
+ struct imessaging_context *msg_ctx;
+ struct irpc_list *irpc;
+ void *data;
+};
+
+/* don't allow calls to take too long */
+#define IRPC_CALL_TIMEOUT 10
+/* wait for the calls as long as it takes */
+#define IRPC_CALL_TIMEOUT_INF 0
+
+
+/* the server function type */
+typedef NTSTATUS (*irpc_function_t)(struct irpc_message *, void *r);
+
+/* register a server function with the irpc messaging system */
+#define IRPC_REGISTER(msg_ctx, pipename, funcname, function, private_data) \
+ irpc_register(msg_ctx, &ndr_table_ ## pipename, \
+ NDR_ ## funcname, \
+ (irpc_function_t)function, private_data)
+
+struct ndr_interface_table;
+
+NTSTATUS irpc_register(struct imessaging_context *msg_ctx,
+ const struct ndr_interface_table *table,
+ int call, irpc_function_t fn, void *private_data);
+
+struct dcerpc_binding_handle *irpc_binding_handle(TALLOC_CTX *mem_ctx,
+ struct imessaging_context *msg_ctx,
+ struct server_id server_id,
+ const struct ndr_interface_table *table);
+struct dcerpc_binding_handle *irpc_binding_handle_by_name(TALLOC_CTX *mem_ctx,
+ struct imessaging_context *msg_ctx,
+ const char *dest_task,
+ const struct ndr_interface_table *table);
+void irpc_binding_handle_add_security_token(struct dcerpc_binding_handle *h,
+ struct security_token *token);
+
+NTSTATUS irpc_add_name(struct imessaging_context *msg_ctx, const char *name);
+NTSTATUS irpc_servers_byname(struct imessaging_context *msg_ctx,
+ TALLOC_CTX *mem_ctx, const char *name,
+ unsigned *num_servers,
+ struct server_id **servers);
+struct irpc_name_records *irpc_all_servers(struct imessaging_context *msg_ctx,
+ TALLOC_CTX *mem_ctx);
+void irpc_remove_name(struct imessaging_context *msg_ctx, const char *name);
+NTSTATUS irpc_send_reply(struct irpc_message *m, NTSTATUS status);
+
+#endif
+
diff --git a/source4/lib/messaging/messaging.c b/source4/lib/messaging/messaging.c
new file mode 100644
index 0000000..67b0366
--- /dev/null
+++ b/source4/lib/messaging/messaging.c
@@ -0,0 +1,1485 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Samba internal messaging functions
+
+ Copyright (C) Andrew Tridgell 2004
+
+ 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 "lib/events/events.h"
+#include "lib/util/server_id.h"
+#include "system/filesys.h"
+#include "messaging/messaging.h"
+#include "messaging/messaging_internal.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/socket/socket.h"
+#include "librpc/gen_ndr/ndr_irpc.h"
+#include "lib/messaging/irpc.h"
+#include "../lib/util/unix_privs.h"
+#include "librpc/rpc/dcerpc.h"
+#include "cluster/cluster.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "lib/param/param.h"
+#include "lib/util/server_id_db.h"
+#include "lib/util/talloc_report_printf.h"
+#include "lib/messaging/messages_dgm.h"
+#include "lib/messaging/messages_dgm_ref.h"
+#include "../source3/lib/messages_util.h"
+#include <tdb.h>
+
+/* change the message version with any incompatible changes in the protocol */
+#define IMESSAGING_VERSION 1
+
+/*
+ a pending irpc call
+*/
+struct irpc_request {
+ struct irpc_request *prev, *next;
+ struct imessaging_context *msg_ctx;
+ int callid;
+ struct {
+ void (*handler)(struct irpc_request *irpc, struct irpc_message *m);
+ void *private_data;
+ } incoming;
+};
+
+/* we have a linked list of dispatch handlers for each msg_type that
+ this messaging server can deal with */
+struct dispatch_fn {
+ struct dispatch_fn *next, *prev;
+ uint32_t msg_type;
+ void *private_data;
+ msg_callback_t fn;
+};
+
+/* an individual message */
+
+static void irpc_handler(struct imessaging_context *,
+ void *,
+ uint32_t,
+ struct server_id,
+ size_t,
+ int *,
+ DATA_BLOB *);
+
+
+/*
+ A useful function for testing the message system.
+*/
+static void ping_message(struct imessaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id src,
+ size_t num_fds,
+ int *fds,
+ DATA_BLOB *data)
+{
+ struct server_id_buf idbuf;
+
+ if (num_fds != 0) {
+ DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
+ return;
+ }
+
+ DEBUG(1,("INFO: Received PING message from server %s [%.*s]\n",
+ server_id_str_buf(src, &idbuf), (int)data->length,
+ data->data?(const char *)data->data:""));
+ imessaging_send(msg, src, MSG_PONG, data);
+}
+
+static void pool_message(struct imessaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id src,
+ size_t num_fds,
+ int *fds,
+ DATA_BLOB *data)
+{
+ FILE *f = NULL;
+
+ if (num_fds != 1) {
+ DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
+ return;
+ }
+
+ f = fdopen(fds[0], "w");
+ if (f == NULL) {
+ DBG_DEBUG("fopen failed: %s\n", strerror(errno));
+ return;
+ }
+
+ talloc_full_report_printf(NULL, f);
+ fclose(f);
+}
+
+static void ringbuf_log_msg(struct imessaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id src,
+ size_t num_fds,
+ int *fds,
+ DATA_BLOB *data)
+{
+ char *log = debug_get_ringbuf();
+ size_t logsize = debug_get_ringbuf_size();
+ DATA_BLOB blob;
+
+ if (num_fds != 0) {
+ DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
+ return;
+ }
+
+ if (log == NULL) {
+ log = discard_const_p(char, "*disabled*\n");
+ logsize = strlen(log) + 1;
+ }
+
+ blob.data = (uint8_t *)log;
+ blob.length = logsize;
+
+ imessaging_send(msg, src, MSG_RINGBUF_LOG, &blob);
+}
+
+/****************************************************************************
+ Receive a "set debug level" message.
+****************************************************************************/
+
+static void debug_imessage(struct imessaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id src,
+ size_t num_fds,
+ int *fds,
+ DATA_BLOB *data)
+{
+ const char *params_str = (const char *)data->data;
+ struct server_id_buf src_buf;
+ struct server_id dst = imessaging_get_server_id(msg_ctx);
+ struct server_id_buf dst_buf;
+
+ if (num_fds != 0) {
+ DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
+ return;
+ }
+
+ /* Check, it's a proper string! */
+ if (params_str[(data->length)-1] != '\0') {
+ DBG_ERR("Invalid debug message from pid %s to pid %s\n",
+ server_id_str_buf(src, &src_buf),
+ server_id_str_buf(dst, &dst_buf));
+ return;
+ }
+
+ DBG_ERR("INFO: Remote set of debug to `%s' (pid %s from pid %s)\n",
+ params_str,
+ server_id_str_buf(dst, &dst_buf),
+ server_id_str_buf(src, &src_buf));
+
+ debug_parse_levels(params_str);
+}
+
+/****************************************************************************
+ Return current debug level.
+****************************************************************************/
+
+static void debuglevel_imessage(struct imessaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id src,
+ size_t num_fds,
+ int *fds,
+ DATA_BLOB *data)
+{
+ char *message = debug_list_class_names_and_levels();
+ DATA_BLOB blob = data_blob_null;
+ struct server_id_buf src_buf;
+ struct server_id dst = imessaging_get_server_id(msg_ctx);
+ struct server_id_buf dst_buf;
+
+ if (num_fds != 0) {
+ DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
+ return;
+ }
+
+ DBG_DEBUG("Received REQ_DEBUGLEVEL message (pid %s from pid %s)\n",
+ server_id_str_buf(dst, &dst_buf),
+ server_id_str_buf(src, &src_buf));
+
+ if (message == NULL) {
+ DBG_ERR("debug_list_class_names_and_levels returned NULL\n");
+ return;
+ }
+
+ blob = data_blob_string_const_null(message);
+ imessaging_send(msg_ctx, src, MSG_DEBUGLEVEL, &blob);
+
+ TALLOC_FREE(message);
+}
+
+/*
+ return uptime of messaging server via irpc
+*/
+static NTSTATUS irpc_uptime(struct irpc_message *msg,
+ struct irpc_uptime *r)
+{
+ struct imessaging_context *ctx = talloc_get_type(msg->private_data, struct imessaging_context);
+ *r->out.start_time = timeval_to_nttime(&ctx->start_time);
+ return NT_STATUS_OK;
+}
+
+static struct dispatch_fn *imessaging_find_dispatch(
+ struct imessaging_context *msg, uint32_t msg_type)
+{
+ /* temporary IDs use an idtree, the rest use a array of pointers */
+ if (msg_type >= MSG_TMP_BASE) {
+ return (struct dispatch_fn *)idr_find(msg->dispatch_tree,
+ msg_type);
+ }
+ if (msg_type < msg->num_types) {
+ return msg->dispatch[msg_type];
+ }
+ return NULL;
+}
+
+/*
+ Register a dispatch function for a particular message type.
+*/
+NTSTATUS imessaging_register(struct imessaging_context *msg, void *private_data,
+ uint32_t msg_type, msg_callback_t fn)
+{
+ struct dispatch_fn *d;
+
+ /* possibly expand dispatch array */
+ if (msg_type >= msg->num_types) {
+ struct dispatch_fn **dp;
+ uint32_t i;
+ dp = talloc_realloc(msg, msg->dispatch, struct dispatch_fn *, msg_type+1);
+ NT_STATUS_HAVE_NO_MEMORY(dp);
+ msg->dispatch = dp;
+ for (i=msg->num_types;i<=msg_type;i++) {
+ msg->dispatch[i] = NULL;
+ }
+ msg->num_types = msg_type+1;
+ }
+
+ d = talloc_zero(msg->dispatch, struct dispatch_fn);
+ NT_STATUS_HAVE_NO_MEMORY(d);
+ d->msg_type = msg_type;
+ d->private_data = private_data;
+ d->fn = fn;
+
+ DLIST_ADD(msg->dispatch[msg_type], d);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ register a temporary message handler. The msg_type is allocated
+ above MSG_TMP_BASE
+*/
+NTSTATUS imessaging_register_tmp(struct imessaging_context *msg, void *private_data,
+ msg_callback_t fn, uint32_t *msg_type)
+{
+ struct dispatch_fn *d;
+ int id;
+
+ d = talloc_zero(msg->dispatch, struct dispatch_fn);
+ NT_STATUS_HAVE_NO_MEMORY(d);
+ d->private_data = private_data;
+ d->fn = fn;
+
+ id = idr_get_new_above(msg->dispatch_tree, d, MSG_TMP_BASE, UINT16_MAX);
+ if (id == -1) {
+ talloc_free(d);
+ return NT_STATUS_TOO_MANY_CONTEXT_IDS;
+ }
+
+ d->msg_type = (uint32_t)id;
+ (*msg_type) = d->msg_type;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ De-register the function for a particular message type.
+*/
+void imessaging_deregister(struct imessaging_context *msg, uint32_t msg_type, void *private_data)
+{
+ struct dispatch_fn *d, *next;
+
+ if (msg_type >= msg->num_types) {
+ d = (struct dispatch_fn *)idr_find(msg->dispatch_tree,
+ msg_type);
+ if (!d) return;
+ idr_remove(msg->dispatch_tree, msg_type);
+ talloc_free(d);
+ return;
+ }
+
+ for (d = msg->dispatch[msg_type]; d; d = next) {
+ next = d->next;
+ if (d->private_data == private_data) {
+ DLIST_REMOVE(msg->dispatch[msg_type], d);
+ talloc_free(d);
+ }
+ }
+}
+
+/*
+*/
+int imessaging_cleanup(struct imessaging_context *msg)
+{
+ if (!msg) {
+ return 0;
+ }
+ return 0;
+}
+
+static void imessaging_dgm_recv(struct tevent_context *ev,
+ const uint8_t *buf, size_t buf_len,
+ int *fds, size_t num_fds,
+ void *private_data);
+
+/* Keep a list of imessaging contexts */
+static struct imessaging_context *msg_ctxs;
+
+/*
+ * A process has terminated, clean-up any names it has registered.
+ */
+NTSTATUS imessaging_process_cleanup(
+ struct imessaging_context *msg_ctx,
+ pid_t pid)
+{
+ struct irpc_name_records *names = NULL;
+ uint32_t i = 0;
+ uint32_t j = 0;
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+ if (mem_ctx == NULL) {
+ DBG_ERR("OOM unable to clean up messaging for process (%d)\n",
+ pid);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ names = irpc_all_servers(msg_ctx, mem_ctx);
+ if (names == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_OK;
+ }
+ for (i = 0; i < names->num_records; i++) {
+ for (j = 0; j < names->names[i]->count; j++) {
+ if (names->names[i]->ids[j].pid == pid) {
+ int ret = server_id_db_prune_name(
+ msg_ctx->names,
+ names->names[i]->name,
+ names->names[i]->ids[j]);
+ if (ret != 0 && ret != ENOENT) {
+ TALLOC_FREE(mem_ctx);
+ return map_nt_error_from_unix_common(
+ ret);
+ }
+ }
+ }
+ }
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_OK;
+}
+
+static int imessaging_context_destructor(struct imessaging_context *msg)
+{
+ struct irpc_request *irpc = NULL;
+ struct irpc_request *next = NULL;
+
+ for (irpc = msg->requests; irpc != NULL; irpc = next) {
+ next = irpc->next;
+
+ DLIST_REMOVE(msg->requests, irpc);
+ irpc->callid = -1;
+ }
+
+ DLIST_REMOVE(msg_ctxs, msg);
+ TALLOC_FREE(msg->msg_dgm_ref);
+ return 0;
+}
+
+/*
+ * Cleanup messaging dgm contexts on a specific event context.
+ *
+ * We must make sure to unref all messaging_dgm_ref's *before* the
+ * tevent context goes away. Only when the last ref is freed, the
+ * refcounted messaging dgm context will be freed.
+ */
+void imessaging_dgm_unref_ev(struct tevent_context *ev)
+{
+ struct imessaging_context *msg = NULL;
+
+ for (msg = msg_ctxs; msg != NULL; msg = msg->next) {
+ if (msg->ev == ev) {
+ TALLOC_FREE(msg->msg_dgm_ref);
+ }
+ }
+}
+
+static NTSTATUS imessaging_reinit(struct imessaging_context *msg)
+{
+ int ret = -1;
+
+ TALLOC_FREE(msg->msg_dgm_ref);
+
+ if (msg->discard_incoming) {
+ msg->num_incoming_listeners = 0;
+ } else {
+ msg->num_incoming_listeners = 1;
+ }
+
+ msg->server_id.pid = getpid();
+
+ msg->msg_dgm_ref = messaging_dgm_ref(msg,
+ msg->ev,
+ &msg->server_id.unique_id,
+ msg->sock_dir,
+ msg->lock_dir,
+ imessaging_dgm_recv,
+ msg,
+ &ret);
+
+ if (msg->msg_dgm_ref == NULL) {
+ DEBUG(2, ("messaging_dgm_ref failed: %s\n",
+ strerror(ret)));
+ return map_nt_error_from_unix_common(ret);
+ }
+
+ server_id_db_reinit(msg->names, msg->server_id);
+ return NT_STATUS_OK;
+}
+
+/*
+ * Must be called after a fork.
+ */
+NTSTATUS imessaging_reinit_all(void)
+{
+ struct imessaging_context *msg = NULL;
+
+ for (msg = msg_ctxs; msg != NULL; msg = msg->next) {
+ NTSTATUS status = imessaging_reinit(msg);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ create the listening socket and setup the dispatcher
+*/
+static struct imessaging_context *imessaging_init_internal(
+ TALLOC_CTX *mem_ctx,
+ bool discard_incoming,
+ struct loadparm_context *lp_ctx,
+ struct server_id server_id,
+ struct tevent_context *ev)
+{
+ NTSTATUS status;
+ struct imessaging_context *msg;
+ bool ok;
+ int ret;
+ const char *lock_dir = NULL;
+ int tdb_flags = TDB_INCOMPATIBLE_HASH | TDB_CLEAR_IF_FIRST;
+
+ if (ev == NULL) {
+ return NULL;
+ }
+
+ msg = talloc_zero(mem_ctx, struct imessaging_context);
+ if (msg == NULL) {
+ return NULL;
+ }
+ msg->ev = ev;
+ msg->discard_incoming = discard_incoming;
+ if (msg->discard_incoming) {
+ msg->num_incoming_listeners = 0;
+ } else {
+ msg->num_incoming_listeners = 1;
+ }
+
+ talloc_set_destructor(msg, imessaging_context_destructor);
+
+ /* create the messaging directory if needed */
+
+ lock_dir = lpcfg_lock_directory(lp_ctx);
+ if (lock_dir == NULL) {
+ goto fail;
+ }
+
+ msg->sock_dir = lpcfg_private_path(msg, lp_ctx, "msg.sock");
+ if (msg->sock_dir == NULL) {
+ goto fail;
+ }
+ ok = directory_create_or_exist_strict(msg->sock_dir, geteuid(), 0700);
+ if (!ok) {
+ goto fail;
+ }
+
+ msg->lock_dir = lpcfg_lock_path(msg, lp_ctx, "msg.lock");
+ if (msg->lock_dir == NULL) {
+ goto fail;
+ }
+ ok = directory_create_or_exist_strict(msg->lock_dir, geteuid(), 0755);
+ if (!ok) {
+ goto fail;
+ }
+
+ msg->msg_dgm_ref = messaging_dgm_ref(
+ msg, ev, &server_id.unique_id, msg->sock_dir, msg->lock_dir,
+ imessaging_dgm_recv, msg, &ret);
+
+ if (msg->msg_dgm_ref == NULL) {
+ goto fail;
+ }
+
+ msg->server_id = server_id;
+ msg->idr = idr_init(msg);
+ if (msg->idr == NULL) {
+ goto fail;
+ }
+
+ msg->dispatch_tree = idr_init(msg);
+ if (msg->dispatch_tree == NULL) {
+ goto fail;
+ }
+
+ msg->start_time = timeval_current();
+
+ tdb_flags |= lpcfg_tdb_flags(lp_ctx, 0);
+
+ /*
+ * This context holds a destructor that cleans up any names
+ * registered on this context on talloc_free()
+ */
+ msg->names = server_id_db_init(msg, server_id, lock_dir, 0, tdb_flags);
+ if (msg->names == NULL) {
+ goto fail;
+ }
+
+ status = imessaging_register(msg, NULL, MSG_PING, ping_message);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ status = imessaging_register(msg, NULL, MSG_REQ_POOL_USAGE,
+ pool_message);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ status = imessaging_register(msg, NULL, MSG_IRPC, irpc_handler);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ status = imessaging_register(msg, NULL, MSG_REQ_RINGBUF_LOG,
+ ringbuf_log_msg);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ status = imessaging_register(msg, NULL, MSG_DEBUG,
+ debug_imessage);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ status = imessaging_register(msg, NULL, MSG_REQ_DEBUGLEVEL,
+ debuglevel_imessage);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ status = IRPC_REGISTER(msg, irpc, IRPC_UPTIME, irpc_uptime, msg);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+#if defined(DEVELOPER) || defined(ENABLE_SELFTEST)
+ /*
+ * Register handlers for messages specific to developer and
+ * self test builds
+ */
+ status = imessaging_register_extra_handlers(msg);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+#endif /* defined(DEVELOPER) || defined(ENABLE_SELFTEST) */
+
+ DLIST_ADD(msg_ctxs, msg);
+
+ return msg;
+fail:
+ talloc_free(msg);
+ return NULL;
+}
+
+/*
+ create the listening socket and setup the dispatcher
+*/
+struct imessaging_context *imessaging_init(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct server_id server_id,
+ struct tevent_context *ev)
+{
+ bool discard_incoming = false;
+ return imessaging_init_internal(mem_ctx,
+ discard_incoming,
+ lp_ctx,
+ server_id,
+ ev);
+}
+
+struct imessaging_context *imessaging_init_discard_incoming(
+ TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct server_id server_id,
+ struct tevent_context *ev)
+{
+ bool discard_incoming = true;
+ return imessaging_init_internal(mem_ctx,
+ discard_incoming,
+ lp_ctx,
+ server_id,
+ ev);
+}
+
+struct imessaging_post_state {
+ struct imessaging_context *msg_ctx;
+ struct imessaging_post_state **busy_ref;
+ size_t buf_len;
+ uint8_t buf[];
+};
+
+static int imessaging_post_state_destructor(struct imessaging_post_state *state)
+{
+ if (state->busy_ref != NULL) {
+ *state->busy_ref = NULL;
+ state->busy_ref = NULL;
+ }
+ return 0;
+}
+
+static void imessaging_post_handler(struct tevent_context *ev,
+ struct tevent_immediate *ti,
+ void *private_data)
+{
+ struct imessaging_post_state *state = talloc_get_type_abort(
+ private_data, struct imessaging_post_state);
+
+ if (state == NULL) {
+ return;
+ }
+
+ /*
+ * In usecases like using messaging_client_init() with irpc processing
+ * we may free the imessaging_context during the messaging handler.
+ * imessaging_post_state is a child of imessaging_context and
+ * might be implicitly free'ed before the explicit TALLOC_FREE(state).
+ *
+ * The busy_ref pointer makes sure the destructor clears
+ * the local 'state' variable.
+ */
+
+ SMB_ASSERT(state->busy_ref == NULL);
+ state->busy_ref = &state;
+
+ imessaging_dgm_recv(ev, state->buf, state->buf_len, NULL, 0,
+ state->msg_ctx);
+
+ state->busy_ref = NULL;
+ TALLOC_FREE(state);
+}
+
+static int imessaging_post_self(struct imessaging_context *msg,
+ const uint8_t *buf, size_t buf_len)
+{
+ struct tevent_immediate *ti;
+ struct imessaging_post_state *state;
+
+ state = talloc_size(
+ msg, offsetof(struct imessaging_post_state, buf) + buf_len);
+ if (state == NULL) {
+ return ENOMEM;
+ }
+ talloc_set_name_const(state, "struct imessaging_post_state");
+
+ talloc_set_destructor(state, imessaging_post_state_destructor);
+
+ ti = tevent_create_immediate(state);
+ if (ti == NULL) {
+ TALLOC_FREE(state);
+ return ENOMEM;
+ }
+
+ state->msg_ctx = msg;
+ state->busy_ref = NULL;
+ state->buf_len = buf_len;
+ memcpy(state->buf, buf, buf_len);
+
+ tevent_schedule_immediate(ti, msg->ev, imessaging_post_handler,
+ state);
+
+ return 0;
+}
+
+static void imessaging_dgm_recv(struct tevent_context *ev,
+ const uint8_t *buf, size_t buf_len,
+ int *fds, size_t num_fds,
+ void *private_data)
+{
+ struct imessaging_context *msg = talloc_get_type_abort(
+ private_data, struct imessaging_context);
+ uint32_t msg_type;
+ struct server_id src, dst;
+ struct server_id_buf srcbuf, dstbuf;
+ DATA_BLOB data;
+
+ if (buf_len < MESSAGE_HDR_LENGTH) {
+ /* Invalid message, ignore */
+ return;
+ }
+
+ if (msg->num_incoming_listeners == 0) {
+ struct server_id_buf selfbuf;
+
+ message_hdr_get(&msg_type, &src, &dst, buf);
+
+ DBG_DEBUG("not listening - discarding message from "
+ "src[%s] to dst[%s] (self[%s]) type=0x%x "
+ "on %s event context\n",
+ server_id_str_buf(src, &srcbuf),
+ server_id_str_buf(dst, &dstbuf),
+ server_id_str_buf(msg->server_id, &selfbuf),
+ (unsigned)msg_type,
+ (ev != msg->ev) ? "different" : "main");
+ return;
+ }
+
+ if (ev != msg->ev) {
+ int ret;
+ ret = imessaging_post_self(msg, buf, buf_len);
+ if (ret != 0) {
+ DBG_WARNING("imessaging_post_self failed: %s\n",
+ strerror(ret));
+ }
+ return;
+ }
+
+ message_hdr_get(&msg_type, &src, &dst, buf);
+
+ data.data = discard_const_p(uint8_t, buf + MESSAGE_HDR_LENGTH);
+ data.length = buf_len - MESSAGE_HDR_LENGTH;
+
+ if ((cluster_id_equal(&dst, &msg->server_id)) ||
+ ((dst.task_id == 0) && (msg->server_id.pid == 0))) {
+ struct dispatch_fn *d, *next;
+
+ DEBUG(10, ("%s: dst %s matches my id: %s, type=0x%x\n",
+ __func__,
+ server_id_str_buf(dst, &dstbuf),
+ server_id_str_buf(msg->server_id, &srcbuf),
+ (unsigned)msg_type));
+
+ d = imessaging_find_dispatch(msg, msg_type);
+
+ for (; d; d = next) {
+ next = d->next;
+ d->fn(msg,
+ d->private_data,
+ d->msg_type,
+ src,
+ num_fds,
+ fds,
+ &data);
+ }
+ } else {
+ DEBUG(10, ("%s: Ignoring type=0x%x dst %s, I am %s, \n",
+ __func__, (unsigned)msg_type,
+ server_id_str_buf(dst, &dstbuf),
+ server_id_str_buf(msg->server_id, &srcbuf)));
+ }
+}
+
+/*
+ A hack, for the short term until we get 'client only' messaging in place
+*/
+struct imessaging_context *imessaging_client_init(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct tevent_context *ev)
+{
+ struct server_id id;
+ ZERO_STRUCT(id);
+ id.pid = getpid();
+ id.task_id = generate_random();
+ id.vnn = NONCLUSTER_VNN;
+
+ /* This is because we are not in the s3 serverid database */
+ id.unique_id = SERVERID_UNIQUE_ID_NOT_TO_VERIFY;
+
+ return imessaging_init_discard_incoming(mem_ctx, lp_ctx, id, ev);
+}
+
+/*
+ a list of registered irpc server functions
+*/
+struct irpc_list {
+ struct irpc_list *next, *prev;
+ struct GUID uuid;
+ const struct ndr_interface_table *table;
+ int callnum;
+ irpc_function_t fn;
+ void *private_data;
+};
+
+
+/*
+ register a irpc server function
+*/
+NTSTATUS irpc_register(struct imessaging_context *msg_ctx,
+ const struct ndr_interface_table *table,
+ int callnum, irpc_function_t fn, void *private_data)
+{
+ struct irpc_list *irpc;
+
+ /* override an existing handler, if any */
+ for (irpc=msg_ctx->irpc; irpc; irpc=irpc->next) {
+ if (irpc->table == table && irpc->callnum == callnum) {
+ break;
+ }
+ }
+ if (irpc == NULL) {
+ irpc = talloc(msg_ctx, struct irpc_list);
+ NT_STATUS_HAVE_NO_MEMORY(irpc);
+ DLIST_ADD(msg_ctx->irpc, irpc);
+ }
+
+ irpc->table = table;
+ irpc->callnum = callnum;
+ irpc->fn = fn;
+ irpc->private_data = private_data;
+ irpc->uuid = irpc->table->syntax_id.uuid;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ handle an incoming irpc reply message
+*/
+static void irpc_handler_reply(struct imessaging_context *msg_ctx, struct irpc_message *m)
+{
+ struct irpc_request *irpc;
+
+ irpc = (struct irpc_request *)idr_find(msg_ctx->idr, m->header.callid);
+ if (irpc == NULL) return;
+
+ irpc->incoming.handler(irpc, m);
+}
+
+/*
+ send a irpc reply
+*/
+NTSTATUS irpc_send_reply(struct irpc_message *m, NTSTATUS status)
+{
+ struct ndr_push *push;
+ DATA_BLOB packet;
+ enum ndr_err_code ndr_err;
+
+ m->header.status = status;
+
+ /* setup the reply */
+ push = ndr_push_init_ctx(m->ndr);
+ if (push == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ m->header.flags |= IRPC_FLAG_REPLY;
+ m->header.creds.token= NULL;
+
+ /* construct the packet */
+ ndr_err = ndr_push_irpc_header(push, NDR_SCALARS|NDR_BUFFERS, &m->header);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ goto failed;
+ }
+
+ ndr_err = m->irpc->table->calls[m->irpc->callnum].ndr_push(push, NDR_OUT, m->data);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ goto failed;
+ }
+
+ /* send the reply message */
+ packet = ndr_push_blob(push);
+ status = imessaging_send(m->msg_ctx, m->from, MSG_IRPC, &packet);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+failed:
+ talloc_free(m);
+ return status;
+}
+
+/*
+ handle an incoming irpc request message
+*/
+static void irpc_handler_request(struct imessaging_context *msg_ctx,
+ struct irpc_message *m)
+{
+ struct irpc_list *i;
+ void *r;
+ enum ndr_err_code ndr_err;
+
+ for (i=msg_ctx->irpc; i; i=i->next) {
+ if (GUID_equal(&i->uuid, &m->header.uuid) &&
+ i->table->syntax_id.if_version == m->header.if_version &&
+ i->callnum == m->header.callnum) {
+ break;
+ }
+ }
+
+ if (i == NULL) {
+ /* no registered handler for this message */
+ talloc_free(m);
+ return;
+ }
+
+ /* allocate space for the structure */
+ r = talloc_zero_size(m->ndr, i->table->calls[m->header.callnum].struct_size);
+ if (r == NULL) goto failed;
+
+ m->ndr->flags |= LIBNDR_FLAG_REF_ALLOC;
+
+ /* parse the request data */
+ ndr_err = i->table->calls[i->callnum].ndr_pull(m->ndr, NDR_IN, r);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) goto failed;
+
+ /* make the call */
+ m->private_data= i->private_data;
+ m->defer_reply = false;
+ m->no_reply = false;
+ m->msg_ctx = msg_ctx;
+ m->irpc = i;
+ m->data = r;
+
+ m->header.status = i->fn(m, r);
+
+ if (m->no_reply) {
+ /* the server function won't ever be replying to this request */
+ talloc_free(m);
+ return;
+ }
+
+ if (m->defer_reply) {
+ /* the server function has asked to defer the reply to later */
+ talloc_steal(msg_ctx, m);
+ return;
+ }
+
+ irpc_send_reply(m, m->header.status);
+ return;
+
+failed:
+ talloc_free(m);
+}
+
+/*
+ handle an incoming irpc message
+*/
+static void irpc_handler(struct imessaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id src,
+ size_t num_fds,
+ int *fds,
+ DATA_BLOB *packet)
+{
+ struct irpc_message *m;
+ enum ndr_err_code ndr_err;
+
+ if (num_fds != 0) {
+ DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
+ return;
+ }
+
+ m = talloc(msg_ctx, struct irpc_message);
+ if (m == NULL) goto failed;
+
+ m->from = src;
+
+ m->ndr = ndr_pull_init_blob(packet, m);
+ if (m->ndr == NULL) goto failed;
+
+ m->ndr->flags |= LIBNDR_FLAG_REF_ALLOC;
+
+ ndr_err = ndr_pull_irpc_header(m->ndr, NDR_BUFFERS|NDR_SCALARS, &m->header);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) goto failed;
+
+ if (m->header.flags & IRPC_FLAG_REPLY) {
+ irpc_handler_reply(msg_ctx, m);
+ } else {
+ irpc_handler_request(msg_ctx, m);
+ }
+ return;
+
+failed:
+ talloc_free(m);
+}
+
+
+/*
+ destroy a irpc request
+*/
+static int irpc_destructor(struct irpc_request *irpc)
+{
+ if (irpc->callid != -1) {
+ DLIST_REMOVE(irpc->msg_ctx->requests, irpc);
+ idr_remove(irpc->msg_ctx->idr, irpc->callid);
+ if (irpc->msg_ctx->discard_incoming) {
+ SMB_ASSERT(irpc->msg_ctx->num_incoming_listeners > 0);
+ } else {
+ SMB_ASSERT(irpc->msg_ctx->num_incoming_listeners > 1);
+ }
+ irpc->msg_ctx->num_incoming_listeners -= 1;
+ irpc->callid = -1;
+ }
+
+ return 0;
+}
+
+/*
+ add a string name that this irpc server can be called on
+
+ It will be removed from the DB either via irpc_remove_name or on
+ talloc_free(msg_ctx->names).
+*/
+NTSTATUS irpc_add_name(struct imessaging_context *msg_ctx, const char *name)
+{
+ int ret;
+
+ ret = server_id_db_add(msg_ctx->names, name);
+ if (ret != 0) {
+ return map_nt_error_from_unix_common(ret);
+ }
+ return NT_STATUS_OK;
+}
+
+static int all_servers_func(const char *name, unsigned num_servers,
+ const struct server_id *servers,
+ void *private_data)
+{
+ struct irpc_name_records *name_records = talloc_get_type(
+ private_data, struct irpc_name_records);
+ struct irpc_name_record *name_record;
+ uint32_t i;
+
+ name_records->names
+ = talloc_realloc(name_records, name_records->names,
+ struct irpc_name_record *, name_records->num_records+1);
+ if (!name_records->names) {
+ return -1;
+ }
+
+ name_records->names[name_records->num_records] = name_record
+ = talloc(name_records->names,
+ struct irpc_name_record);
+ if (!name_record) {
+ return -1;
+ }
+
+ name_records->num_records++;
+
+ name_record->name = talloc_strdup(name_record, name);
+ if (!name_record->name) {
+ return -1;
+ }
+
+ name_record->count = num_servers;
+ name_record->ids = talloc_array(name_record, struct server_id,
+ num_servers);
+ if (name_record->ids == NULL) {
+ return -1;
+ }
+ for (i=0;i<name_record->count;i++) {
+ name_record->ids[i] = servers[i];
+ }
+ return 0;
+}
+
+/*
+ return a list of server ids for a server name
+*/
+struct irpc_name_records *irpc_all_servers(struct imessaging_context *msg_ctx,
+ TALLOC_CTX *mem_ctx)
+{
+ int ret;
+ struct irpc_name_records *name_records = talloc_zero(mem_ctx, struct irpc_name_records);
+ if (name_records == NULL) {
+ return NULL;
+ }
+
+ ret = server_id_db_traverse_read(msg_ctx->names, all_servers_func,
+ name_records);
+ if (ret == -1) {
+ TALLOC_FREE(name_records);
+ return NULL;
+ }
+
+ return name_records;
+}
+
+/*
+ remove a name from a messaging context
+*/
+void irpc_remove_name(struct imessaging_context *msg_ctx, const char *name)
+{
+ server_id_db_remove(msg_ctx->names, name);
+}
+
+struct server_id imessaging_get_server_id(struct imessaging_context *msg_ctx)
+{
+ return msg_ctx->server_id;
+}
+
+struct irpc_bh_state {
+ struct imessaging_context *msg_ctx;
+ struct server_id server_id;
+ const struct ndr_interface_table *table;
+ uint32_t timeout;
+ struct security_token *token;
+};
+
+static bool irpc_bh_is_connected(struct dcerpc_binding_handle *h)
+{
+ struct irpc_bh_state *hs = dcerpc_binding_handle_data(h,
+ struct irpc_bh_state);
+
+ if (!hs->msg_ctx) {
+ return false;
+ }
+
+ return true;
+}
+
+static uint32_t irpc_bh_set_timeout(struct dcerpc_binding_handle *h,
+ uint32_t timeout)
+{
+ struct irpc_bh_state *hs = dcerpc_binding_handle_data(h,
+ struct irpc_bh_state);
+ uint32_t old = hs->timeout;
+
+ hs->timeout = timeout;
+
+ return old;
+}
+
+struct irpc_bh_raw_call_state {
+ struct irpc_request *irpc;
+ uint32_t opnum;
+ DATA_BLOB in_data;
+ DATA_BLOB in_packet;
+ DATA_BLOB out_data;
+};
+
+static void irpc_bh_raw_call_incoming_handler(struct irpc_request *irpc,
+ struct irpc_message *m);
+
+static struct tevent_req *irpc_bh_raw_call_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dcerpc_binding_handle *h,
+ const struct GUID *object,
+ uint32_t opnum,
+ uint32_t in_flags,
+ const uint8_t *in_data,
+ size_t in_length)
+{
+ struct irpc_bh_state *hs =
+ dcerpc_binding_handle_data(h,
+ struct irpc_bh_state);
+ struct tevent_req *req;
+ struct irpc_bh_raw_call_state *state;
+ bool ok;
+ struct irpc_header header;
+ struct ndr_push *ndr;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct irpc_bh_raw_call_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->opnum = opnum;
+ state->in_data.data = discard_const_p(uint8_t, in_data);
+ state->in_data.length = in_length;
+
+ ok = irpc_bh_is_connected(h);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
+ return tevent_req_post(req, ev);
+ }
+
+ state->irpc = talloc_zero(state, struct irpc_request);
+ if (tevent_req_nomem(state->irpc, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->irpc->msg_ctx = hs->msg_ctx;
+ state->irpc->callid = idr_get_new(hs->msg_ctx->idr,
+ state->irpc, UINT16_MAX);
+ if (state->irpc->callid == -1) {
+ tevent_req_nterror(req, NT_STATUS_INSUFFICIENT_RESOURCES);
+ return tevent_req_post(req, ev);
+ }
+ state->irpc->incoming.handler = irpc_bh_raw_call_incoming_handler;
+ state->irpc->incoming.private_data = req;
+
+ /* make sure we accept incoming messages */
+ SMB_ASSERT(state->irpc->msg_ctx->num_incoming_listeners < UINT64_MAX);
+ state->irpc->msg_ctx->num_incoming_listeners += 1;
+ DLIST_ADD_END(state->irpc->msg_ctx->requests, state->irpc);
+ talloc_set_destructor(state->irpc, irpc_destructor);
+
+ /* setup the header */
+ header.uuid = hs->table->syntax_id.uuid;
+
+ header.if_version = hs->table->syntax_id.if_version;
+ header.callid = state->irpc->callid;
+ header.callnum = state->opnum;
+ header.flags = 0;
+ header.status = NT_STATUS_OK;
+ header.creds.token= hs->token;
+
+ /* construct the irpc packet */
+ ndr = ndr_push_init_ctx(state->irpc);
+ if (tevent_req_nomem(ndr, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ ndr_err = ndr_push_irpc_header(ndr, NDR_SCALARS|NDR_BUFFERS, &header);
+ status = ndr_map_error2ntstatus(ndr_err);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+
+ ndr_err = ndr_push_bytes(ndr, in_data, in_length);
+ status = ndr_map_error2ntstatus(ndr_err);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+
+ /* and send it */
+ state->in_packet = ndr_push_blob(ndr);
+ status = imessaging_send(hs->msg_ctx, hs->server_id,
+ MSG_IRPC, &state->in_packet);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+
+ if (hs->timeout != IRPC_CALL_TIMEOUT_INF) {
+ /* set timeout-callback in case caller wants that */
+ ok = tevent_req_set_endtime(req, ev, timeval_current_ofs(hs->timeout, 0));
+ if (!ok) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ return req;
+}
+
+static void irpc_bh_raw_call_incoming_handler(struct irpc_request *irpc,
+ struct irpc_message *m)
+{
+ struct tevent_req *req =
+ talloc_get_type_abort(irpc->incoming.private_data,
+ struct tevent_req);
+ struct irpc_bh_raw_call_state *state =
+ tevent_req_data(req,
+ struct irpc_bh_raw_call_state);
+
+ talloc_steal(state, m);
+
+ if (!NT_STATUS_IS_OK(m->header.status)) {
+ tevent_req_nterror(req, m->header.status);
+ return;
+ }
+
+ state->out_data = data_blob_talloc(state,
+ m->ndr->data + m->ndr->offset,
+ m->ndr->data_size - m->ndr->offset);
+ if ((m->ndr->data_size - m->ndr->offset) > 0 && !state->out_data.data) {
+ tevent_req_oom(req);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static NTSTATUS irpc_bh_raw_call_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **out_data,
+ size_t *out_length,
+ uint32_t *out_flags)
+{
+ struct irpc_bh_raw_call_state *state =
+ tevent_req_data(req,
+ struct irpc_bh_raw_call_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *out_data = talloc_move(mem_ctx, &state->out_data.data);
+ *out_length = state->out_data.length;
+ *out_flags = 0;
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+struct irpc_bh_disconnect_state {
+ uint8_t _dummy;
+};
+
+static struct tevent_req *irpc_bh_disconnect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dcerpc_binding_handle *h)
+{
+ struct irpc_bh_state *hs = dcerpc_binding_handle_data(h,
+ struct irpc_bh_state);
+ struct tevent_req *req;
+ struct irpc_bh_disconnect_state *state;
+ bool ok;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct irpc_bh_disconnect_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ ok = irpc_bh_is_connected(h);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
+ return tevent_req_post(req, ev);
+ }
+
+ hs->msg_ctx = NULL;
+
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+}
+
+static NTSTATUS irpc_bh_disconnect_recv(struct tevent_req *req)
+{
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+static bool irpc_bh_ref_alloc(struct dcerpc_binding_handle *h)
+{
+ return true;
+}
+
+static const struct dcerpc_binding_handle_ops irpc_bh_ops = {
+ .name = "wbint",
+ .is_connected = irpc_bh_is_connected,
+ .set_timeout = irpc_bh_set_timeout,
+ .raw_call_send = irpc_bh_raw_call_send,
+ .raw_call_recv = irpc_bh_raw_call_recv,
+ .disconnect_send = irpc_bh_disconnect_send,
+ .disconnect_recv = irpc_bh_disconnect_recv,
+
+ .ref_alloc = irpc_bh_ref_alloc,
+};
+
+/* initialise a irpc binding handle */
+struct dcerpc_binding_handle *irpc_binding_handle(TALLOC_CTX *mem_ctx,
+ struct imessaging_context *msg_ctx,
+ struct server_id server_id,
+ const struct ndr_interface_table *table)
+{
+ struct dcerpc_binding_handle *h;
+ struct irpc_bh_state *hs;
+
+ h = dcerpc_binding_handle_create(mem_ctx,
+ &irpc_bh_ops,
+ NULL,
+ table,
+ &hs,
+ struct irpc_bh_state,
+ __location__);
+ if (h == NULL) {
+ return NULL;
+ }
+ hs->msg_ctx = msg_ctx;
+ hs->server_id = server_id;
+ hs->table = table;
+ hs->timeout = IRPC_CALL_TIMEOUT;
+
+ return h;
+}
+
+struct dcerpc_binding_handle *irpc_binding_handle_by_name(TALLOC_CTX *mem_ctx,
+ struct imessaging_context *msg_ctx,
+ const char *dest_task,
+ const struct ndr_interface_table *table)
+{
+ struct dcerpc_binding_handle *h;
+ unsigned num_sids;
+ struct server_id *sids;
+ struct server_id sid;
+ NTSTATUS status;
+
+ /* find the server task */
+
+ status = irpc_servers_byname(msg_ctx, mem_ctx, dest_task,
+ &num_sids, &sids);
+ if (!NT_STATUS_IS_OK(status)) {
+ errno = EADDRNOTAVAIL;
+ return NULL;
+ }
+ sid = sids[0];
+ talloc_free(sids);
+
+ h = irpc_binding_handle(mem_ctx, msg_ctx,
+ sid, table);
+ if (h == NULL) {
+ return NULL;
+ }
+
+ return h;
+}
+
+void irpc_binding_handle_add_security_token(struct dcerpc_binding_handle *h,
+ struct security_token *token)
+{
+ struct irpc_bh_state *hs =
+ dcerpc_binding_handle_data(h,
+ struct irpc_bh_state);
+
+ hs->token = token;
+}
diff --git a/source4/lib/messaging/messaging.h b/source4/lib/messaging/messaging.h
new file mode 100644
index 0000000..e7ae9e8
--- /dev/null
+++ b/source4/lib/messaging/messaging.h
@@ -0,0 +1,70 @@
+/*
+ Unix SMB/CIFS implementation.
+ messages.c header
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) 2001, 2002 by Martin Pool
+
+ 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 _SOURCE4_LIB_MESSAGING_MESSAGES_H_
+#define _SOURCE4_LIB_MESSAGING_MESSAGES_H_
+
+#include "librpc/gen_ndr/server_id.h"
+#include "librpc/gen_ndr/messaging.h"
+
+struct imessaging_context;
+
+/* taskid for messaging of parent process */
+#define SAMBA_PARENT_TASKID 0
+
+typedef void (*msg_callback_t)(
+ struct imessaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ size_t num_fds,
+ int *fds,
+ DATA_BLOB *data);
+
+NTSTATUS imessaging_send(struct imessaging_context *msg, struct server_id server,
+ uint32_t msg_type, const DATA_BLOB *data);
+NTSTATUS imessaging_register(struct imessaging_context *msg, void *private_data,
+ uint32_t msg_type,
+ msg_callback_t fn);
+NTSTATUS imessaging_register_tmp(struct imessaging_context *msg, void *private_data,
+ msg_callback_t fn, uint32_t *msg_type);
+struct imessaging_context *imessaging_init(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct server_id server_id,
+ struct tevent_context *ev);
+struct imessaging_context *imessaging_init_discard_incoming(
+ TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct server_id server_id,
+ struct tevent_context *ev);
+void imessaging_dgm_unref_ev(struct tevent_context *ev);
+NTSTATUS imessaging_reinit_all(void);
+int imessaging_cleanup(struct imessaging_context *msg);
+struct imessaging_context *imessaging_client_init(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct tevent_context *ev);
+NTSTATUS imessaging_send_ptr(struct imessaging_context *msg, struct server_id server,
+ uint32_t msg_type, void *ptr);
+void imessaging_deregister(struct imessaging_context *msg, uint32_t msg_type, void *private_data);
+struct server_id imessaging_get_server_id(struct imessaging_context *msg_ctx);
+NTSTATUS imessaging_process_cleanup(struct imessaging_context *msg_ctx,
+ pid_t pid);
+
+#endif
diff --git a/source4/lib/messaging/messaging_handlers.c b/source4/lib/messaging/messaging_handlers.c
new file mode 100644
index 0000000..57e3e1c
--- /dev/null
+++ b/source4/lib/messaging/messaging_handlers.c
@@ -0,0 +1,135 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Handers for non core Samba internal messages
+
+ Handlers for messages that are only included in developer and self test
+ builds.
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2018
+
+ 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 "lib/util/server_id.h"
+#include "messaging/messaging.h"
+#include "messaging/messaging_internal.h"
+
+#if defined(DEVELOPER) || defined(ENABLE_SELFTEST)
+
+/*
+ * Inject a fault into the currently running process
+ */
+static void do_inject_fault(struct imessaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id src,
+ size_t num_fds,
+ int *fds,
+ DATA_BLOB *data)
+{
+ int sig;
+ struct server_id_buf tmp;
+
+ if (num_fds != 0) {
+ DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
+ return;
+ }
+
+ if (data->length != sizeof(sig)) {
+ DBG_ERR("Process %s sent bogus signal injection request\n",
+ server_id_str_buf(src, &tmp));
+ return;
+ }
+
+ sig = *(int *)data->data;
+ if (sig == -1) {
+ DBG_ERR("Process %s requested an iternal failure, "
+ "calling exit(1)\n",
+ server_id_str_buf(src, &tmp));
+ exit(1);
+ }
+
+#if HAVE_STRSIGNAL
+ DBG_ERR("Process %s requested injection of signal %d (%s)\n",
+ server_id_str_buf(src, &tmp),
+ sig,
+ strsignal(sig));
+#else
+ DBG_ERR("Process %s requested injection of signal %d\n",
+ server_id_str_buf(src, &tmp),
+ sig);
+#endif
+
+ kill(getpid(), sig);
+}
+
+/*
+ * Cause the current process to sleep for a specified number of seconds
+ */
+static void do_sleep(struct imessaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id src,
+ size_t num_fds,
+ int *fds,
+ DATA_BLOB *data)
+{
+ unsigned int seconds;
+ struct server_id_buf tmp;
+
+ if (num_fds != 0) {
+ DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
+ return;
+ }
+
+ if (data->length != sizeof(seconds)) {
+ DBG_ERR("Process %s sent bogus sleep request\n",
+ server_id_str_buf(src, &tmp));
+ return;
+ }
+
+ seconds = *(unsigned int *)data->data;
+ DBG_ERR("Process %s requested a sleep of %u seconds\n",
+ server_id_str_buf(src, &tmp),
+ seconds);
+ sleep(seconds);
+ DBG_ERR("Restarting after %u second sleep requested by process %s\n",
+ seconds,
+ server_id_str_buf(src, &tmp));
+}
+
+/*
+ * Register the extra messaging handlers
+ */
+NTSTATUS imessaging_register_extra_handlers(struct imessaging_context *msg)
+{
+ NTSTATUS status;
+
+ status = imessaging_register(
+ msg, NULL, MSG_SMB_INJECT_FAULT, do_inject_fault);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = imessaging_register(msg, NULL, MSG_SMB_SLEEP, do_sleep);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+#endif /* defined(DEVELOPER) || defined(ENABLE_SELFTEST) */
diff --git a/source4/lib/messaging/messaging_internal.h b/source4/lib/messaging/messaging_internal.h
new file mode 100644
index 0000000..6281bda
--- /dev/null
+++ b/source4/lib/messaging/messaging_internal.h
@@ -0,0 +1,50 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Samba internal messaging functions
+
+ Copyright (C) Andrew Tridgell 2004
+
+ 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/>.
+*/
+
+struct irpc_request;
+
+struct imessaging_context {
+ struct imessaging_context *prev, *next;
+ struct tevent_context *ev;
+ struct server_id server_id;
+ const char *sock_dir;
+ const char *lock_dir;
+ struct dispatch_fn **dispatch;
+ uint32_t num_types;
+ struct idr_context *dispatch_tree;
+ struct irpc_list *irpc;
+ struct idr_context *idr;
+ struct irpc_request *requests;
+ struct server_id_db *names;
+ struct timeval start_time;
+ void *msg_dgm_ref;
+ /*
+ * The number of instances waiting for incoming
+ * messages. By default it's always greater than 0.
+ *
+ * If it's 0 we'll discard incoming messages,
+ * see imessaging_init_discard_imcoming().
+ */
+ bool discard_incoming;
+ uint64_t num_incoming_listeners;
+};
+
+NTSTATUS imessaging_register_extra_handlers(struct imessaging_context *msg);
diff --git a/source4/lib/messaging/messaging_send.c b/source4/lib/messaging/messaging_send.c
new file mode 100644
index 0000000..24cdce3
--- /dev/null
+++ b/source4/lib/messaging/messaging_send.c
@@ -0,0 +1,115 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Samba internal messaging functions (send).
+
+ Copyright (C) Andrew Tridgell 2004
+
+ 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 "messaging/messaging.h"
+#include "messaging/irpc.h"
+#include "lib/messaging/messages_dgm.h"
+#include "lib/messaging/messages_dgm_ref.h"
+#include "../source3/lib/messages_util.h"
+#include "messaging/messaging_internal.h"
+#include "lib/util/server_id_db.h"
+#include "cluster/cluster.h"
+#include "../lib/util/unix_privs.h"
+
+/*
+ * This file is for functions that can be called from auth_log without
+ * depending on all of dcerpc and so cause dep loops.
+ */
+
+/*
+ return a list of server ids for a server name
+*/
+NTSTATUS irpc_servers_byname(struct imessaging_context *msg_ctx,
+ TALLOC_CTX *mem_ctx, const char *name,
+ unsigned *num_servers,
+ struct server_id **servers)
+{
+ int ret;
+
+ ret = server_id_db_lookup(msg_ctx->names, name, mem_ctx,
+ num_servers, servers);
+ if (ret != 0) {
+ return map_nt_error_from_unix_common(ret);
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ Send a message to a particular server
+*/
+NTSTATUS imessaging_send(struct imessaging_context *msg, struct server_id server,
+ uint32_t msg_type, const DATA_BLOB *data)
+{
+ uint8_t hdr[MESSAGE_HDR_LENGTH];
+ struct iovec iov[2];
+ int num_iov, ret;
+ pid_t pid;
+ void *priv;
+
+ if (!cluster_node_equal(&msg->server_id, &server)) {
+ /* No cluster in source4... */
+ return NT_STATUS_OK;
+ }
+
+ message_hdr_put(hdr, msg_type, msg->server_id, server);
+
+ iov[0] = (struct iovec) { .iov_base = &hdr, .iov_len = sizeof(hdr) };
+ num_iov = 1;
+
+ if (data != NULL) {
+ iov[1] = (struct iovec) { .iov_base = data->data,
+ .iov_len = data->length };
+ num_iov += 1;
+ }
+
+ pid = server.pid;
+ if (pid == 0) {
+ pid = getpid();
+ }
+
+ ret = messaging_dgm_send(pid, iov, num_iov, NULL, 0);
+
+ if (ret == EACCES) {
+ priv = root_privileges();
+ ret = messaging_dgm_send(pid, iov, num_iov, NULL, 0);
+ TALLOC_FREE(priv);
+ }
+
+ if (ret != 0) {
+ return map_nt_error_from_unix_common(ret);
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ Send a message to a particular server, with the message containing a single pointer
+*/
+NTSTATUS imessaging_send_ptr(struct imessaging_context *msg, struct server_id server,
+ uint32_t msg_type, void *ptr)
+{
+ DATA_BLOB blob;
+
+ blob.data = (uint8_t *)&ptr;
+ blob.length = sizeof(void *);
+
+ return imessaging_send(msg, server, msg_type, &blob);
+}
diff --git a/source4/lib/messaging/pymessaging.c b/source4/lib/messaging/pymessaging.c
new file mode 100644
index 0000000..148c941
--- /dev/null
+++ b/source4/lib/messaging/pymessaging.c
@@ -0,0 +1,551 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright © Jelmer Vernooij <jelmer@samba.org> 2008
+
+ Based on the equivalent for EJS:
+ Copyright © Andrew Tridgell <tridge@samba.org> 2005
+
+ 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 "python/py3compat.h"
+#include "includes.h"
+#include "python/modules.h"
+#include "libcli/util/pyerrors.h"
+#include "librpc/rpc/pyrpc_util.h"
+#include "librpc/ndr/libndr.h"
+#include "lib/messaging/messaging.h"
+#include "lib/messaging/irpc.h"
+#include "lib/events/events.h"
+#include "cluster/cluster.h"
+#include "param/param.h"
+#include "param/pyparam.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/gen_ndr/server_id.h"
+#include <pytalloc.h>
+#include "messaging_internal.h"
+
+
+extern PyTypeObject imessaging_Type;
+
+static bool server_id_from_py(PyObject *object, struct server_id *server_id)
+{
+ if (!PyTuple_Check(object)) {
+ if (!py_check_dcerpc_type(object, "samba.dcerpc.server_id", "server_id")) {
+
+ PyErr_SetString(PyExc_ValueError, "Expected tuple or server_id");
+ return false;
+ }
+ *server_id = *pytalloc_get_type(object, struct server_id);
+ return true;
+ }
+ if (PyTuple_Size(object) == 3) {
+ unsigned long long pid;
+ int task_id, vnn;
+
+ if (!PyArg_ParseTuple(object, "Kii", &pid, &task_id, &vnn)) {
+ return false;
+ }
+ server_id->pid = pid;
+ server_id->task_id = task_id;
+ server_id->vnn = vnn;
+ return true;
+ } else if (PyTuple_Size(object) == 2) {
+ unsigned long long pid;
+ int task_id;
+ if (!PyArg_ParseTuple(object, "Ki", &pid, &task_id))
+ return false;
+ *server_id = cluster_id(pid, task_id);
+ return true;
+ } else {
+ unsigned long long pid = getpid();
+ int task_id;
+ if (!PyArg_ParseTuple(object, "i", &task_id))
+ return false;
+ *server_id = cluster_id(pid, task_id);
+ return true;
+ }
+}
+
+typedef struct {
+ PyObject_HEAD
+ TALLOC_CTX *mem_ctx;
+ struct imessaging_context *msg_ctx;
+} imessaging_Object;
+
+static PyObject *py_imessaging_connect(PyTypeObject *self, PyObject *args, PyObject *kwargs)
+{
+ struct tevent_context *ev;
+ const char *kwnames[] = { "own_id", "lp_ctx", NULL };
+ PyObject *own_id = Py_None;
+ PyObject *py_lp_ctx = Py_None;
+ imessaging_Object *ret;
+ struct loadparm_context *lp_ctx;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OO:connect",
+ discard_const_p(char *, kwnames), &own_id, &py_lp_ctx)) {
+ return NULL;
+ }
+
+ ret = PyObject_New(imessaging_Object, &imessaging_Type);
+ if (ret == NULL)
+ return NULL;
+
+ ret->mem_ctx = talloc_new(NULL);
+
+ lp_ctx = lpcfg_from_py_object(ret->mem_ctx, py_lp_ctx);
+ if (lp_ctx == NULL) {
+ PyErr_SetString(PyExc_RuntimeError, "imessaging_connect unable to interpret loadparm_context");
+ talloc_free(ret->mem_ctx);
+ return NULL;
+ }
+
+ ev = s4_event_context_init(ret->mem_ctx);
+
+ if (own_id != Py_None) {
+ struct server_id server_id;
+
+ if (!server_id_from_py(own_id, &server_id))
+ return NULL;
+
+ ret->msg_ctx = imessaging_init(ret->mem_ctx,
+ lp_ctx,
+ server_id,
+ ev);
+ } else {
+ ret->msg_ctx = imessaging_client_init(ret->mem_ctx,
+ lp_ctx,
+ ev);
+ }
+
+ if (ret->msg_ctx == NULL) {
+ PyErr_SetString(PyExc_RuntimeError, "imessaging_connect unable to create a messaging context");
+ talloc_free(ret->mem_ctx);
+ return NULL;
+ }
+
+ return (PyObject *)ret;
+}
+
+static void py_imessaging_dealloc(PyObject *self)
+{
+ imessaging_Object *iface = (imessaging_Object *)self;
+ talloc_free(iface->msg_ctx);
+ self->ob_type->tp_free(self);
+}
+
+static PyObject *py_imessaging_send(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ imessaging_Object *iface = (imessaging_Object *)self;
+ uint32_t msg_type;
+ DATA_BLOB data;
+ PyObject *target;
+ NTSTATUS status;
+ struct server_id server;
+ const char *kwnames[] = { "target", "msg_type", "data", NULL };
+ Py_ssize_t length;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OIs#:send",
+ discard_const_p(char *, kwnames), &target, &msg_type, &data.data, &length)) {
+
+ return NULL;
+ }
+
+ data.length = length;
+
+ if (!server_id_from_py(target, &server))
+ return NULL;
+
+ status = imessaging_send(iface->msg_ctx, server, msg_type, &data);
+ if (NT_STATUS_IS_ERR(status)) {
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static void py_msg_callback_wrapper(struct imessaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ size_t num_fds,
+ int *fds,
+ DATA_BLOB *data)
+{
+ PyObject *py_server_id, *callback_and_tuple = (PyObject *)private_data;
+ PyObject *callback, *py_private;
+
+ struct server_id *p_server_id = talloc(NULL, struct server_id);
+
+ if (num_fds != 0) {
+ DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
+ return;
+ }
+
+ if (!p_server_id) {
+ PyErr_NoMemory();
+ return;
+ }
+ *p_server_id = server_id;
+
+ if (!PyArg_ParseTuple(callback_and_tuple, "OO",
+ &callback,
+ &py_private)) {
+ return;
+ }
+
+ py_server_id = py_return_ndr_struct("samba.dcerpc.server_id", "server_id", p_server_id, p_server_id);
+ talloc_unlink(NULL, p_server_id);
+
+ PyObject_CallFunction(callback, discard_const_p(char, "OiOs#"),
+ py_private,
+ msg_type,
+ py_server_id,
+ data->data, data->length);
+}
+
+static PyObject *py_imessaging_register(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ imessaging_Object *iface = (imessaging_Object *)self;
+ int msg_type = -1;
+ PyObject *callback_and_context;
+ NTSTATUS status;
+ const char *kwnames[] = { "callback_and_context", "msg_type", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|i:register",
+ discard_const_p(char *, kwnames),
+ &callback_and_context, &msg_type)) {
+ return NULL;
+ }
+ if (!PyTuple_Check(callback_and_context)
+ || PyTuple_Size(callback_and_context) != 2) {
+ PyErr_SetString(PyExc_ValueError, "Expected of size 2 for callback_and_context");
+ return NULL;
+ }
+
+ Py_INCREF(callback_and_context);
+
+ if (msg_type == -1) {
+ uint32_t msg_type32 = msg_type;
+ status = imessaging_register_tmp(iface->msg_ctx, callback_and_context,
+ py_msg_callback_wrapper, &msg_type32);
+ msg_type = msg_type32;
+ } else {
+ status = imessaging_register(iface->msg_ctx, callback_and_context,
+ msg_type, py_msg_callback_wrapper);
+ }
+ if (NT_STATUS_IS_ERR(status)) {
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+
+ return PyLong_FromLong(msg_type);
+}
+
+static PyObject *py_imessaging_deregister(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ imessaging_Object *iface = (imessaging_Object *)self;
+ int msg_type = -1;
+ PyObject *callback;
+ const char *kwnames[] = { "callback", "msg_type", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|i:deregister",
+ discard_const_p(char *, kwnames), &callback, &msg_type)) {
+ return NULL;
+ }
+
+ imessaging_deregister(iface->msg_ctx, msg_type, callback);
+
+ Py_RETURN_NONE;
+}
+
+static void simple_timer_handler(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval current_time,
+ void *private_data)
+{
+ return;
+}
+
+static PyObject *py_imessaging_loop_once(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ imessaging_Object *iface = (imessaging_Object *)self;
+ double offset;
+ int seconds;
+ struct timeval next_event;
+ struct tevent_timer *timer = NULL;
+ const char *kwnames[] = { "timeout", NULL };
+
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "d",
+ discard_const_p(char *, kwnames), &offset)) {
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ if (offset != 0.0) {
+ seconds = offset;
+ offset -= seconds;
+ next_event = tevent_timeval_current_ofs(seconds, (int)(offset*1000000));
+
+ timer = tevent_add_timer(iface->msg_ctx->ev, frame, next_event, simple_timer_handler,
+ NULL);
+ if (timer == NULL) {
+ PyErr_NoMemory();
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+ }
+
+ tevent_loop_once(iface->msg_ctx->ev);
+
+ TALLOC_FREE(frame);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_irpc_add_name(PyObject *self, PyObject *args)
+{
+ imessaging_Object *iface = (imessaging_Object *)self;
+ char *server_name;
+ NTSTATUS status;
+
+ if (!PyArg_ParseTuple(args, "s", &server_name)) {
+ return NULL;
+ }
+
+ status = irpc_add_name(iface->msg_ctx, server_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_irpc_remove_name(PyObject *self, PyObject *args)
+{
+ imessaging_Object *iface = (imessaging_Object *)self;
+ char *server_name;
+
+ if (!PyArg_ParseTuple(args, "s", &server_name)) {
+ return NULL;
+ }
+
+ irpc_remove_name(iface->msg_ctx, server_name);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_irpc_servers_byname(PyObject *self, PyObject *args)
+{
+ imessaging_Object *iface = (imessaging_Object *)self;
+ char *server_name;
+ unsigned i, num_ids;
+ struct server_id *ids;
+ PyObject *pylist;
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ NTSTATUS status;
+
+ if (!mem_ctx) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "s", &server_name)) {
+ TALLOC_FREE(mem_ctx);
+ return NULL;
+ }
+
+ status = irpc_servers_byname(iface->msg_ctx, mem_ctx, server_name,
+ &num_ids, &ids);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(mem_ctx);
+ PyErr_SetString(PyExc_KeyError, "No such name");
+ return NULL;
+ }
+
+ pylist = PyList_New(num_ids);
+ if (pylist == NULL) {
+ TALLOC_FREE(mem_ctx);
+ PyErr_NoMemory();
+ return NULL;
+ }
+ for (i = 0; i < num_ids; i++) {
+ PyObject *py_server_id;
+ struct server_id *p_server_id = talloc(NULL, struct server_id);
+ if (!p_server_id) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ *p_server_id = ids[i];
+
+ py_server_id = py_return_ndr_struct("samba.dcerpc.server_id", "server_id", p_server_id, p_server_id);
+ if (!py_server_id) {
+ return NULL;
+ }
+ PyList_SetItem(pylist, i, py_server_id);
+ talloc_unlink(NULL, p_server_id);
+ }
+ TALLOC_FREE(mem_ctx);
+ return pylist;
+}
+
+static PyObject *py_irpc_all_servers(PyObject *self,
+ PyObject *Py_UNUSED(ignored))
+{
+ imessaging_Object *iface = (imessaging_Object *)self;
+ PyObject *pylist;
+ int i;
+ struct irpc_name_records *records;
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ if (!mem_ctx) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ records = irpc_all_servers(iface->msg_ctx, mem_ctx);
+ if (records == NULL) {
+ return NULL;
+ }
+
+ pylist = PyList_New(records->num_records);
+ if (pylist == NULL) {
+ TALLOC_FREE(mem_ctx);
+ PyErr_NoMemory();
+ return NULL;
+ }
+ for (i = 0; i < records->num_records; i++) {
+ PyObject *py_name_record
+ = py_return_ndr_struct("samba.dcerpc.irpc",
+ "name_record",
+ records->names[i],
+ records->names[i]);
+ if (!py_name_record) {
+ return NULL;
+ }
+ PyList_SetItem(pylist, i,
+ py_name_record);
+ }
+ TALLOC_FREE(mem_ctx);
+ return pylist;
+}
+
+static PyMethodDef py_imessaging_methods[] = {
+ { "send", PY_DISCARD_FUNC_SIG(PyCFunction, py_imessaging_send),
+ METH_VARARGS|METH_KEYWORDS,
+ "S.send(target, msg_type, data) -> None\nSend a message" },
+ { "register", PY_DISCARD_FUNC_SIG(PyCFunction, py_imessaging_register),
+ METH_VARARGS|METH_KEYWORDS,
+ "S.register((callback, context), msg_type=None) -> msg_type\nRegister a message handler. "
+ "The callback and context must be supplied as a two-element tuple." },
+ { "deregister", PY_DISCARD_FUNC_SIG(PyCFunction,
+ py_imessaging_deregister),
+ METH_VARARGS|METH_KEYWORDS,
+ "S.deregister((callback, context), msg_type) -> None\nDeregister a message handler "
+ "The callback and context must be supplied as the exact same two-element tuple "
+ "as was used as registration time." },
+ { "loop_once", PY_DISCARD_FUNC_SIG(PyCFunction,
+ py_imessaging_loop_once),
+ METH_VARARGS|METH_KEYWORDS,
+ "S.loop_once(timeout) -> None\n"
+ "Loop on the internal event context until we get an event "
+ "(which might be a message calling the callback), "
+ "timeout after timeout seconds (if not 0)" },
+ { "irpc_add_name", (PyCFunction)py_irpc_add_name, METH_VARARGS,
+ "S.irpc_add_name(name) -> None\n"
+ "Add this context to the list of server_id values that "
+ "are registered for a particular name" },
+ { "irpc_remove_name", (PyCFunction)py_irpc_remove_name, METH_VARARGS,
+ "S.irpc_remove_name(name) -> None\n"
+ "Remove this context from the list of server_id values that "
+ "are registered for a particular name" },
+ { "irpc_servers_byname", (PyCFunction)py_irpc_servers_byname, METH_VARARGS,
+ "S.irpc_servers_byname(name) -> list\nGet list of server_id values that are registered for a particular name" },
+ { "irpc_all_servers", (PyCFunction)py_irpc_all_servers, METH_NOARGS,
+ "S.irpc_all_servers() -> list\n"
+ "Get list of all registered names and the associated server_id values" },
+ { NULL, NULL, 0, NULL }
+};
+
+static PyObject *py_imessaging_server_id(PyObject *obj, void *closure)
+{
+ imessaging_Object *iface = (imessaging_Object *)obj;
+ PyObject *py_server_id;
+ struct server_id server_id = imessaging_get_server_id(iface->msg_ctx);
+ struct server_id *p_server_id = talloc(NULL, struct server_id);
+ if (!p_server_id) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ *p_server_id = server_id;
+
+ py_server_id = py_return_ndr_struct("samba.dcerpc.server_id", "server_id", p_server_id, p_server_id);
+ talloc_unlink(NULL, p_server_id);
+
+ return py_server_id;
+}
+
+static PyGetSetDef py_imessaging_getset[] = {
+ {
+ .name = discard_const_p(char, "server_id"),
+ .get = py_imessaging_server_id,
+ .doc = discard_const_p(char, "local server id")
+ },
+ { .name = NULL },
+};
+
+
+PyTypeObject imessaging_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "messaging.Messaging",
+ .tp_basicsize = sizeof(imessaging_Object),
+ .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
+ .tp_new = py_imessaging_connect,
+ .tp_dealloc = py_imessaging_dealloc,
+ .tp_methods = py_imessaging_methods,
+ .tp_getset = py_imessaging_getset,
+ .tp_doc = "Messaging(own_id=None)\n" \
+ "Create a new object that can be used to communicate with the peers in the specified messaging path.\n"
+};
+
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "messaging",
+ .m_doc = "Internal RPC",
+ .m_size = -1,
+ .m_methods = NULL,
+};
+
+MODULE_INIT_FUNC(messaging)
+{
+ PyObject *mod;
+
+ if (PyType_Ready(&imessaging_Type) < 0)
+ return NULL;
+
+ mod = PyModule_Create(&moduledef);
+ if (mod == NULL)
+ return NULL;
+
+ Py_INCREF((PyObject *)&imessaging_Type);
+ PyModule_AddObject(mod, "Messaging", (PyObject *)&imessaging_Type);
+ PyModule_AddObject(mod, "IRPC_CALL_TIMEOUT", PyLong_FromLong(IRPC_CALL_TIMEOUT));
+ PyModule_AddObject(mod, "IRPC_CALL_TIMEOUT_INF", PyLong_FromLong(IRPC_CALL_TIMEOUT_INF));
+
+ return mod;
+}
diff --git a/source4/lib/messaging/tests/irpc.c b/source4/lib/messaging/tests/irpc.c
new file mode 100644
index 0000000..466b47f
--- /dev/null
+++ b/source4/lib/messaging/tests/irpc.c
@@ -0,0 +1,308 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local test for irpc code
+
+ Copyright (C) Andrew Tridgell 2004
+
+ 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 "lib/events/events.h"
+#include "lib/messaging/irpc.h"
+#include "librpc/gen_ndr/ndr_echo.h"
+#include "librpc/gen_ndr/ndr_echo_c.h"
+#include "torture/torture.h"
+#include "cluster/cluster.h"
+#include "param/param.h"
+#include "torture/local/proto.h"
+
+const uint32_t MSG_ID1 = 1, MSG_ID2 = 2;
+
+static bool test_debug;
+
+struct irpc_test_data
+{
+ struct imessaging_context *msg_ctx1, *msg_ctx2;
+ struct tevent_context *ev;
+};
+
+/*
+ serve up AddOne over the irpc system
+*/
+static NTSTATUS irpc_AddOne(struct irpc_message *irpc, struct echo_AddOne *r)
+{
+ *r->out.out_data = r->in.in_data + 1;
+ if (test_debug) {
+ printf("irpc_AddOne: in=%u in+1=%u out=%u\n",
+ r->in.in_data, r->in.in_data+1, *r->out.out_data);
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ a deferred reply to echodata
+*/
+static void deferred_echodata(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct irpc_message *irpc = talloc_get_type(private_data, struct irpc_message);
+ struct echo_EchoData *r = (struct echo_EchoData *)irpc->data;
+ r->out.out_data = (uint8_t *)talloc_memdup(r, r->in.in_data, r->in.len);
+ if (r->out.out_data == NULL) {
+ irpc_send_reply(irpc, NT_STATUS_NO_MEMORY);
+ }
+ printf("sending deferred reply\n");
+ irpc_send_reply(irpc, NT_STATUS_OK);
+}
+
+
+/*
+ serve up EchoData over the irpc system
+*/
+static NTSTATUS irpc_EchoData(struct irpc_message *irpc, struct echo_EchoData *r)
+{
+ struct irpc_test_data *data = talloc_get_type_abort(irpc->private_data, struct irpc_test_data);
+ irpc->defer_reply = true;
+ tevent_add_timer(data->ev, irpc, timeval_zero(), deferred_echodata, irpc);
+ return NT_STATUS_OK;
+}
+
+
+/*
+ test a addone call over the internal messaging system
+*/
+static bool test_addone(struct torture_context *test, const void *_data,
+ const void *_value)
+{
+ struct echo_AddOne r;
+ NTSTATUS status;
+ const struct irpc_test_data *data = (const struct irpc_test_data *)_data;
+ uint32_t value = *(const uint32_t *)_value;
+ struct dcerpc_binding_handle *irpc_handle;
+
+ irpc_handle = irpc_binding_handle(test, data->msg_ctx1,
+ cluster_id(0, MSG_ID2),
+ &ndr_table_rpcecho);
+ torture_assert(test, irpc_handle, "no memory");
+
+ /* make the call */
+ r.in.in_data = value;
+
+ test_debug = true;
+ /*
+ * Note: this makes use of nested event loops
+ * as client and server use the same loop.
+ */
+ dcerpc_binding_handle_set_sync_ev(irpc_handle, data->ev);
+ status = dcerpc_echo_AddOne_r(irpc_handle, test, &r);
+ test_debug = false;
+ torture_assert_ntstatus_ok(test, status, "AddOne failed");
+
+ /* check the answer */
+ torture_assert(test, *r.out.out_data == r.in.in_data + 1,
+ "AddOne wrong answer");
+
+ torture_comment(test, "%u + 1 = %u\n", r.in.in_data, *r.out.out_data);
+ return true;
+}
+
+/*
+ test a echodata call over the internal messaging system
+*/
+static bool test_echodata(struct torture_context *tctx,
+ const void *tcase_data,
+ const void *test_data)
+{
+ struct echo_EchoData r;
+ NTSTATUS status;
+ const struct irpc_test_data *data = (const struct irpc_test_data *)tcase_data;
+ TALLOC_CTX *mem_ctx = tctx;
+ struct dcerpc_binding_handle *irpc_handle;
+
+ irpc_handle = irpc_binding_handle(mem_ctx, data->msg_ctx1,
+ cluster_id(0, MSG_ID2),
+ &ndr_table_rpcecho);
+ torture_assert(tctx, irpc_handle, "no memory");
+
+ /* make the call */
+ r.in.in_data = (unsigned char *)talloc_strdup(mem_ctx, "0123456789");
+ r.in.len = strlen((char *)r.in.in_data);
+
+ /*
+ * Note: this makes use of nested event loops
+ * as client and server use the same loop.
+ */
+ dcerpc_binding_handle_set_sync_ev(irpc_handle, data->ev);
+ status = dcerpc_echo_EchoData_r(irpc_handle, mem_ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "EchoData failed");
+
+ /* check the answer */
+ if (memcmp(r.out.out_data, r.in.in_data, r.in.len) != 0) {
+ NDR_PRINT_OUT_DEBUG(echo_EchoData, &r);
+ torture_fail(tctx, "EchoData wrong answer");
+ }
+
+ torture_comment(tctx, "Echo '%*.*s' -> '%*.*s'\n",
+ r.in.len, r.in.len,
+ r.in.in_data,
+ r.in.len, r.in.len,
+ r.out.out_data);
+ return true;
+}
+
+struct irpc_callback_state {
+ struct echo_AddOne r;
+ int *pong_count;
+};
+
+static void irpc_callback(struct tevent_req *subreq)
+{
+ struct irpc_callback_state *s =
+ tevent_req_callback_data(subreq,
+ struct irpc_callback_state);
+ NTSTATUS status;
+
+ status = dcerpc_echo_AddOne_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("irpc call failed - %s\n", nt_errstr(status));
+ }
+ if (*s->r.out.out_data != s->r.in.in_data + 1) {
+ printf("AddOne wrong answer - %u + 1 = %u should be %u\n",
+ s->r.in.in_data, *s->r.out.out_data, s->r.in.in_data+1);
+ }
+ (*s->pong_count)++;
+}
+
+/*
+ test echo speed
+*/
+static bool test_speed(struct torture_context *tctx,
+ const void *tcase_data,
+ const void *test_data)
+{
+ int ping_count = 0;
+ int pong_count = 0;
+ const struct irpc_test_data *data = (const struct irpc_test_data *)tcase_data;
+ struct timeval tv;
+ TALLOC_CTX *mem_ctx = tctx;
+ int timelimit = torture_setting_int(tctx, "timelimit", 10);
+ struct dcerpc_binding_handle *irpc_handle;
+
+ irpc_handle = irpc_binding_handle(mem_ctx, data->msg_ctx1,
+ cluster_id(0, MSG_ID2),
+ &ndr_table_rpcecho);
+ torture_assert(tctx, irpc_handle, "no memory");
+
+ tv = timeval_current();
+
+ torture_comment(tctx, "Sending echo for %d seconds\n", timelimit);
+ while (timeval_elapsed(&tv) < timelimit) {
+ struct tevent_req *subreq;
+ struct irpc_callback_state *s;
+
+ s = talloc_zero(mem_ctx, struct irpc_callback_state);
+ torture_assert(tctx, s != NULL, "no mem");
+
+ s->pong_count = &pong_count;
+
+ subreq = dcerpc_echo_AddOne_r_send(mem_ctx,
+ tctx->ev,
+ irpc_handle,
+ &s->r);
+ torture_assert(tctx, subreq != NULL, "AddOne send failed");
+
+ tevent_req_set_callback(subreq, irpc_callback, s);
+
+ ping_count++;
+
+ while (ping_count > pong_count + 20) {
+ tevent_loop_once(data->ev);
+ }
+ }
+
+ torture_comment(tctx, "waiting for %d remaining replies (done %d)\n",
+ ping_count - pong_count, pong_count);
+ while (timeval_elapsed(&tv) < 30 && pong_count < ping_count) {
+ tevent_loop_once(data->ev);
+ }
+
+ torture_assert_int_equal(tctx, ping_count, pong_count, "ping test failed");
+
+ torture_comment(tctx, "echo rate of %.0f messages/sec\n",
+ (ping_count+pong_count)/timeval_elapsed(&tv));
+ return true;
+}
+
+
+static bool irpc_setup(struct torture_context *tctx, void **_data)
+{
+ struct irpc_test_data *data;
+
+ *_data = data = talloc(tctx, struct irpc_test_data);
+
+ lpcfg_set_cmdline(tctx->lp_ctx, "pid directory", "piddir.tmp");
+
+ data->ev = tctx->ev;
+ torture_assert(tctx, data->msg_ctx1 =
+ imessaging_init(tctx,
+ tctx->lp_ctx,
+ cluster_id(0, MSG_ID1),
+ data->ev),
+ "Failed to init first messaging context");
+
+ torture_assert(tctx, data->msg_ctx2 =
+ imessaging_init(tctx,
+ tctx->lp_ctx,
+ cluster_id(0, MSG_ID2),
+ data->ev),
+ "Failed to init second messaging context");
+
+ /* register the server side function */
+ IRPC_REGISTER(data->msg_ctx1, rpcecho, ECHO_ADDONE, irpc_AddOne, data);
+ IRPC_REGISTER(data->msg_ctx2, rpcecho, ECHO_ADDONE, irpc_AddOne, data);
+
+ IRPC_REGISTER(data->msg_ctx1, rpcecho, ECHO_ECHODATA, irpc_EchoData, data);
+ IRPC_REGISTER(data->msg_ctx2, rpcecho, ECHO_ECHODATA, irpc_EchoData, data);
+
+ return true;
+}
+
+struct torture_suite *torture_local_irpc(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "irpc");
+ struct torture_tcase *tcase = torture_suite_add_tcase(suite, "irpc");
+ int i;
+ uint32_t *values = talloc_array(tcase, uint32_t, 5);
+
+ values[0] = 0;
+ values[1] = 0x7FFFFFFE;
+ values[2] = 0xFFFFFFFE;
+ values[3] = 0xFFFFFFFF;
+ values[4] = random() & 0xFFFFFFFF;
+
+ tcase->setup = irpc_setup;
+
+ for (i = 0; i < 5; i++) {
+ torture_tcase_add_test_const(tcase, "addone", test_addone,
+ (void *)&values[i]);
+ }
+
+ torture_tcase_add_test_const(tcase, "echodata", test_echodata, NULL);
+ torture_tcase_add_test_const(tcase, "speed", test_speed, NULL);
+
+ return suite;
+}
diff --git a/source4/lib/messaging/tests/messaging.c b/source4/lib/messaging/tests/messaging.c
new file mode 100644
index 0000000..dcbbc19
--- /dev/null
+++ b/source4/lib/messaging/tests/messaging.c
@@ -0,0 +1,694 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local test for messaging code
+
+ Copyright (C) Andrew Tridgell 2004
+
+ 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 "lib/events/events.h"
+#include "lib/messaging/irpc.h"
+#include "torture/torture.h"
+#include "cluster/cluster.h"
+#include "param/param.h"
+#include "torture/local/proto.h"
+#include "system/select.h"
+#include "system/filesys.h"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+static uint32_t msg_pong;
+
+static void ping_message(struct imessaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id src,
+ size_t num_fds,
+ int *fds,
+ DATA_BLOB *data)
+{
+ NTSTATUS status;
+
+ if (num_fds != 0) {
+ DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
+ return;
+ }
+
+ status = imessaging_send(msg, src, msg_pong, data);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("pong failed - %s\n", nt_errstr(status));
+ }
+}
+
+static void pong_message(struct imessaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id src,
+ size_t num_fds,
+ int *fds,
+ DATA_BLOB *data)
+{
+ int *count = (int *)private_data;
+
+ if (num_fds != 0) {
+ DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
+ return;
+ }
+
+ (*count)++;
+}
+
+static void exit_message(struct imessaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id src,
+ size_t num_fds,
+ int *fds,
+ DATA_BLOB *data)
+{
+ if (num_fds != 0) {
+ DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
+ return;
+ }
+
+ talloc_free(private_data);
+ exit(0);
+}
+
+/*
+ test ping speed
+*/
+static bool test_ping_speed(struct torture_context *tctx)
+{
+ struct tevent_context *ev;
+ struct imessaging_context *msg_client_ctx;
+ struct imessaging_context *msg_server_ctx;
+ int ping_count = 0;
+ int pong_count = 0;
+ struct timeval tv;
+ int timelimit = torture_setting_int(tctx, "timelimit", 10);
+ uint32_t msg_ping, msg_exit;
+
+ lpcfg_set_cmdline(tctx->lp_ctx, "pid directory", "piddir.tmp");
+
+ ev = tctx->ev;
+
+ msg_server_ctx = imessaging_init(tctx,
+ tctx->lp_ctx, cluster_id(0, 1),
+ ev);
+
+ torture_assert(tctx, msg_server_ctx != NULL, "Failed to init ping messaging context");
+
+ imessaging_register_tmp(msg_server_ctx, NULL, ping_message, &msg_ping);
+ imessaging_register_tmp(msg_server_ctx, tctx, exit_message, &msg_exit);
+
+ msg_client_ctx = imessaging_init(tctx,
+ tctx->lp_ctx,
+ cluster_id(0, 2),
+ ev);
+
+ torture_assert(tctx, msg_client_ctx != NULL,
+ "msg_client_ctx imessaging_init() failed");
+
+ imessaging_register_tmp(msg_client_ctx, &pong_count, pong_message, &msg_pong);
+
+ tv = timeval_current();
+
+ torture_comment(tctx, "Sending pings for %d seconds\n", timelimit);
+ while (timeval_elapsed(&tv) < timelimit) {
+ DATA_BLOB data;
+ NTSTATUS status1, status2;
+
+ data.data = discard_const_p(uint8_t, "testing");
+ data.length = strlen((const char *)data.data);
+
+ status1 = imessaging_send(msg_client_ctx, cluster_id(0, 1), msg_ping, &data);
+ status2 = imessaging_send(msg_client_ctx, cluster_id(0, 1), msg_ping, NULL);
+
+ torture_assert_ntstatus_ok(tctx, status1, "msg1 failed");
+ ping_count++;
+
+ torture_assert_ntstatus_ok(tctx, status2, "msg2 failed");
+ ping_count++;
+
+ while (ping_count > pong_count + 20) {
+ tevent_loop_once(ev);
+ }
+ }
+
+ torture_comment(tctx, "waiting for %d remaining replies (done %d)\n",
+ ping_count - pong_count, pong_count);
+ while (timeval_elapsed(&tv) < 30 && pong_count < ping_count) {
+ tevent_loop_once(ev);
+ }
+
+ torture_comment(tctx, "sending exit\n");
+ imessaging_send(msg_client_ctx, cluster_id(0, 1), msg_exit, NULL);
+
+ torture_assert_int_equal(tctx, ping_count, pong_count, "ping test failed");
+
+ torture_comment(tctx, "ping rate of %.0f messages/sec\n",
+ (ping_count+pong_count)/timeval_elapsed(&tv));
+
+ talloc_free(msg_client_ctx);
+ talloc_free(msg_server_ctx);
+
+ return true;
+}
+
+static bool test_messaging_overflow(struct torture_context *tctx)
+{
+ struct imessaging_context *msg_ctx;
+ ssize_t nwritten, nread;
+ pid_t child;
+ char c = 0;
+ int up_pipe[2], down_pipe[2];
+ int i, ret, child_status;
+
+ ret = pipe(up_pipe);
+ torture_assert(tctx, ret == 0, "pipe failed");
+ ret = pipe(down_pipe);
+ torture_assert(tctx, ret == 0, "pipe failed");
+
+ child = fork();
+ if (child < 0) {
+ torture_fail(tctx, "fork failed");
+ }
+
+ if (child == 0) {
+ ret = tevent_re_initialise(tctx->ev);
+ torture_assert(tctx, ret == 0, "tevent_re_initialise failed");
+
+ msg_ctx = imessaging_init(tctx, tctx->lp_ctx,
+ cluster_id(getpid(), 0),
+ tctx->ev);
+ torture_assert(tctx, msg_ctx != NULL,
+ "imessaging_init failed");
+
+ do {
+ nwritten = write(up_pipe[1], &c, 1);
+ } while ((nwritten == -1) && (errno == EINTR));
+
+ ret = close(down_pipe[1]);
+ torture_assert(tctx, ret == 0, "close failed");
+
+ do {
+ nread = read(down_pipe[0], &c, 1);
+ } while ((nread == -1) && (errno == EINTR));
+
+ exit(0);
+ }
+
+ do {
+ nread = read(up_pipe[0], &c, 1);
+ } while ((nread == -1) && (errno == EINTR));
+
+ msg_ctx = imessaging_init(tctx, tctx->lp_ctx, cluster_id(getpid(), 0),
+ tctx->ev);
+ torture_assert(tctx, msg_ctx != NULL, "imessaging_init failed");
+
+ for (i=0; i<1000; i++) {
+ NTSTATUS status;
+ status = imessaging_send(msg_ctx, cluster_id(child, 0),
+ MSG_PING, NULL);
+ torture_assert_ntstatus_ok(tctx, status,
+ "imessaging_send failed");
+ }
+
+ tevent_loop_once(tctx->ev);
+
+ talloc_free(msg_ctx);
+
+ ret = close(down_pipe[1]);
+ torture_assert(tctx, ret == 0, "close failed");
+
+ ret = waitpid(child, &child_status, 0);
+ torture_assert(tctx, ret == child, "wrong child exited");
+ torture_assert(tctx, child_status == 0, "child failed");
+
+ poll(NULL, 0, 500);
+
+ return true;
+}
+
+struct overflow_parent_child {
+ gnutls_hash_hd_t md5_hash_hnd;
+ bool done;
+};
+
+static void overflow_md5_child_handler(struct imessaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ size_t num_fds,
+ int *fds,
+ DATA_BLOB *data)
+{
+ struct overflow_parent_child *state = private_data;
+
+ if (num_fds != 0) {
+ DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
+ return;
+ }
+
+ if (data->length == 0) {
+ state->done = true;
+ return;
+ }
+
+ gnutls_hash(state->md5_hash_hnd, data->data, data->length);
+}
+
+struct overflow_child_parent {
+ uint8_t final[16];
+ bool done;
+};
+
+static void overflow_md5_parent_handler(struct imessaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ size_t num_fds,
+ int *fds,
+ DATA_BLOB *data)
+{
+ struct overflow_child_parent *state = private_data;
+
+ if (num_fds != 0) {
+ DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
+ return;
+ }
+
+ if (data->length != sizeof(state->final)) {
+ memset(state->final, 0, sizeof(state->final));
+ state->done = true;
+ return;
+ }
+ memcpy(state->final, data->data, 16);
+ state->done = true;
+}
+
+static bool test_messaging_overflow_check(struct torture_context *tctx)
+{
+ struct imessaging_context *msg_ctx;
+ ssize_t nwritten, nread;
+ pid_t child;
+ char c = 0;
+ int up_pipe[2], down_pipe[2];
+ int i, ret, child_status;
+ gnutls_hash_hd_t hash_hnd;
+ uint8_t final[16];
+ struct overflow_child_parent child_msg = { .done = false };
+ NTSTATUS status;
+
+ ret = pipe(up_pipe);
+ torture_assert(tctx, ret == 0, "pipe failed");
+ ret = pipe(down_pipe);
+ torture_assert(tctx, ret == 0, "pipe failed");
+
+ child = fork();
+ if (child < 0) {
+ torture_fail(tctx, "fork failed");
+ }
+
+ if (child == 0) {
+ struct overflow_parent_child child_state = { .done = false };
+ DATA_BLOB retblob = { .data = final, .length = sizeof(final) };
+
+ ret = tevent_re_initialise(tctx->ev);
+ torture_assert(tctx, ret == 0, "tevent_re_initialise failed");
+
+ gnutls_hash_init(&child_state.md5_hash_hnd, GNUTLS_DIG_MD5);
+
+ msg_ctx = imessaging_init(tctx, tctx->lp_ctx,
+ cluster_id(getpid(), 0),
+ tctx->ev);
+ torture_assert(tctx, msg_ctx != NULL,
+ "imessaging_init failed");
+
+ status = imessaging_register(msg_ctx, &child_state,
+ MSG_TMP_BASE-1,
+ overflow_md5_child_handler);
+ torture_assert(tctx, NT_STATUS_IS_OK(status),
+ "imessaging_register failed");
+
+ do {
+ nwritten = write(up_pipe[1], &c, 1);
+ } while ((nwritten == -1) && (errno == EINTR));
+
+ ret = close(down_pipe[1]);
+ torture_assert(tctx, ret == 0, "close failed");
+
+ do {
+ nread = read(down_pipe[0], &c, 1);
+ } while ((nread == -1) && (errno == EINTR));
+
+ while (!child_state.done) {
+ tevent_loop_once(tctx->ev);
+ }
+
+ gnutls_hash_deinit(child_state.md5_hash_hnd, final);
+
+ status = imessaging_send(msg_ctx,
+ cluster_id(getppid(), 0),
+ MSG_TMP_BASE-2,
+ &retblob);
+ torture_assert(tctx, NT_STATUS_IS_OK(status),
+ "imessaging_send failed");
+
+ exit(0);
+ }
+
+ do {
+ nread = read(up_pipe[0], &c, 1);
+ } while ((nread == -1) && (errno == EINTR));
+
+ msg_ctx = imessaging_init(tctx, tctx->lp_ctx, cluster_id(getpid(), 0),
+ tctx->ev);
+ torture_assert(tctx, msg_ctx != NULL, "imessaging_init failed");
+
+ status = imessaging_register(msg_ctx,
+ &child_msg,
+ MSG_TMP_BASE-2,
+ overflow_md5_parent_handler);
+ torture_assert(tctx,
+ NT_STATUS_IS_OK(status),
+ "imessaging_register failed");
+
+ gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
+
+ for (i=0; i<1000; i++) {
+ size_t len = ((random() % 100) + 1);
+ uint8_t buf[len];
+ DATA_BLOB blob = { .data = buf, .length = len };
+
+ generate_random_buffer(buf, len);
+
+ gnutls_hash(hash_hnd, buf, len);
+
+ status = imessaging_send(msg_ctx, cluster_id(child, 0),
+ MSG_TMP_BASE-1, &blob);
+ torture_assert_ntstatus_ok(tctx, status,
+ "imessaging_send failed");
+ }
+
+ status = imessaging_send(msg_ctx, cluster_id(child, 0),
+ MSG_TMP_BASE-1, NULL);
+ torture_assert_ntstatus_ok(tctx, status,
+ "imessaging_send failed");
+
+ gnutls_hash_deinit(hash_hnd, final);
+
+ do {
+ nwritten = write(down_pipe[1], &c, 1);
+ } while ((nwritten == -1) && (errno == EINTR));
+
+ while (!child_msg.done) {
+ tevent_loop_once(tctx->ev);
+ }
+
+ ret = close(down_pipe[1]);
+ torture_assert(tctx, ret == 0, "close failed");
+
+ talloc_free(msg_ctx);
+
+ ret = waitpid(child, &child_status, 0);
+ torture_assert(tctx, ret == child, "wrong child exited");
+ torture_assert(tctx, child_status == 0, "child failed");
+
+ if (memcmp(final, child_msg.final, 16) != 0) {
+ dump_data_file(final, 16, false, stderr);
+ dump_data_file(child_msg.final, 16, false, stderr);
+ fflush(stderr);
+ torture_fail(tctx, "checksum comparison failed");
+ }
+
+ return true;
+}
+
+struct test_multi_ctx {
+ struct torture_context *tctx;
+ struct imessaging_context *server_ctx;
+ struct imessaging_context *client_ctx[4];
+ size_t num_missing;
+ bool got_server;
+ bool got_client_0_1;
+ bool got_client_2_3;
+ bool ok;
+};
+
+static void multi_ctx_server_handler(struct imessaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ size_t num_fds,
+ int *fds,
+ DATA_BLOB *data)
+{
+ struct test_multi_ctx *state = private_data;
+ char *str = NULL;
+
+ if (num_fds != 0) {
+ DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
+ return;
+ }
+
+ torture_assert_goto(state->tctx, state->num_missing >= 1,
+ state->ok, fail,
+ "num_missing should be at least 1.");
+ state->num_missing -= 1;
+
+ torture_assert_goto(state->tctx, !state->got_server,
+ state->ok, fail,
+ "already got server.");
+ state->got_server = true;
+
+ /*
+ * We free the context itself and most likely reuse
+ * the memory immediately.
+ */
+ TALLOC_FREE(state->server_ctx);
+ str = generate_random_str(state->tctx, 128);
+ torture_assert_goto(state->tctx, str != NULL,
+ state->ok, fail,
+ "generate_random_str()");
+
+fail:
+ return;
+}
+
+static void multi_ctx_client_0_1_handler(struct imessaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ size_t num_fds,
+ int *fds,
+ DATA_BLOB *data)
+{
+ struct test_multi_ctx *state = private_data;
+ char *str = NULL;
+
+ if (num_fds != 0) {
+ DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
+ return;
+ }
+
+ torture_assert_goto(state->tctx, state->num_missing >= 2,
+ state->ok, fail,
+ "num_missing should be at least 2.");
+ state->num_missing -= 2;
+
+ torture_assert_goto(state->tctx, !state->got_client_0_1,
+ state->ok, fail,
+ "already got client_0_1.");
+ state->got_client_0_1 = true;
+
+ /*
+ * We free two contexts and most likely reuse
+ * the memory immediately.
+ */
+ TALLOC_FREE(state->client_ctx[0]);
+ str = generate_random_str(state->tctx, 128);
+ torture_assert_goto(state->tctx, str != NULL,
+ state->ok, fail,
+ "generate_random_str()");
+ TALLOC_FREE(state->client_ctx[1]);
+ str = generate_random_str(state->tctx, 128);
+ torture_assert_goto(state->tctx, str != NULL,
+ state->ok, fail,
+ "generate_random_str()");
+
+fail:
+ return;
+}
+
+static void multi_ctx_client_2_3_handler(struct imessaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ size_t num_fds,
+ int *fds,
+ DATA_BLOB *data)
+{
+ struct test_multi_ctx *state = private_data;
+ char *str = NULL;
+
+ if (num_fds != 0) {
+ DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
+ return;
+ }
+
+ torture_assert_goto(state->tctx, state->num_missing >= 2,
+ state->ok, fail,
+ "num_missing should be at least 2.");
+ state->num_missing -= 2;
+
+ torture_assert_goto(state->tctx, !state->got_client_2_3,
+ state->ok, fail,
+ "already got client_2_3.");
+ state->got_client_2_3 = true;
+
+ /*
+ * We free two contexts and most likely reuse
+ * the memory immediately.
+ */
+ TALLOC_FREE(state->client_ctx[2]);
+ str = generate_random_str(state->tctx, 128);
+ torture_assert_goto(state->tctx, str != NULL,
+ state->ok, fail,
+ "generate_random_str()");
+ TALLOC_FREE(state->client_ctx[3]);
+ str = generate_random_str(state->tctx, 128);
+ torture_assert_goto(state->tctx, str != NULL,
+ state->ok, fail,
+ "generate_random_str()");
+
+fail:
+ return;
+}
+
+static bool test_multi_ctx(struct torture_context *tctx)
+{
+ struct test_multi_ctx state = {
+ .tctx = tctx,
+ .ok = true,
+ };
+ struct timeval tv;
+ NTSTATUS status;
+
+ lpcfg_set_cmdline(tctx->lp_ctx, "pid directory", "piddir.tmp");
+
+ /*
+ * We use cluster_id(0, 0) as that gets for
+ * all task ids.
+ */
+ state.server_ctx = imessaging_init(tctx,
+ tctx->lp_ctx,
+ cluster_id(0, 0),
+ tctx->ev);
+ torture_assert(tctx, state.server_ctx != NULL,
+ "Failed to init messaging context");
+
+ status = imessaging_register(state.server_ctx, &state,
+ MSG_TMP_BASE-1,
+ multi_ctx_server_handler);
+ torture_assert(tctx, NT_STATUS_IS_OK(status), "imessaging_register failed");
+
+ state.client_ctx[0] = imessaging_init(tctx,
+ tctx->lp_ctx,
+ cluster_id(0, 0),
+ tctx->ev);
+ torture_assert(tctx, state.client_ctx[0] != NULL,
+ "msg_client_ctx imessaging_init() failed");
+ status = imessaging_register(state.client_ctx[0], &state,
+ MSG_TMP_BASE-1,
+ multi_ctx_client_0_1_handler);
+ torture_assert(tctx, NT_STATUS_IS_OK(status), "imessaging_register failed");
+ state.client_ctx[1] = imessaging_init(tctx,
+ tctx->lp_ctx,
+ cluster_id(0, 0),
+ tctx->ev);
+ torture_assert(tctx, state.client_ctx[1] != NULL,
+ "msg_client_ctx imessaging_init() failed");
+ status = imessaging_register(state.client_ctx[1], &state,
+ MSG_TMP_BASE-1,
+ multi_ctx_client_0_1_handler);
+ torture_assert(tctx, NT_STATUS_IS_OK(status), "imessaging_register failed");
+ state.client_ctx[2] = imessaging_init(tctx,
+ tctx->lp_ctx,
+ cluster_id(0, 0),
+ tctx->ev);
+ torture_assert(tctx, state.client_ctx[2] != NULL,
+ "msg_client_ctx imessaging_init() failed");
+ status = imessaging_register(state.client_ctx[2], &state,
+ MSG_TMP_BASE-1,
+ multi_ctx_client_2_3_handler);
+ torture_assert(tctx, NT_STATUS_IS_OK(status), "imessaging_register failed");
+ state.client_ctx[3] = imessaging_init(tctx,
+ tctx->lp_ctx,
+ cluster_id(0, 0),
+ tctx->ev);
+ torture_assert(tctx, state.client_ctx[3] != NULL,
+ "msg_client_ctx imessaging_init() failed");
+ status = imessaging_register(state.client_ctx[3], &state,
+ MSG_TMP_BASE-1,
+ multi_ctx_client_2_3_handler);
+ torture_assert(tctx, NT_STATUS_IS_OK(status), "imessaging_register failed");
+
+ /*
+ * Send one message that need to arrive on 3 ( 5 - 2 ) handlers.
+ */
+ state.num_missing = 5;
+
+ status = imessaging_send(state.server_ctx,
+ cluster_id(0, 0),
+ MSG_TMP_BASE-1, NULL);
+ torture_assert_ntstatus_ok(tctx, status, "msg failed");
+
+ tv = timeval_current();
+ while (timeval_elapsed(&tv) < 30 && state.num_missing > 0 && state.ok) {
+ int ret;
+
+ ret = tevent_loop_once(tctx->ev);
+ torture_assert_int_equal(tctx, ret, 0, "tevent_loop_once()");
+ }
+
+ if (!state.ok) {
+ return false;
+ }
+
+ torture_assert_int_equal(tctx, state.num_missing, 0,
+ "wrong message count");
+
+ torture_assert(tctx, state.got_client_0_1, "got_client_0_1");
+ torture_assert(tctx, state.got_client_2_3, "got_client_2_3");
+ torture_assert(tctx, state.got_server, "got_server");
+
+ return true;
+}
+
+struct torture_suite *torture_local_messaging(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *s = torture_suite_create(mem_ctx, "messaging");
+ torture_suite_add_simple_test(s, "overflow", test_messaging_overflow);
+ torture_suite_add_simple_test(s, "overflow_check",
+ test_messaging_overflow_check);
+ torture_suite_add_simple_test(s, "ping_speed", test_ping_speed);
+ torture_suite_add_simple_test(s, "multi_ctx", test_multi_ctx);
+ return s;
+}
diff --git a/source4/lib/messaging/wscript_build b/source4/lib/messaging/wscript_build
new file mode 100644
index 0000000..3408396
--- /dev/null
+++ b/source4/lib/messaging/wscript_build
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+
+
+bld.SAMBA_LIBRARY('MESSAGING_SEND',
+ source='messaging_send.c',
+ public_deps='messages_util messages_dgm UNIX_PRIVS cluster server_id_db',
+ private_library=True
+ )
+
+bld.SAMBA_LIBRARY('MESSAGING',
+ source='messaging.c messaging_handlers.c',
+ public_deps='''
+ samba-util
+ NDR_IRPC
+ UNIX_PRIVS
+ cluster
+ ndr
+ dcerpc
+ messages_util
+ server_id_db
+ talloc_report_printf
+ ''',
+ private_library=True
+ )
+
+pyparam_util = bld.pyembed_libname('pyparam_util')
+pytalloc_util = bld.pyembed_libname('pytalloc-util')
+
+bld.SAMBA_PYTHON('python_messaging',
+ source='pymessaging.c',
+ deps='MESSAGING events %s %s' % (pyparam_util, pytalloc_util),
+ realname='samba/messaging.so'
+ )
diff --git a/source4/lib/policy/gp_filesys.c b/source4/lib/policy/gp_filesys.c
new file mode 100644
index 0000000..8ad4645
--- /dev/null
+++ b/source4/lib/policy/gp_filesys.c
@@ -0,0 +1,698 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Group Policy Object Support
+ * Copyright (C) Wilco Baan Hofman 2008-2010
+ *
+ * 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 "lib/policy/policy.h"
+#include "libcli/raw/smb.h"
+#include "libcli/libcli.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+#include "libcli/raw/libcliraw.h"
+#include <dirent.h>
+#include <errno.h>
+
+#define GP_MAX_DEPTH 25
+
+struct gp_file_entry {
+ bool is_directory;
+ const char *rel_path;
+};
+struct gp_file_list {
+ uint32_t num_files;
+ struct gp_file_entry *files;
+};
+struct gp_list_state {
+ struct smbcli_tree *tree;
+ uint8_t depth;
+ const char *cur_rel_path;
+ const char *share_path;
+
+ struct gp_file_list list;
+};
+
+static NTSTATUS gp_do_list(const char *, struct gp_list_state *);
+
+/* Create a temporary policy directory */
+static const char *gp_tmpdir(TALLOC_CTX *mem_ctx)
+{
+ char *gp_dir = talloc_asprintf(mem_ctx, "%s/policy", tmpdir());
+ struct stat st;
+ int rv;
+
+ if (gp_dir == NULL) return NULL;
+
+ if (stat(gp_dir, &st) != 0) {
+ rv = mkdir(gp_dir, 0755);
+ if (rv < 0) {
+ DEBUG(0, ("Failed to create directory %s: %s\n",
+ gp_dir, strerror(errno)));
+ talloc_free(gp_dir);
+ return NULL;
+ }
+ }
+
+ return gp_dir;
+}
+
+/* This function is called by the smbcli_list function */
+static void gp_list_helper (struct clilist_file_info *info, const char *mask,
+ void *list_state_ptr)
+{
+ struct gp_list_state *state = list_state_ptr;
+ const char *rel_path;
+
+ /* Ignore . and .. directory entries */
+ if (strcmp(info->name, ".") == 0 || strcmp(info->name, "..") == 0) {
+ return;
+ }
+
+ /* Safety check against ../.. in filenames which may occur on non-POSIX
+ * platforms */
+ if (strstr(info->name, "../")) {
+ return;
+ }
+
+ rel_path = talloc_asprintf(state, "%s\\%s", state->cur_rel_path, info->name);
+ if (rel_path == NULL) return;
+
+ /* Append entry to file list */
+ state->list.files = talloc_realloc(state, state->list.files,
+ struct gp_file_entry,
+ state->list.num_files + 1);
+ if (state->list.files == NULL) return;
+
+ state->list.files[state->list.num_files].rel_path = rel_path;
+
+ /* Directory */
+ if (info->attrib & FILE_ATTRIBUTE_DIRECTORY) {
+ state->list.files[state->list.num_files].is_directory = true;
+ state->list.num_files++;
+
+ /* Recurse into this directory if the depth is below the maximum */
+ if (state->depth < GP_MAX_DEPTH) {
+ gp_do_list(rel_path, state);
+ }
+
+ return;
+ }
+
+ state->list.files[state->list.num_files].is_directory = false;
+ state->list.num_files++;
+
+ return;
+}
+
+static NTSTATUS gp_do_list (const char *rel_path, struct gp_list_state *state)
+{
+ uint16_t attributes;
+ int rv;
+ char *mask;
+ const char *old_rel_path;
+
+ attributes = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_DIRECTORY;
+
+ /* Update the relative paths, while buffering the parent */
+ old_rel_path = state->cur_rel_path;
+ state->cur_rel_path = rel_path;
+ state->depth++;
+
+ /* Get the current mask */
+ mask = talloc_asprintf(state, "%s%s\\*", state->share_path, rel_path);
+ NT_STATUS_HAVE_NO_MEMORY(mask);
+ rv = smbcli_list(state->tree, mask, attributes, gp_list_helper, state);
+ talloc_free(mask);
+
+ /* Go back to the state of the parent */
+ state->cur_rel_path = old_rel_path;
+ state->depth--;
+
+ if (rv == -1)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS gp_cli_connect(struct gp_context *gp_ctx)
+{
+ struct smbcli_options options;
+ struct smbcli_session_options session_options;
+
+ if (gp_ctx->cli != NULL)
+ return NT_STATUS_OK;
+
+ gp_ctx->cli = smbcli_state_init(gp_ctx);
+
+ lpcfg_smbcli_options(gp_ctx->lp_ctx, &options);
+ lpcfg_smbcli_session_options(gp_ctx->lp_ctx, &session_options);
+
+ return smbcli_full_connection(gp_ctx,
+ &gp_ctx->cli,
+ gp_ctx->active_dc->name,
+ lpcfg_smb_ports(gp_ctx->lp_ctx),
+ "sysvol",
+ NULL,
+ lpcfg_socket_options(gp_ctx->lp_ctx),
+ gp_ctx->credentials,
+ lpcfg_resolve_context(gp_ctx->lp_ctx),
+ gp_ctx->ev_ctx,
+ &options,
+ &session_options,
+ lpcfg_gensec_settings(gp_ctx, gp_ctx->lp_ctx));
+}
+
+static char * gp_get_share_path(TALLOC_CTX *mem_ctx, const char *file_sys_path)
+{
+ unsigned int i, bkslash_cnt;
+
+ /* Get the path from the share down (\\..\..\(this\stuff) */
+ for (i = 0, bkslash_cnt = 0; file_sys_path[i] != '\0'; i++) {
+ if (file_sys_path[i] == '\\')
+ bkslash_cnt++;
+
+ if (bkslash_cnt == 4) {
+ return talloc_strdup(mem_ctx, &file_sys_path[i]);
+ }
+ }
+
+ return NULL;
+}
+
+static NTSTATUS gp_get_file (struct smbcli_tree *tree, const char *remote_src,
+ const char *local_dst)
+{
+ int fh_remote, fh_local;
+ uint8_t *buf;
+ size_t nread = 0;
+ size_t buf_size = 1024;
+ size_t file_size;
+ uint16_t attr;
+
+ /* Open the remote file */
+ fh_remote = smbcli_open(tree, remote_src, O_RDONLY, DENY_NONE);
+ if (fh_remote == -1) {
+ DEBUG(0, ("Failed to open remote file: %s\n", remote_src));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Open the local file */
+ fh_local = open(local_dst, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (fh_local == -1) {
+ DEBUG(0, ("Failed to open local file: %s\n", local_dst));
+ smbcli_close(tree, fh_remote);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Get the remote file size for error checking */
+ if (NT_STATUS_IS_ERR(smbcli_qfileinfo(tree, fh_remote,
+ &attr, &file_size, NULL, NULL, NULL, NULL, NULL)) &&
+ NT_STATUS_IS_ERR(smbcli_getattrE(tree, fh_remote,
+ &attr, &file_size, NULL, NULL, NULL))) {
+ DEBUG(0, ("Failed to get remote file size: %s\n", smbcli_errstr(tree)));
+ smbcli_close(tree, fh_remote);
+ close(fh_local);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ buf = talloc_zero_array(tree, uint8_t, buf_size);
+ if (buf == NULL) {
+ smbcli_close(tree, fh_remote);
+ close(fh_local);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Copy the contents of the file */
+ while (1) {
+ int n = smbcli_read(tree, fh_remote, buf, nread, buf_size);
+
+ if (n <= 0) {
+ break;
+ }
+
+ if (write(fh_local, buf, n) != n) {
+ DEBUG(0, ("Short write while copying file.\n"));
+ smbcli_close(tree, fh_remote);
+ close(fh_local);
+ talloc_free(buf);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ nread += n;
+ }
+
+ /* Close the files */
+ smbcli_close(tree, fh_remote);
+ close(fh_local);
+
+ talloc_free(buf);
+
+ /* Bytes read should match the file size, or the copy was incomplete */
+ if (nread != file_size) {
+ DEBUG(0, ("Remote/local file size mismatch after copying file: "
+ "%s (remote %zu, local %zu).\n",
+ remote_src, file_size, nread));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS gp_get_files(struct smbcli_tree *tree, const char *share_path,
+ const char *local_path, struct gp_file_list *list)
+{
+ uint32_t i;
+ int rv;
+ char *local_rel_path, *full_local_path, *full_remote_path;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ mem_ctx = talloc_new(tree);
+ NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
+
+ for (i = 0; i < list->num_files; i++) {
+
+ /* Get local path by replacing backslashes with slashes */
+ local_rel_path = talloc_strdup(mem_ctx, list->files[i].rel_path);
+ if (local_rel_path == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ string_replace(local_rel_path, '\\', '/');
+
+ full_local_path = talloc_asprintf(mem_ctx, "%s%s", local_path,
+ local_rel_path);
+ if (full_local_path == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* If the entry is a directory, create it. */
+ if (list->files[i].is_directory == true) {
+ rv = mkdir(full_local_path, 0755);
+ if (rv < 0) {
+ DEBUG(0, ("Failed to create directory %s: %s\n",
+ full_local_path, strerror(errno)));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ continue;
+ }
+
+ full_remote_path = talloc_asprintf(mem_ctx, "%s%s", share_path,
+ list->files[i].rel_path);
+ if (full_remote_path == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Get the file */
+ status = gp_get_file(tree, full_remote_path, full_local_path);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Error getting file.\n"));
+ talloc_free(mem_ctx);
+ return status;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gp_fetch_gpt (struct gp_context *gp_ctx, struct gp_object *gpo,
+ const char **ret_local_path)
+{
+ TALLOC_CTX *mem_ctx;
+ struct gp_list_state *state;
+ NTSTATUS status;
+ struct stat st;
+ int rv;
+ const char *local_path, *share_path;
+
+ /* Create a forked memory context, as a base for everything here */
+ mem_ctx = talloc_new(gp_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
+
+ if (gp_ctx->cli == NULL) {
+ status = gp_cli_connect(gp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to create cli connection to DC\n"));
+ talloc_free(mem_ctx);
+ return status;
+ }
+ }
+
+ /* Get the remote path to copy from */
+ share_path = gp_get_share_path(mem_ctx, gpo->file_sys_path);
+ if (share_path == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Get the local path to copy to */
+ local_path = talloc_asprintf(gp_ctx, "%s/%s", gp_tmpdir(mem_ctx), gpo->name);
+ if (local_path == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Prepare the state structure */
+ state = talloc_zero(mem_ctx, struct gp_list_state);
+ if (state == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->tree = gp_ctx->cli->tree;
+ state->share_path = share_path;
+
+ /* Create the GPO dir if it does not exist */
+ if (stat(local_path, &st) != 0) {
+ rv = mkdir(local_path, 0755);
+ if (rv < 0) {
+ DEBUG(0, ("Could not create local path\n"));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ /* Get the file list */
+ status = gp_do_list("", state);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Could not list GPO files on remote server\n"));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ /* If the list has no entries there is a problem. */
+ if (state->list.num_files == 0) {
+ DEBUG(0, ("File list is has no entries. Is the GPT directory empty?\n"));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Fetch the files */
+ status = gp_get_files(gp_ctx->cli->tree, share_path, local_path, &state->list);
+
+ /* Return the local path to the gpo */
+ *ret_local_path = local_path;
+
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS push_recursive (struct gp_context *gp_ctx, const char *local_path,
+ const char *remote_path, int depth)
+{
+ DIR *dir;
+ struct dirent *dirent;
+ char *entry_local_path = NULL;
+ char *entry_remote_path = NULL;
+ int local_fd = -1, remote_fd = -1;
+ int buf[1024];
+ int nread, total_read;
+ struct stat s;
+ NTSTATUS status;
+
+ dir = opendir(local_path);
+ while ((dirent = readdir(dir)) != NULL) {
+ if (strcmp(dirent->d_name, ".") == 0 ||
+ strcmp(dirent->d_name, "..") == 0) {
+ continue;
+ }
+
+ entry_local_path = talloc_asprintf(gp_ctx, "%s/%s", local_path,
+ dirent->d_name);
+ if (entry_local_path == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ entry_remote_path = talloc_asprintf(gp_ctx, "%s\\%s",
+ remote_path, dirent->d_name);
+ if (entry_remote_path == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ if (stat(entry_local_path, &s) != 0) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ if (s.st_mode & S_IFDIR) {
+ DEBUG(6, ("Pushing directory %s to %s on sysvol\n",
+ entry_local_path, entry_remote_path));
+ smbcli_mkdir(gp_ctx->cli->tree, entry_remote_path);
+ if (depth < GP_MAX_DEPTH) {
+ push_recursive(gp_ctx, entry_local_path,
+ entry_remote_path, depth+1);
+ }
+ } else {
+ DEBUG(6, ("Pushing file %s to %s on sysvol\n",
+ entry_local_path, entry_remote_path));
+ remote_fd = smbcli_open(gp_ctx->cli->tree,
+ entry_remote_path,
+ O_WRONLY | O_CREAT,
+ 0);
+ if (remote_fd < 0) {
+ DEBUG(0, ("Failed to create remote file: %s\n",
+ entry_remote_path));
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ local_fd = open(entry_local_path, O_RDONLY);
+ if (local_fd < 0) {
+ DEBUG(0, ("Failed to open local file: %s\n",
+ entry_local_path));
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ total_read = 0;
+ while ((nread = read(local_fd, &buf, sizeof(buf)))) {
+ if (nread == -1) {
+ DBG_ERR("read failed with errno %s\n",
+ strerror(errno));
+ status = NT_STATUS_UNSUCCESSFUL;
+ close(local_fd);
+ local_fd = -1;
+ goto done;
+ }
+ smbcli_write(gp_ctx->cli->tree, remote_fd, 0,
+ &buf, total_read, nread);
+ total_read += nread;
+ }
+
+ close(local_fd);
+ local_fd = -1;
+ smbcli_close(gp_ctx->cli->tree, remote_fd);
+ remote_fd = -1;
+ }
+ TALLOC_FREE(entry_local_path);
+ TALLOC_FREE(entry_remote_path);
+ }
+
+ status = NT_STATUS_OK;
+done:
+ if (local_fd != -1) {
+ close(local_fd);
+ }
+ if (remote_fd != -1) {
+ smbcli_close(gp_ctx->cli->tree, remote_fd);
+ }
+ talloc_free(entry_local_path);
+ talloc_free(entry_remote_path);
+
+ closedir(dir);
+
+ return status;
+}
+
+
+
+NTSTATUS gp_push_gpt(struct gp_context *gp_ctx, const char *local_path,
+ const char *file_sys_path)
+{
+ NTSTATUS status;
+ char *share_path;
+
+ if (gp_ctx->cli == NULL) {
+ status = gp_cli_connect(gp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to create cli connection to DC\n"));
+ return status;
+ }
+ }
+ share_path = gp_get_share_path(gp_ctx, file_sys_path);
+
+ DEBUG(5, ("Copying %s to %s on sysvol\n", local_path, share_path));
+
+ smbcli_mkdir(gp_ctx->cli->tree, share_path);
+
+ status = push_recursive(gp_ctx, local_path, share_path, 0);
+
+ talloc_free(share_path);
+ return status;
+}
+
+NTSTATUS gp_create_gpt(struct gp_context *gp_ctx, const char *name,
+ const char *file_sys_path)
+{
+ TALLOC_CTX *mem_ctx;
+ const char *tmp_dir, *policy_dir, *tmp_str;
+ int rv;
+ int fd;
+ NTSTATUS status;
+ const char *file_content = "[General]\r\nVersion=0\r\n";
+
+ /* Create a forked memory context, as a base for everything here */
+ mem_ctx = talloc_new(gp_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
+
+ tmp_dir = gp_tmpdir(mem_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_dir);
+ policy_dir = talloc_asprintf(mem_ctx, "%s/%s", tmp_dir, name);
+ NT_STATUS_HAVE_NO_MEMORY(policy_dir);
+
+ /* Create the directories */
+
+ rv = mkdir(policy_dir, 0755);
+ if (rv < 0) {
+ DEBUG(0, ("Could not create the policy dir: %s\n", policy_dir));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ tmp_str = talloc_asprintf(mem_ctx, "%s/User", policy_dir);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_str);
+ rv = mkdir(tmp_str, 0755);
+ if (rv < 0) {
+ DEBUG(0, ("Could not create the User dir: %s\n", tmp_str));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ tmp_str = talloc_asprintf(mem_ctx, "%s/Machine", policy_dir);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_str);
+ rv = mkdir(tmp_str, 0755);
+ if (rv < 0) {
+ DEBUG(0, ("Could not create the Machine dir: %s\n", tmp_str));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Create a GPT.INI with version 0 */
+
+ tmp_str = talloc_asprintf(mem_ctx, "%s/GPT.INI", policy_dir);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_str);
+ fd = open(tmp_str, O_CREAT | O_WRONLY, 0644);
+ if (fd < 0) {
+ DEBUG(0, ("Could not create the GPT.INI: %s\n", tmp_str));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ rv = write(fd, file_content, strlen(file_content));
+ close(fd);
+ if (rv != strlen(file_content)) {
+ DEBUG(0, ("Short write in GPT.INI\n"));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Upload the GPT to the sysvol share on a DC */
+ status = gp_push_gpt(gp_ctx, policy_dir, file_sys_path);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gp_set_gpt_security_descriptor(struct gp_context *gp_ctx,
+ struct gp_object *gpo,
+ struct security_descriptor *sd)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ union smb_setfileinfo fileinfo;
+ union smb_open io;
+ union smb_close io_close;
+
+ /* Create a connection to sysvol if it is not already there */
+ if (gp_ctx->cli == NULL) {
+ status = gp_cli_connect(gp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to create cli connection to DC\n"));
+ return status;
+ }
+ }
+
+ /* Create a forked memory context which can be freed easily */
+ mem_ctx = talloc_new(gp_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
+
+ /* Open the directory with NTCreate AndX call */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid.fnum = 0;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = gp_get_share_path(mem_ctx, gpo->file_sys_path);
+ status = smb_raw_open(gp_ctx->cli->tree, mem_ctx, &io);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Can't open GPT directory\n"));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ /* Set the security descriptor on the directory */
+ fileinfo.generic.level = RAW_SFILEINFO_SEC_DESC;
+ fileinfo.set_secdesc.in.file.fnum = io.ntcreatex.out.file.fnum;
+ fileinfo.set_secdesc.in.secinfo_flags = SECINFO_PROTECTED_DACL |
+ SECINFO_OWNER |
+ SECINFO_GROUP |
+ SECINFO_DACL;
+ fileinfo.set_secdesc.in.sd = sd;
+ status = smb_raw_setfileinfo(gp_ctx->cli->tree, &fileinfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to set security descriptor on the GPT\n"));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ /* Close the directory */
+ io_close.close.level = RAW_CLOSE_CLOSE;
+ io_close.close.in.file.fnum = io.ntcreatex.out.file.fnum;
+ io_close.close.in.write_time = 0;
+ status = smb_raw_close(gp_ctx->cli->tree, &io_close);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to close directory\n"));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+}
diff --git a/source4/lib/policy/gp_ini.c b/source4/lib/policy/gp_ini.c
new file mode 100644
index 0000000..da2f5f4
--- /dev/null
+++ b/source4/lib/policy/gp_ini.c
@@ -0,0 +1,133 @@
+
+/*
+ * Unix SMB/CIFS implementation.
+ * Group Policy Object Support
+ * Copyright (C) Wilco Baan Hofman 2010
+ *
+ * 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 "lib/util/samba_util.h"
+#include "lib/policy/policy.h"
+
+struct gp_parse_context {
+ struct gp_ini_context *ini;
+ int32_t cur_section;
+};
+
+
+static bool gp_add_ini_section(const char *name, void *callback_data)
+{
+ struct gp_parse_context *parse = callback_data;
+ struct gp_ini_context *ini = parse->ini;
+
+ ini->sections = talloc_realloc(ini, ini->sections, struct gp_ini_section, ini->num_sections+1);
+ if (ini->sections == NULL) return false;
+ ini->sections[ini->num_sections].name = talloc_strdup(ini, name);
+ if (ini->sections[ini->num_sections].name == NULL) return false;
+ parse->cur_section = ini->num_sections;
+ ini->num_sections++;
+
+ return true;
+}
+
+static bool gp_add_ini_param(const char *name, const char *value, void *callback_data)
+{
+ struct gp_parse_context *parse = callback_data;
+ struct gp_ini_context *ini = parse->ini;
+ struct gp_ini_section *section;
+
+ if (parse->cur_section == -1) {
+ return false;
+ }
+
+ section = &ini->sections[parse->cur_section];
+
+ section->params = talloc_realloc(ini, ini->sections[parse->cur_section].params, struct gp_ini_param, section->num_params+1);
+ if (section->params == NULL) return false;
+ section->params[section->num_params].name = talloc_strdup(ini, name);
+ if (section->params[section->num_params].name == NULL) return false;
+ section->params[section->num_params].value = talloc_strdup(ini, value);
+ if (section->params[section->num_params].value == NULL) return false;
+ section->num_params++;
+
+ return true;
+}
+
+NTSTATUS gp_parse_ini(TALLOC_CTX *mem_ctx, struct gp_context *gp_ctx, const char *filename, struct gp_ini_context **ret)
+{
+ struct gp_parse_context parse;
+ bool rv;
+
+ parse.ini = talloc_zero(mem_ctx, struct gp_ini_context);
+ NT_STATUS_HAVE_NO_MEMORY(parse.ini);
+ parse.cur_section = -1;
+
+ rv = pm_process(filename, gp_add_ini_section, gp_add_ini_param, &parse);
+ if (!rv) {
+ DEBUG(0, ("Error while processing ini file %s\n", filename));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ *ret = parse.ini;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gp_get_ini_string(struct gp_ini_context *ini, const char *section, const char *name, char **ret)
+{
+ uint16_t i;
+ int32_t cur_sec = -1;
+ for (i = 0; i < ini->num_sections; i++) {
+ if (strcmp(ini->sections[i].name, section) == 0) {
+ cur_sec = i;
+ break;
+ }
+ }
+
+ if (cur_sec == -1) {
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ for (i = 0; i < ini->sections[cur_sec].num_params; i++) {
+ if (strcmp(ini->sections[cur_sec].params[i].name, name) == 0) {
+ *ret = ini->sections[cur_sec].params[i].value;
+ return NT_STATUS_OK;
+ }
+ }
+ return NT_STATUS_NOT_FOUND;
+}
+
+NTSTATUS gp_get_ini_uint(struct gp_ini_context *ini, const char *section, const char *name, uint32_t *ret)
+{
+ uint16_t i;
+ int32_t cur_sec = -1;
+ for (i = 0; i < ini->num_sections; i++) {
+ if (strcmp(ini->sections[i].name, section) == 0) {
+ cur_sec = i;
+ break;
+ }
+ }
+
+ if (cur_sec == -1) {
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ for (i = 0; i < ini->sections[cur_sec].num_params; i++) {
+ if (strcmp(ini->sections[cur_sec].params[i].name, name) == 0) {
+ *ret = atol(ini->sections[cur_sec].params[i].value);
+ return NT_STATUS_OK;
+ }
+ }
+ return NT_STATUS_NOT_FOUND;
+}
diff --git a/source4/lib/policy/gp_ldap.c b/source4/lib/policy/gp_ldap.c
new file mode 100644
index 0000000..67b329b
--- /dev/null
+++ b/source4/lib/policy/gp_ldap.c
@@ -0,0 +1,1130 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Group Policy Object Support
+ * Copyright (C) Jelmer Vernooij 2008
+ * Copyright (C) Wilco Baan Hofman 2008-2010
+ *
+ * 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 "param/param.h"
+#include <ldb.h>
+#include "lib/ldb-samba/ldb_wrap.h"
+#include "auth/credentials/credentials.h"
+#include "../librpc/gen_ndr/nbt.h"
+#include "libcli/libcli.h"
+#include "libnet/libnet.h"
+#include "../librpc/gen_ndr/ndr_security.h"
+#include "../libcli/security/security.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include "../lib/talloc/talloc.h"
+#include "lib/policy/policy.h"
+
+struct gpo_stringmap {
+ const char *str;
+ uint32_t flags;
+};
+static const struct gpo_stringmap gplink_options [] = {
+ { "GPLINK_OPT_DISABLE", GPLINK_OPT_DISABLE },
+ { "GPLINK_OPT_ENFORCE", GPLINK_OPT_ENFORCE },
+ { NULL, 0 }
+};
+static const struct gpo_stringmap gpo_flags [] = {
+ { "GPO_FLAG_USER_DISABLE", GPO_FLAG_USER_DISABLE },
+ { "GPO_FLAG_MACHINE_DISABLE", GPO_FLAG_MACHINE_DISABLE },
+ { NULL, 0 }
+};
+
+static NTSTATUS parse_gpo(TALLOC_CTX *mem_ctx, struct ldb_message *msg, struct gp_object **ret)
+{
+ struct gp_object *gpo = talloc(mem_ctx, struct gp_object);
+ enum ndr_err_code ndr_err;
+ const DATA_BLOB *data;
+
+ NT_STATUS_HAVE_NO_MEMORY(gpo);
+
+ gpo->dn = talloc_strdup(mem_ctx, ldb_dn_get_linearized(msg->dn));
+ if (gpo->dn == NULL) {
+ TALLOC_FREE(gpo);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(9, ("Parsing GPO LDAP data for %s\n", gpo->dn));
+
+ gpo->display_name = talloc_strdup(gpo, ldb_msg_find_attr_as_string(msg, "displayName", ""));
+ if (gpo->display_name == NULL) {
+ TALLOC_FREE(gpo);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ gpo->name = talloc_strdup(gpo, ldb_msg_find_attr_as_string(msg, "name", ""));
+ if (gpo->name == NULL) {
+ TALLOC_FREE(gpo);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ gpo->flags = ldb_msg_find_attr_as_uint(msg, "flags", 0);
+ gpo->version = ldb_msg_find_attr_as_uint(msg, "versionNumber", 0);
+
+ gpo->file_sys_path = talloc_strdup(gpo, ldb_msg_find_attr_as_string(msg, "gPCFileSysPath", ""));
+ if (gpo->file_sys_path == NULL) {
+ TALLOC_FREE(gpo);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Pull the security descriptor through the NDR library */
+ data = ldb_msg_find_ldb_val(msg, "nTSecurityDescriptor");
+ gpo->security_descriptor = talloc(gpo, struct security_descriptor);
+ if (gpo->security_descriptor == NULL) {
+ TALLOC_FREE(gpo);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ndr_err = ndr_pull_struct_blob(data,
+ mem_ctx,
+ gpo->security_descriptor,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ *ret = gpo;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gp_get_gpo_flags(TALLOC_CTX *mem_ctx, uint32_t flags, const char ***ret)
+{
+ unsigned int i, count=0;
+ const char **flag_strs = talloc_array(mem_ctx, const char *, 1);
+
+ NT_STATUS_HAVE_NO_MEMORY(flag_strs);
+
+ flag_strs[0] = NULL;
+
+ for (i = 0; gpo_flags[i].str != NULL; i++) {
+ if (flags & gpo_flags[i].flags) {
+ flag_strs = talloc_realloc(mem_ctx, flag_strs, const char *, count+2);
+ NT_STATUS_HAVE_NO_MEMORY(flag_strs);
+ flag_strs[count] = gpo_flags[i].str;
+ flag_strs[count+1] = NULL;
+ count++;
+ }
+ }
+ *ret = flag_strs;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gp_get_gplink_options(TALLOC_CTX *mem_ctx, uint32_t options, const char ***ret)
+{
+ unsigned int i, count=0;
+ const char **flag_strs = talloc_array(mem_ctx, const char *, 1);
+
+ NT_STATUS_HAVE_NO_MEMORY(flag_strs);
+ flag_strs[0] = NULL;
+
+ for (i = 0; gplink_options[i].str != NULL; i++) {
+ if (options & gplink_options[i].flags) {
+ flag_strs = talloc_realloc(mem_ctx, flag_strs, const char *, count+2);
+ NT_STATUS_HAVE_NO_MEMORY(flag_strs);
+ flag_strs[count] = gplink_options[i].str;
+ flag_strs[count+1] = NULL;
+ count++;
+ }
+ }
+ *ret = flag_strs;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gp_init(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev_ctx,
+ struct gp_context **gp_ctx)
+{
+
+ struct libnet_LookupDCs *io;
+ char *url;
+ struct libnet_context *net_ctx;
+ struct ldb_context *ldb_ctx;
+ NTSTATUS rv;
+
+ /* Initialise the libnet context */
+ net_ctx = libnet_context_init(ev_ctx, lp_ctx);
+ net_ctx->cred = credentials;
+
+ /* Prepare libnet lookup structure for looking a DC (PDC is correct). */
+ io = talloc_zero(mem_ctx, struct libnet_LookupDCs);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+ io->in.name_type = NBT_NAME_PDC;
+ io->in.domain_name = lpcfg_workgroup(lp_ctx);
+
+ /* Find Active DC's */
+ rv = libnet_LookupDCs(net_ctx, mem_ctx, io);
+ if (!NT_STATUS_IS_OK(rv)) {
+ DEBUG(0, ("Failed to lookup DCs in domain\n"));
+ return rv;
+ }
+
+ /* Connect to ldap://DC_NAME with all relevant contexts*/
+ url = talloc_asprintf(mem_ctx, "ldap://%s", io->out.dcs[0].name);
+ NT_STATUS_HAVE_NO_MEMORY(url);
+ ldb_ctx = ldb_wrap_connect(mem_ctx, net_ctx->event_ctx, lp_ctx,
+ url, NULL, net_ctx->cred, 0);
+ if (ldb_ctx == NULL) {
+ DEBUG(0, ("Can't connect to DC's LDAP with url %s\n", url));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ *gp_ctx = talloc_zero(mem_ctx, struct gp_context);
+ NT_STATUS_HAVE_NO_MEMORY(gp_ctx);
+
+ (*gp_ctx)->lp_ctx = lp_ctx;
+ (*gp_ctx)->credentials = credentials;
+ (*gp_ctx)->ev_ctx = ev_ctx;
+ (*gp_ctx)->ldb_ctx = ldb_ctx;
+ (*gp_ctx)->active_dc = talloc_reference(*gp_ctx, &io->out.dcs[0]);
+
+ /* We don't need to keep the libnet context */
+ talloc_free(net_ctx);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gp_list_all_gpos(struct gp_context *gp_ctx, struct gp_object ***ret)
+{
+ struct ldb_result *result;
+ int rv;
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx;
+ struct ldb_dn *dn;
+ struct gp_object **gpo;
+ unsigned int i; /* same as in struct ldb_result */
+ const char **attrs;
+
+ /* Create a forked memory context, as a base for everything here */
+ mem_ctx = talloc_new(gp_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
+
+ /* Create full ldb dn of the policies base object */
+ dn = ldb_get_default_basedn(gp_ctx->ldb_ctx);
+ rv = ldb_dn_add_child(dn, ldb_dn_new(mem_ctx, gp_ctx->ldb_ctx, "CN=Policies,CN=System"));
+ if (!rv) {
+ DEBUG(0, ("Can't append subtree to DN\n"));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(10, ("Searching for policies in DN: %s\n", ldb_dn_get_linearized(dn)));
+
+ attrs = talloc_array(mem_ctx, const char *, 7);
+ if (attrs == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ attrs[0] = "nTSecurityDescriptor";
+ attrs[1] = "versionNumber";
+ attrs[2] = "flags";
+ attrs[3] = "name";
+ attrs[4] = "displayName";
+ attrs[5] = "gPCFileSysPath";
+ attrs[6] = NULL;
+
+ rv = ldb_search(gp_ctx->ldb_ctx, mem_ctx, &result, dn, LDB_SCOPE_ONELEVEL, attrs, "(objectClass=groupPolicyContainer)");
+ if (rv != LDB_SUCCESS) {
+ DEBUG(0, ("LDB search failed: %s\n%s\n", ldb_strerror(rv), ldb_errstring(gp_ctx->ldb_ctx)));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ gpo = talloc_array(gp_ctx, struct gp_object *, result->count+1);
+ if (gpo == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ gpo[result->count] = NULL;
+
+ for (i = 0; i < result->count; i++) {
+ status = parse_gpo(gp_ctx, result->msgs[i], &gpo[i]);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to parse GPO.\n"));
+ talloc_free(mem_ctx);
+ return status;
+ }
+ }
+
+ talloc_free(mem_ctx);
+
+ *ret = gpo;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gp_get_gpo_info(struct gp_context *gp_ctx, const char *dn_str, struct gp_object **ret)
+{
+ struct ldb_result *result;
+ struct ldb_dn *dn;
+ struct gp_object *gpo;
+ int rv;
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx;
+ const char **attrs;
+
+ /* Create a forked memory context, as a base for everything here */
+ mem_ctx = talloc_new(gp_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
+
+ /* Create an ldb dn struct for the dn string */
+ dn = ldb_dn_new(mem_ctx, gp_ctx->ldb_ctx, dn_str);
+
+ attrs = talloc_array(mem_ctx, const char *, 7);
+ if (attrs == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ attrs[0] = "nTSecurityDescriptor";
+ attrs[1] = "versionNumber";
+ attrs[2] = "flags";
+ attrs[3] = "name";
+ attrs[4] = "displayName";
+ attrs[5] = "gPCFileSysPath";
+ attrs[6] = NULL;
+
+ rv = ldb_search(gp_ctx->ldb_ctx,
+ mem_ctx,
+ &result,
+ dn,
+ LDB_SCOPE_BASE,
+ attrs,
+ "objectClass=groupPolicyContainer");
+ if (rv != LDB_SUCCESS) {
+ DEBUG(0, ("LDB search failed: %s\n%s\n", ldb_strerror(rv), ldb_errstring(gp_ctx->ldb_ctx)));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* We expect exactly one record */
+ if (result->count != 1) {
+ DEBUG(0, ("Could not find GPC with dn %s\n", dn_str));
+ talloc_free(mem_ctx);
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ status = parse_gpo(gp_ctx, result->msgs[0], &gpo);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to parse GPO.\n"));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ talloc_free(mem_ctx);
+
+ *ret = gpo;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS parse_gplink (TALLOC_CTX *mem_ctx, const char *gplink_str, struct gp_link ***ret)
+{
+ int start, idx=0;
+ int pos;
+ struct gp_link **gplinks;
+ char *buf, *end;
+ const char *gplink_start = "[LDAP://";
+
+ gplinks = talloc_array(mem_ctx, struct gp_link *, 1);
+ NT_STATUS_HAVE_NO_MEMORY(gplinks);
+
+ gplinks[0] = NULL;
+
+ /* Assuming every gPLink starts with "[LDAP://" */
+ start = strlen(gplink_start);
+
+ for (pos = start; pos < strlen(gplink_str); pos++) {
+ if (gplink_str[pos] == ';') {
+ gplinks = talloc_realloc(mem_ctx, gplinks, struct gp_link *, idx+2);
+ NT_STATUS_HAVE_NO_MEMORY(gplinks);
+ gplinks[idx] = talloc(mem_ctx, struct gp_link);
+ NT_STATUS_HAVE_NO_MEMORY(gplinks[idx]);
+ gplinks[idx]->dn = talloc_strndup(mem_ctx,
+ gplink_str + start,
+ pos - start);
+ if (gplinks[idx]->dn == NULL) {
+ TALLOC_FREE(gplinks);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (start = pos + 1; gplink_str[pos] != ']'; pos++);
+
+ buf = talloc_strndup(gplinks, gplink_str + start, pos - start);
+ if (buf == NULL) {
+ TALLOC_FREE(gplinks);
+ return NT_STATUS_NO_MEMORY;
+ }
+ gplinks[idx]->options = (uint32_t) strtoll(buf, &end, 0);
+ talloc_free(buf);
+
+ /* Set the last entry in the array to be NULL */
+ gplinks[idx + 1] = NULL;
+
+ /* Increment the array index, the string position past
+ the next "[LDAP://", and set the start reference */
+ idx++;
+ pos += strlen(gplink_start)+1;
+ start = pos;
+ }
+ }
+
+ *ret = gplinks;
+ return NT_STATUS_OK;
+}
+
+
+NTSTATUS gp_get_gplinks(struct gp_context *gp_ctx, const char *dn_str, struct gp_link ***ret)
+{
+ TALLOC_CTX *mem_ctx;
+ struct ldb_dn *dn;
+ struct ldb_result *result;
+ struct gp_link **gplinks;
+ char *gplink_str;
+ int rv;
+ unsigned int i;
+ NTSTATUS status;
+
+ /* Create a forked memory context, as a base for everything here */
+ mem_ctx = talloc_new(gp_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
+
+ dn = ldb_dn_new(mem_ctx, gp_ctx->ldb_ctx, dn_str);
+
+ rv = ldb_search(gp_ctx->ldb_ctx, mem_ctx, &result, dn, LDB_SCOPE_BASE, NULL, "(objectclass=*)");
+ if (rv != LDB_SUCCESS) {
+ DEBUG(0, ("LDB search failed: %s\n%s\n", ldb_strerror(rv), ldb_errstring(gp_ctx->ldb_ctx)));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ for (i = 0; i < result->count; i++) {
+ struct ldb_message_element *element = \
+ ldb_msg_find_element(result->msgs[i], "gPLink");
+ if (element != NULL) {
+ SMB_ASSERT(element->num_values > 0);
+ gplink_str = talloc_strdup(
+ mem_ctx,
+ (char *) element->values[0].data);
+ if (gplink_str == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ goto found;
+ }
+ }
+ gplink_str = talloc_strdup(mem_ctx, "");
+ if (gplink_str == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ found:
+
+ status = parse_gplink(gp_ctx, gplink_str, &gplinks);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to parse gPLink\n"));
+ return status;
+ }
+
+ talloc_free(mem_ctx);
+
+ *ret = gplinks;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gp_list_gpos(struct gp_context *gp_ctx, struct security_token *token, const char ***ret)
+{
+ TALLOC_CTX *mem_ctx;
+ const char **gpos;
+ struct ldb_result *result;
+ char *sid;
+ struct ldb_dn *dn;
+ struct ldb_message_element *element;
+ bool inherit;
+ const char *attrs[] = { "objectClass", NULL };
+ int rv;
+ NTSTATUS status;
+ unsigned int count = 0;
+ unsigned int i;
+ enum {
+ ACCOUNT_TYPE_USER = 0,
+ ACCOUNT_TYPE_MACHINE = 1
+ } account_type;
+
+ /* Create a forked memory context, as a base for everything here */
+ mem_ctx = talloc_new(gp_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
+
+ sid = ldap_encode_ndr_dom_sid(mem_ctx,
+ &token->sids[PRIMARY_USER_SID_INDEX]);
+ NT_STATUS_HAVE_NO_MEMORY(sid);
+
+ /* Find the user DN and objectclass via the sid from the security token */
+ rv = ldb_search(gp_ctx->ldb_ctx,
+ mem_ctx,
+ &result,
+ ldb_get_default_basedn(gp_ctx->ldb_ctx),
+ LDB_SCOPE_SUBTREE,
+ attrs,
+ "(&(objectclass=user)(objectSid=%s))", sid);
+ if (rv != LDB_SUCCESS) {
+ DEBUG(0, ("LDB search failed: %s\n%s\n", ldb_strerror(rv),
+ ldb_errstring(gp_ctx->ldb_ctx)));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ if (result->count != 1) {
+ DEBUG(0, ("Could not find user with sid %s.\n", sid));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ DEBUG(10,("Found DN for this user: %s\n", ldb_dn_get_linearized(result->msgs[0]->dn)));
+
+ element = ldb_msg_find_element(result->msgs[0], "objectClass");
+
+ /* We need to know if this account is a user or machine. */
+ account_type = ACCOUNT_TYPE_USER;
+ for (i = 0; i < element->num_values; i++) {
+ if (strcmp((char *)element->values[i].data, "computer") == 0) {
+ account_type = ACCOUNT_TYPE_MACHINE;
+ DEBUG(10, ("This user is a machine\n"));
+ }
+ }
+
+ gpos = talloc_array(gp_ctx, const char *, 1);
+ if (gpos == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ gpos[0] = NULL;
+
+ /* Walk through the containers until we hit the root */
+ inherit = 1;
+ dn = ldb_dn_get_parent(mem_ctx, result->msgs[0]->dn);
+ while (ldb_dn_compare_base(ldb_get_default_basedn(gp_ctx->ldb_ctx), dn) == 0) {
+ const char *gpo_attrs[] = { "gPLink", "gPOptions", NULL };
+ struct gp_link **gplinks;
+ enum gpo_inheritance gpoptions;
+
+ DEBUG(10, ("Getting gPLinks for DN: %s\n", ldb_dn_get_linearized(dn)));
+
+ /* Get the gPLink and gPOptions attributes from the container */
+ rv = ldb_search(gp_ctx->ldb_ctx,
+ mem_ctx,
+ &result,
+ dn,
+ LDB_SCOPE_BASE,
+ gpo_attrs,
+ "objectclass=*");
+ if (rv != LDB_SUCCESS) {
+ DEBUG(0, ("LDB search failed: %s\n%s\n", ldb_strerror(rv),
+ ldb_errstring(gp_ctx->ldb_ctx)));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Parse the gPLink attribute, put it into a nice struct array */
+ status = parse_gplink(mem_ctx, ldb_msg_find_attr_as_string(result->msgs[0], "gPLink", ""), &gplinks);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to parse gPLink\n"));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ /* Check all group policy links on this container */
+ for (i = 0; gplinks[i] != NULL; i++) {
+ struct gp_object *gpo;
+ uint32_t access_granted;
+
+ /* If inheritance was blocked at a higher level and this
+ * gplink is not enforced, it should not be applied */
+ if (!inherit && !(gplinks[i]->options & GPLINK_OPT_ENFORCE))
+ continue;
+
+ /* Don't apply disabled links */
+ if (gplinks[i]->options & GPLINK_OPT_DISABLE)
+ continue;
+
+ /* Get GPO information */
+ status = gp_get_gpo_info(gp_ctx, gplinks[i]->dn, &gpo);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to get gpo information for %s\n", gplinks[i]->dn));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ /* If the account does not have read access, this GPO does not apply
+ * to this account */
+ status = se_access_check(gpo->security_descriptor,
+ token,
+ (SEC_STD_READ_CONTROL | SEC_ADS_LIST | SEC_ADS_READ_PROP),
+ &access_granted);
+ if (!NT_STATUS_IS_OK(status)) {
+ continue;
+ }
+
+ /* If the account is a user and the GPO has user disabled flag, or
+ * a machine and the GPO has machine disabled flag, this GPO does
+ * not apply to this account */
+ if ((account_type == ACCOUNT_TYPE_USER &&
+ (gpo->flags & GPO_FLAG_USER_DISABLE)) ||
+ (account_type == ACCOUNT_TYPE_MACHINE &&
+ (gpo->flags & GPO_FLAG_MACHINE_DISABLE))) {
+ continue;
+ }
+
+ /* Add the GPO to the list */
+ gpos = talloc_realloc(gp_ctx, gpos, const char *, count+2);
+ if (gpos == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ gpos[count] = talloc_strdup(gp_ctx, gplinks[i]->dn);
+ if (gpos[count] == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ gpos[count+1] = NULL;
+ count++;
+
+ /* Clean up */
+ talloc_free(gpo);
+ }
+
+ /* If inheritance is blocked, then we should only add enforced gPLinks
+ * higher up */
+ gpoptions = ldb_msg_find_attr_as_uint(result->msgs[0], "gPOptions", 0);
+ if (gpoptions == GPO_BLOCK_INHERITANCE) {
+ inherit = 0;
+ }
+ dn = ldb_dn_get_parent(mem_ctx, dn);
+ }
+
+ talloc_free(mem_ctx);
+
+ *ret = gpos;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gp_set_gplink(struct gp_context *gp_ctx, const char *dn_str, struct gp_link *gplink)
+{
+ TALLOC_CTX *mem_ctx;
+ struct ldb_result *result;
+ struct ldb_dn *dn;
+ struct ldb_message *msg;
+ const char *attrs[] = { "gPLink", NULL };
+ const char *gplink_str;
+ int rv;
+ char *start;
+
+ /* Create a forked memory context, as a base for everything here */
+ mem_ctx = talloc_new(gp_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
+
+ dn = ldb_dn_new(mem_ctx, gp_ctx->ldb_ctx, dn_str);
+
+ rv = ldb_search(gp_ctx->ldb_ctx, mem_ctx, &result, dn, LDB_SCOPE_BASE, attrs, "(objectclass=*)");
+ if (rv != LDB_SUCCESS) {
+ DEBUG(0, ("LDB search failed: %s\n%s\n", ldb_strerror(rv), ldb_errstring(gp_ctx->ldb_ctx)));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (result->count != 1) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ gplink_str = ldb_msg_find_attr_as_string(result->msgs[0], "gPLink", "");
+
+ /* If this GPO link already exists, alter the options, else add it */
+ if ((start = strcasestr(gplink_str, gplink->dn)) != NULL) {
+ start += strlen(gplink->dn);
+ *start = '\0';
+ start++;
+ while (*start != ']' && *start != '\0') {
+ start++;
+ }
+ gplink_str = talloc_asprintf(mem_ctx, "%s;%d%s", gplink_str, gplink->options, start);
+ if (gplink_str == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ } else {
+ /* Prepend the new GPO link to the string. This list is backwards in priority. */
+ gplink_str = talloc_asprintf(mem_ctx, "[LDAP://%s;%d]%s", gplink->dn, gplink->options, gplink_str);
+ if (gplink_str == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = dn;
+
+ rv = ldb_msg_add_string(msg, "gPLink", gplink_str);
+ if (rv != LDB_SUCCESS) {
+ DEBUG(0, ("LDB message add string failed: %s\n", ldb_strerror(rv)));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
+
+ rv = ldb_modify(gp_ctx->ldb_ctx, msg);
+ if (rv != LDB_SUCCESS) {
+ DEBUG(0, ("LDB modify failed: %s\n", ldb_strerror(rv)));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gp_del_gplink(struct gp_context *gp_ctx, const char *dn_str, const char *gplink_dn)
+{
+ TALLOC_CTX *mem_ctx;
+ struct ldb_result *result;
+ struct ldb_dn *dn;
+ struct ldb_message *msg;
+ const char *attrs[] = { "gPLink", NULL };
+ const char *gplink_str, *search_string;
+ int rv;
+ char *p;
+
+ /* Create a forked memory context, as a base for everything here */
+ mem_ctx = talloc_new(gp_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
+
+ dn = ldb_dn_new(mem_ctx, gp_ctx->ldb_ctx, dn_str);
+
+ rv = ldb_search(gp_ctx->ldb_ctx, mem_ctx, &result, dn, LDB_SCOPE_BASE, attrs, "(objectclass=*)");
+ if (rv != LDB_SUCCESS) {
+ DEBUG(0, ("LDB search failed: %s\n%s\n", ldb_strerror(rv), ldb_errstring(gp_ctx->ldb_ctx)));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (result->count != 1) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ gplink_str = ldb_msg_find_attr_as_string(result->msgs[0], "gPLink", "");
+
+ /* If this GPO link already exists, alter the options, else add it */
+ search_string = talloc_asprintf(mem_ctx, "[LDAP://%s]", gplink_dn);
+ if (search_string == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ p = strcasestr(gplink_str, search_string);
+ if (p == NULL) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ *p = '\0';
+ p++;
+ while (*p != ']' && *p != '\0') {
+ p++;
+ }
+ p++;
+ gplink_str = talloc_asprintf(mem_ctx, "%s%s", gplink_str, p);
+ if (gplink_str == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = dn;
+
+ if (strcmp(gplink_str, "") == 0) {
+ rv = ldb_msg_add_empty(msg, "gPLink", LDB_FLAG_MOD_DELETE, NULL);
+ if (rv != LDB_SUCCESS) {
+ DEBUG(0, ("LDB message add empty element failed: %s\n", ldb_strerror(rv)));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ } else {
+ rv = ldb_msg_add_string(msg, "gPLink", gplink_str);
+ if (rv != LDB_SUCCESS) {
+ DEBUG(0, ("LDB message add string failed: %s\n", ldb_strerror(rv)));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
+ }
+ rv = ldb_modify(gp_ctx->ldb_ctx, msg);
+ if (rv != LDB_SUCCESS) {
+ DEBUG(0, ("LDB modify failed: %s\n", ldb_strerror(rv)));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gp_get_inheritance(struct gp_context *gp_ctx, const char *dn_str, enum gpo_inheritance *inheritance)
+{
+ TALLOC_CTX *mem_ctx;
+ struct ldb_result *result;
+ struct ldb_dn *dn;
+ const char *attrs[] = { "gPOptions", NULL };
+ int rv;
+
+ /* Create a forked memory context, as a base for everything here */
+ mem_ctx = talloc_new(gp_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
+
+ dn = ldb_dn_new(mem_ctx, gp_ctx->ldb_ctx, dn_str);
+
+ rv = ldb_search(gp_ctx->ldb_ctx, mem_ctx, &result, dn, LDB_SCOPE_BASE, attrs, "(objectclass=*)");
+ if (rv != LDB_SUCCESS) {
+ DEBUG(0, ("LDB search failed: %s\n%s\n", ldb_strerror(rv), ldb_errstring(gp_ctx->ldb_ctx)));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (result->count != 1) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ *inheritance = ldb_msg_find_attr_as_uint(result->msgs[0], "gPOptions", 0);
+
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gp_set_inheritance(struct gp_context *gp_ctx, const char *dn_str, enum gpo_inheritance inheritance)
+{
+ char *inheritance_string;
+ struct ldb_message *msg;
+ int rv;
+
+ msg = ldb_msg_new(gp_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(msg);
+
+ msg->dn = ldb_dn_new(msg, gp_ctx->ldb_ctx, dn_str);
+
+ inheritance_string = talloc_asprintf(msg, "%d", inheritance);
+ if (inheritance_string == NULL) {
+ TALLOC_FREE(msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rv = ldb_msg_add_string(msg, "gPOptions", inheritance_string);
+ if (rv != LDB_SUCCESS) {
+ DEBUG(0, ("LDB message add string failed: %s\n", ldb_strerror(rv)));
+ talloc_free(msg);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
+
+ rv = ldb_modify(gp_ctx->ldb_ctx, msg);
+ if (rv != LDB_SUCCESS) {
+ DEBUG(0, ("LDB modify failed: %s\n", ldb_strerror(rv)));
+ talloc_free(msg);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ talloc_free(msg);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gp_create_ldap_gpo(struct gp_context *gp_ctx, struct gp_object *gpo)
+{
+ struct ldb_message *msg;
+ TALLOC_CTX *mem_ctx;
+ int rv;
+ char *dn_str, *flags_str, *version_str;
+ struct ldb_dn *child_dn, *gpo_dn;
+
+ mem_ctx = talloc_new(gp_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
+
+ /* CN={GUID} */
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = ldb_get_default_basedn(gp_ctx->ldb_ctx);
+ dn_str = talloc_asprintf(mem_ctx, "CN=%s,CN=Policies,CN=System", gpo->name);
+ if (dn_str == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ child_dn = ldb_dn_new(mem_ctx, gp_ctx->ldb_ctx, dn_str);
+ rv = ldb_dn_add_child(msg->dn, child_dn);
+ if (!rv) goto ldb_msg_add_error;
+
+ flags_str = talloc_asprintf(mem_ctx, "%d", gpo->flags);
+ if (flags_str == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ version_str = talloc_asprintf(mem_ctx, "%d", gpo->version);
+ if (version_str == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rv = ldb_msg_add_string(msg, "objectClass", "top");
+ if (rv != LDB_SUCCESS) goto ldb_msg_add_error;
+ rv = ldb_msg_add_string(msg, "objectClass", "container");
+ if (rv != LDB_SUCCESS) goto ldb_msg_add_error;
+ rv = ldb_msg_add_string(msg, "objectClass", "groupPolicyContainer");
+ if (rv != LDB_SUCCESS) goto ldb_msg_add_error;
+ rv = ldb_msg_add_string(msg, "displayName", gpo->display_name);
+ if (rv != LDB_SUCCESS) goto ldb_msg_add_error;
+ rv = ldb_msg_add_string(msg, "name", gpo->name);
+ if (rv != LDB_SUCCESS) goto ldb_msg_add_error;
+ rv = ldb_msg_add_string(msg, "CN", gpo->name);
+ if (rv != LDB_SUCCESS) goto ldb_msg_add_error;
+ rv = ldb_msg_add_string(msg, "gPCFileSysPath", gpo->file_sys_path);
+ if (rv != LDB_SUCCESS) goto ldb_msg_add_error;
+ rv = ldb_msg_add_string(msg, "flags", flags_str);
+ if (rv != LDB_SUCCESS) goto ldb_msg_add_error;
+ rv = ldb_msg_add_string(msg, "versionNumber", version_str);
+ if (rv != LDB_SUCCESS) goto ldb_msg_add_error;
+ rv = ldb_msg_add_string(msg, "showInAdvancedViewOnly", "TRUE");
+ if (rv != LDB_SUCCESS) goto ldb_msg_add_error;
+ rv = ldb_msg_add_string(msg, "gpCFunctionalityVersion", "2");
+ if (rv != LDB_SUCCESS) goto ldb_msg_add_error;
+
+ rv = ldb_add(gp_ctx->ldb_ctx, msg);
+ if (rv != LDB_SUCCESS) {
+ DEBUG(0, ("LDB add error: %s\n", ldb_errstring(gp_ctx->ldb_ctx)));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ gpo_dn = msg->dn;
+
+ /* CN=User */
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = ldb_dn_copy(mem_ctx, gpo_dn);
+ child_dn = ldb_dn_new(mem_ctx, gp_ctx->ldb_ctx, "CN=User");
+ rv = ldb_dn_add_child(msg->dn, child_dn);
+ if (!rv) goto ldb_msg_add_error;
+
+ rv = ldb_msg_add_string(msg, "objectClass", "top");
+ if (rv != LDB_SUCCESS) goto ldb_msg_add_error;
+ rv = ldb_msg_add_string(msg, "objectClass", "container");
+ if (rv != LDB_SUCCESS) goto ldb_msg_add_error;
+ rv = ldb_msg_add_string(msg, "showInAdvancedViewOnly", "TRUE");
+ if (rv != LDB_SUCCESS) goto ldb_msg_add_error;
+ rv = ldb_msg_add_string(msg, "CN", "User");
+ if (rv != LDB_SUCCESS) goto ldb_msg_add_error;
+ rv = ldb_msg_add_string(msg, "name", "User");
+ if (rv != LDB_SUCCESS) goto ldb_msg_add_error;
+
+ rv = ldb_add(gp_ctx->ldb_ctx, msg);
+ if (rv != LDB_SUCCESS) {
+ DEBUG(0, ("LDB add error: %s\n", ldb_errstring(gp_ctx->ldb_ctx)));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* CN=Machine */
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = ldb_dn_copy(mem_ctx, gpo_dn);
+ child_dn = ldb_dn_new(mem_ctx, gp_ctx->ldb_ctx, "CN=Machine");
+ rv = ldb_dn_add_child(msg->dn, child_dn);
+ if (!rv) goto ldb_msg_add_error;
+
+ rv = ldb_msg_add_string(msg, "objectClass", "top");
+ if (rv != LDB_SUCCESS) goto ldb_msg_add_error;
+ rv = ldb_msg_add_string(msg, "objectClass", "container");
+ if (rv != LDB_SUCCESS) goto ldb_msg_add_error;
+ rv = ldb_msg_add_string(msg, "showInAdvancedViewOnly", "TRUE");
+ if (rv != LDB_SUCCESS) goto ldb_msg_add_error;
+ rv = ldb_msg_add_string(msg, "CN", "Machine");
+ if (rv != LDB_SUCCESS) goto ldb_msg_add_error;
+ rv = ldb_msg_add_string(msg, "name", "Machine");
+ if (rv != LDB_SUCCESS) goto ldb_msg_add_error;
+
+ rv = ldb_add(gp_ctx->ldb_ctx, msg);
+ if (rv != LDB_SUCCESS) {
+ DEBUG(0, ("LDB add error: %s\n", ldb_errstring(gp_ctx->ldb_ctx)));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ gpo->dn = talloc_strdup(gpo, ldb_dn_get_linearized(gpo_dn));
+ if (gpo->dn == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+
+ ldb_msg_add_error:
+ DEBUG(0, ("LDB Error adding element to ldb message\n"));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS gp_set_ads_acl (struct gp_context *gp_ctx, const char *dn_str, const struct security_descriptor *sd)
+{
+ TALLOC_CTX *mem_ctx;
+ DATA_BLOB data;
+ enum ndr_err_code ndr_err;
+ struct ldb_message *msg;
+ int rv;
+
+ /* Create a forked memory context to clean up easily */
+ mem_ctx = talloc_new(gp_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
+
+ /* Push the security descriptor through the NDR library */
+ ndr_err = ndr_push_struct_blob(&data,
+ mem_ctx,
+ sd,
+ (ndr_push_flags_fn_t)ndr_push_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+
+ /* Create a LDB message */
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = ldb_dn_new(mem_ctx, gp_ctx->ldb_ctx, dn_str);
+
+ rv = ldb_msg_add_value(msg, "nTSecurityDescriptor", &data, NULL);
+ if (rv != LDB_SUCCESS) {
+ DEBUG(0, ("LDB message add element failed for adding nTSecurityDescriptor: %s\n", ldb_strerror(rv)));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
+
+ rv = ldb_modify(gp_ctx->ldb_ctx, msg);
+ if (rv != LDB_SUCCESS) {
+ DEBUG(0, ("LDB modify failed: %s\n", ldb_strerror(rv)));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+}
+
+/* This function sets flags, version and displayName on a GPO */
+NTSTATUS gp_set_ldap_gpo(struct gp_context *gp_ctx, struct gp_object *gpo)
+{
+ int rv;
+ TALLOC_CTX *mem_ctx;
+ struct ldb_message *msg;
+ char *version_str, *flags_str;
+
+ mem_ctx = talloc_new(gp_ctx);
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = ldb_dn_new(mem_ctx, gp_ctx->ldb_ctx, gpo->dn);
+
+ version_str = talloc_asprintf(mem_ctx, "%d", gpo->version);
+ if (msg == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ flags_str = talloc_asprintf(mem_ctx, "%d", gpo->flags);
+ if (msg == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rv = ldb_msg_add_string(msg, "flags", flags_str);
+ if (rv != LDB_SUCCESS) {
+ DEBUG(0, ("LDB message add string failed for flags: %s\n", ldb_strerror(rv)));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
+
+ rv = ldb_msg_add_string(msg, "version", version_str);
+ if (rv != LDB_SUCCESS) {
+ DEBUG(0, ("LDB message add string failed for version: %s\n", ldb_strerror(rv)));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ msg->elements[1].flags = LDB_FLAG_MOD_REPLACE;
+
+ rv = ldb_msg_add_string(msg, "displayName", gpo->display_name);
+ if (rv != LDB_SUCCESS) {
+ DEBUG(0, ("LDB message add string failed for displayName: %s\n", ldb_strerror(rv)));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ msg->elements[2].flags = LDB_FLAG_MOD_REPLACE;
+
+ rv = ldb_modify(gp_ctx->ldb_ctx, msg);
+ if (rv != LDB_SUCCESS) {
+ DEBUG(0, ("LDB modify failed: %s\n", ldb_strerror(rv)));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+}
diff --git a/source4/lib/policy/gp_manage.c b/source4/lib/policy/gp_manage.c
new file mode 100644
index 0000000..769e6c0
--- /dev/null
+++ b/source4/lib/policy/gp_manage.c
@@ -0,0 +1,329 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Group Policy Object Support
+ * Copyright (C) Wilco Baan Hofman 2010
+ *
+ * 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 "../libcli/security/dom_sid.h"
+#include "../libcli/security/security_descriptor.h"
+#include "../librpc/ndr/libndr.h"
+#include "../lib/util/charset/charset.h"
+#include "param/param.h"
+#include "lib/policy/policy.h"
+
+uint32_t gp_ads_to_dir_access_mask(uint32_t access_mask)
+{
+ uint32_t fs_mask;
+
+ /* Copy the standard access mask */
+ fs_mask = access_mask & 0x001F0000;
+
+ /* When READ_PROP and LIST_CONTENTS are set, read access is granted on the GPT */
+ if (access_mask & SEC_ADS_READ_PROP && access_mask & SEC_ADS_LIST) {
+ fs_mask |= SEC_STD_SYNCHRONIZE | SEC_DIR_LIST | SEC_DIR_READ_ATTRIBUTE |
+ SEC_DIR_READ_EA | SEC_DIR_TRAVERSE;
+ }
+
+ /* When WRITE_PROP is set, full write access is granted on the GPT */
+ if (access_mask & SEC_ADS_WRITE_PROP) {
+ fs_mask |= SEC_STD_SYNCHRONIZE | SEC_DIR_WRITE_ATTRIBUTE |
+ SEC_DIR_WRITE_EA | SEC_DIR_ADD_FILE |
+ SEC_DIR_ADD_SUBDIR;
+ }
+
+ /* Map CREATE_CHILD to add file and add subdir */
+ if (access_mask & SEC_ADS_CREATE_CHILD)
+ fs_mask |= SEC_DIR_ADD_FILE | SEC_DIR_ADD_SUBDIR;
+
+ /* Map ADS delete child to dir delete child */
+ if (access_mask & SEC_ADS_DELETE_CHILD)
+ fs_mask |= SEC_DIR_DELETE_CHILD;
+
+ return fs_mask;
+}
+
+NTSTATUS gp_create_gpt_security_descriptor (TALLOC_CTX *mem_ctx, struct security_descriptor *ds_sd, struct security_descriptor **ret)
+{
+ struct security_descriptor *fs_sd;
+ NTSTATUS status;
+ uint32_t i;
+
+ /* Allocate the file system security descriptor */
+ fs_sd = talloc(mem_ctx, struct security_descriptor);
+ NT_STATUS_HAVE_NO_MEMORY(fs_sd);
+
+ /* Copy the basic information from the directory server security descriptor */
+ fs_sd->owner_sid = talloc_memdup(fs_sd, ds_sd->owner_sid, sizeof(struct dom_sid));
+ if (fs_sd->owner_sid == NULL) {
+ TALLOC_FREE(fs_sd);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ fs_sd->group_sid = talloc_memdup(fs_sd, ds_sd->group_sid, sizeof(struct dom_sid));
+ if (fs_sd->group_sid == NULL) {
+ TALLOC_FREE(fs_sd);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ fs_sd->type = ds_sd->type;
+ fs_sd->revision = ds_sd->revision;
+
+ /* Copy the sacl */
+ fs_sd->sacl = security_acl_dup(fs_sd, ds_sd->sacl);
+ if (fs_sd->sacl == NULL) {
+ TALLOC_FREE(fs_sd);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Copy the dacl */
+ fs_sd->dacl = talloc_zero(fs_sd, struct security_acl);
+ if (fs_sd->dacl == NULL) {
+ TALLOC_FREE(fs_sd);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < ds_sd->dacl->num_aces; i++) {
+ char *trustee = dom_sid_string(fs_sd, &ds_sd->dacl->aces[i].trustee);
+ struct security_ace *ace;
+
+ /* Don't add the allow for SID_BUILTIN_PREW2K */
+ if ((ds_sd->dacl->aces[i].type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
+ ds_sd->dacl->aces[i].type == SEC_ACE_TYPE_ACCESS_ALLOWED) &&
+ strcmp(trustee, SID_BUILTIN_PREW2K) == 0) {
+ talloc_free(trustee);
+ continue;
+ }
+
+ /* Copy the ace from the directory server security descriptor */
+ ace = talloc_memdup(fs_sd, &ds_sd->dacl->aces[i], sizeof(struct security_ace));
+ if (ace == NULL) {
+ TALLOC_FREE(fs_sd);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Set specific inheritance flags for within the GPO */
+ ace->flags |= SEC_ACE_FLAG_OBJECT_INHERIT | SEC_ACE_FLAG_CONTAINER_INHERIT;
+ if (strcmp(trustee, SID_CREATOR_OWNER) == 0) {
+ ace->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+ }
+
+ /* Get a directory access mask from the assigned access mask on the LDAP object */
+ ace->access_mask = gp_ads_to_dir_access_mask(ace->access_mask);
+
+ /* Add the ace to the security descriptor DACL */
+ status = security_descriptor_dacl_add(fs_sd, ace);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to add a dacl to file system security descriptor\n"));
+ return status;
+ }
+
+ /* Clean up the allocated data in this iteration */
+ talloc_free(trustee);
+ }
+
+ *ret = fs_sd;
+ return NT_STATUS_OK;
+}
+
+
+NTSTATUS gp_create_gpo (struct gp_context *gp_ctx, const char *display_name, struct gp_object **ret)
+{
+ struct GUID guid_struct;
+ char *guid_str;
+ char *name;
+ struct security_descriptor *sd;
+ TALLOC_CTX *mem_ctx;
+ struct gp_object *gpo;
+ NTSTATUS status;
+
+ /* Create a forked memory context, as a base for everything here */
+ mem_ctx = talloc_new(gp_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
+
+ /* Create the gpo struct to return later */
+ gpo = talloc(gp_ctx, struct gp_object);
+ if (gpo == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Generate a GUID */
+ guid_struct = GUID_random();
+ guid_str = GUID_string2(mem_ctx, &guid_struct);
+ if (guid_str == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ name = strupper_talloc(mem_ctx, guid_str);
+ if (name == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Prepare the GPO struct */
+ gpo->dn = NULL;
+ gpo->name = name;
+ gpo->flags = 0;
+ gpo->version = 0;
+ gpo->display_name = talloc_strdup(gpo, display_name);
+ if (gpo->display_name == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ gpo->file_sys_path = talloc_asprintf(gpo, "\\\\%s\\sysvol\\%s\\Policies\\%s", lpcfg_dnsdomain(gp_ctx->lp_ctx), lpcfg_dnsdomain(gp_ctx->lp_ctx), name);
+ if (gpo->file_sys_path == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Create the GPT */
+ status = gp_create_gpt(gp_ctx, name, gpo->file_sys_path);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to create GPT\n"));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+
+ /* Create the LDAP GPO, including CN=User and CN=Machine */
+ status = gp_create_ldap_gpo(gp_ctx, gpo);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to create LDAP group policy object\n"));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ /* Get the new security descriptor */
+ status = gp_get_gpo_info(gp_ctx, gpo->dn, &gpo);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to fetch LDAP group policy object\n"));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ /* Create matching file and DS security descriptors */
+ status = gp_create_gpt_security_descriptor(mem_ctx, gpo->security_descriptor, &sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to convert ADS security descriptor to filesystem security descriptor\n"));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ /* Set the security descriptor on the filesystem for this GPO */
+ status = gp_set_gpt_security_descriptor(gp_ctx, gpo, sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to set security descriptor (ACL) on the file system\n"));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ talloc_free(mem_ctx);
+
+ *ret = gpo;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gp_set_acl (struct gp_context *gp_ctx, const char *dn_str, const struct security_descriptor *sd)
+{
+ TALLOC_CTX *mem_ctx;
+ struct security_descriptor *fs_sd;
+ struct gp_object *gpo;
+ NTSTATUS status;
+
+ /* Create a forked memory context, as a base for everything here */
+ mem_ctx = talloc_new(gp_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
+
+ /* Set the ACL on LDAP database */
+ status = gp_set_ads_acl(gp_ctx, dn_str, sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to set ACL on ADS\n"));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ /* Get the group policy object information, for filesystem location and merged sd */
+ status = gp_get_gpo_info(gp_ctx, dn_str, &gpo);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to set ACL on ADS\n"));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ /* Create matching file and DS security descriptors */
+ status = gp_create_gpt_security_descriptor(mem_ctx, gpo->security_descriptor, &fs_sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to convert ADS security descriptor to filesystem security descriptor\n"));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ /* Set the security descriptor on the filesystem for this GPO */
+ status = gp_set_gpt_security_descriptor(gp_ctx, gpo, fs_sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to set security descriptor (ACL) on the file system\n"));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gp_push_gpo (struct gp_context *gp_ctx, const char *local_path, struct gp_object *gpo)
+{
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx;
+ struct gp_ini_context *ini;
+ char *filename;
+
+ mem_ctx = talloc_new(gp_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
+
+ /* Get version from ini file */
+ /* FIXME: The local file system may be case sensitive */
+ filename = talloc_asprintf(mem_ctx, "%s/%s", local_path, "GPT.INI");
+ if (filename == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = gp_parse_ini(mem_ctx, gp_ctx, local_path, &ini);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to parse GPT.INI.\n"));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ /* Push the GPT to the remote sysvol */
+ status = gp_push_gpt(gp_ctx, local_path, gpo->file_sys_path);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to push GPT to DC's sysvol share.\n"));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ /* Write version to LDAP */
+ status = gp_set_ldap_gpo(gp_ctx, gpo);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to set GPO options in DC's LDAP.\n"));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+}
diff --git a/source4/lib/policy/policy.h b/source4/lib/policy/policy.h
new file mode 100644
index 0000000..3d8a0dc
--- /dev/null
+++ b/source4/lib/policy/policy.h
@@ -0,0 +1,125 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Group Policy Object Support
+ * Copyright (C) Guenther Deschner 2005-2008 (from samba 3 gpo.h)
+ * Copyright (C) Wilco Baan Hofman 2010
+ *
+ * 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 __POLICY_H__
+#define __POLICY_H__
+
+#define GPLINK_OPT_DISABLE (1 << 0)
+#define GPLINK_OPT_ENFORCE (1 << 1)
+
+#define GPO_FLAG_USER_DISABLE (1 << 0)
+#define GPO_FLAG_MACHINE_DISABLE (1 << 1)
+
+struct security_token;
+struct nbt_dc_name;
+
+enum gpo_inheritance {
+ GPO_INHERIT = 0,
+ GPO_BLOCK_INHERITANCE = 1,
+};
+
+struct gp_context {
+ struct ldb_context *ldb_ctx;
+ struct loadparm_context *lp_ctx;
+ struct cli_credentials *credentials;
+ struct tevent_context *ev_ctx;
+ struct smbcli_state *cli;
+ struct nbt_dc_name *active_dc;
+};
+
+struct gp_object {
+ uint32_t version;
+ uint32_t flags;
+ const char *display_name;
+ const char *name;
+ const char *dn;
+ const char *file_sys_path;
+ struct security_descriptor *security_descriptor;
+};
+
+
+struct gp_link {
+ uint32_t options;
+ const char *dn;
+};
+
+struct gp_ini_param {
+ char *name;
+ char *value;
+};
+
+struct gp_ini_section {
+ char *name;
+ uint16_t num_params;
+ struct gp_ini_param *params;
+};
+
+struct gp_ini_context {
+ uint16_t num_sections;
+ struct gp_ini_section *sections;
+};
+
+NTSTATUS gp_init(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct cli_credentials *creds,
+ struct tevent_context *ev_ctx,
+ struct gp_context **gp_ctx);
+
+
+/* LDAP functions */
+NTSTATUS gp_list_all_gpos(struct gp_context *gp_ctx, struct gp_object ***ret);
+NTSTATUS gp_get_gplinks(struct gp_context *gp_ctx, const char *req_dn, struct gp_link ***ret);
+NTSTATUS gp_list_gpos(struct gp_context *gp_ctx, struct security_token *token, const char ***ret);
+
+NTSTATUS gp_get_gpo_info(struct gp_context *gp_ctx, const char *dn_str, struct gp_object **ret);
+
+
+NTSTATUS gp_get_gplink_options(TALLOC_CTX *mem_ctx, uint32_t flags, const char ***ret);
+NTSTATUS gp_get_gpo_flags(TALLOC_CTX *mem_ctx, uint32_t flags, const char ***ret);
+
+NTSTATUS gp_set_gplink(struct gp_context *gp_ctx, const char *dn_str, struct gp_link *gplink);
+NTSTATUS gp_del_gplink(struct gp_context *gp_ctx, const char *dn_str, const char *gp_dn);
+NTSTATUS gp_get_inheritance(struct gp_context *gp_ctx, const char *dn_str, enum gpo_inheritance *inheritance);
+NTSTATUS gp_set_inheritance(struct gp_context *gp_ctx, const char *dn_str, enum gpo_inheritance inheritance);
+
+NTSTATUS gp_create_ldap_gpo(struct gp_context *gp_ctx, struct gp_object *gpo);
+NTSTATUS gp_set_ads_acl (struct gp_context *gp_ctx, const char *dn_str, const struct security_descriptor *sd);
+NTSTATUS gp_push_gpo (struct gp_context *gp_ctx, const char *local_path, struct gp_object *gpo);
+NTSTATUS gp_set_ldap_gpo(struct gp_context *gp_ctx, struct gp_object *gpo);
+
+/* File system functions */
+NTSTATUS gp_fetch_gpt (struct gp_context *gp_ctx, struct gp_object *gpo, const char **path);
+NTSTATUS gp_create_gpt(struct gp_context *gp_ctx, const char *name, const char *file_sys_path);
+NTSTATUS gp_set_gpt_security_descriptor(struct gp_context *gp_ctx, struct gp_object *gpo, struct security_descriptor *sd);
+NTSTATUS gp_push_gpt(struct gp_context *gp_ctx, const char *local_path,
+ const char *file_sys_path);
+
+/* Ini functions */
+NTSTATUS gp_parse_ini(TALLOC_CTX *mem_ctx, struct gp_context *gp_ctx, const char *filename, struct gp_ini_context **ret);
+NTSTATUS gp_get_ini_string(struct gp_ini_context *ini, const char *section, const char *name, char **ret);
+NTSTATUS gp_get_ini_uint(struct gp_ini_context *ini, const char *section, const char *name, uint32_t *ret);
+
+/* Managing functions */
+NTSTATUS gp_create_gpo (struct gp_context *gp_ctx, const char *display_name, struct gp_object **ret);
+NTSTATUS gp_create_gpt_security_descriptor (TALLOC_CTX *mem_ctx, struct security_descriptor *ds_sd, struct security_descriptor **ret);
+NTSTATUS gp_set_acl (struct gp_context *gp_ctx, const char *dn_str, const struct security_descriptor *sd);
+uint32_t gp_ads_to_dir_access_mask(uint32_t access_mask);
+
+#endif
diff --git a/source4/lib/policy/pypolicy.c b/source4/lib/policy/pypolicy.c
new file mode 100644
index 0000000..f40d811
--- /dev/null
+++ b/source4/lib/policy/pypolicy.c
@@ -0,0 +1,174 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Python bindings for libpolicy
+ * Copyright (C) Jelmer Vernooij 2010
+ *
+ * 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 "python/py3compat.h"
+#include "policy.h"
+#include "libcli/util/pyerrors.h"
+
+void initpolicy(void);
+
+static PyObject *py_get_gpo_flags(PyObject *self, PyObject *args)
+{
+ int flags;
+ PyObject *py_ret;
+ const char **ret;
+ TALLOC_CTX *mem_ctx;
+ int i;
+ NTSTATUS status;
+
+ if (!PyArg_ParseTuple(args, "i", &flags))
+ return NULL;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ status = gp_get_gpo_flags(mem_ctx, flags, &ret);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ py_ret = PyList_New(0);
+ for (i = 0; ret[i]; i++) {
+ int res = 0;
+ PyObject *item = PyUnicode_FromString(ret[i]);
+ if (item == NULL) {
+ talloc_free(mem_ctx);
+ Py_DECREF(py_ret);
+ PyErr_NoMemory();
+ return NULL;
+ }
+ res = PyList_Append(py_ret, item);
+ Py_CLEAR(item);
+ if (res == -1) {
+ Py_DECREF(py_ret);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+ }
+
+ talloc_free(mem_ctx);
+
+ return py_ret;
+}
+
+static PyObject *py_get_gplink_options(PyObject *self, PyObject *args)
+{
+ int flags;
+ PyObject *py_ret;
+ const char **ret;
+ TALLOC_CTX *mem_ctx;
+ int i;
+ NTSTATUS status;
+
+ if (!PyArg_ParseTuple(args, "i", &flags))
+ return NULL;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ status = gp_get_gplink_options(mem_ctx, flags, &ret);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ py_ret = PyList_New(0);
+ for (i = 0; ret[i]; i++) {
+ int res = 0;
+ PyObject *item = PyUnicode_FromString(ret[i]);
+ if (item == NULL) {
+ talloc_free(mem_ctx);
+ Py_DECREF(py_ret);
+ PyErr_NoMemory();
+ return NULL;
+ }
+ res = PyList_Append(py_ret, item);
+ Py_CLEAR(item);
+ if (res == -1) {
+ Py_DECREF(py_ret);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+ }
+
+ talloc_free(mem_ctx);
+
+ return py_ret;
+}
+
+static PyObject *py_ads_to_dir_access_mask(PyObject *self, PyObject *args)
+{
+ uint32_t access_mask, dir_mask;
+
+ if (! PyArg_ParseTuple(args, "I", &access_mask))
+ return NULL;
+
+ dir_mask = gp_ads_to_dir_access_mask(access_mask);
+
+ return Py_BuildValue("I", dir_mask);
+}
+
+
+static PyMethodDef py_policy_methods[] = {
+ { "get_gpo_flags", (PyCFunction)py_get_gpo_flags, METH_VARARGS,
+ "get_gpo_flags(flags) -> list" },
+ { "get_gplink_options", (PyCFunction)py_get_gplink_options, METH_VARARGS,
+ "get_gplink_options(options) -> list" },
+ { "ads_to_dir_access_mask", (PyCFunction)py_ads_to_dir_access_mask, METH_VARARGS,
+ "ads_to_dir_access_mask(access_mask) -> dir_mask" },
+ {0}
+};
+
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "policy",
+ .m_doc = "(Group) Policy manipulation",
+ .m_size = -1,
+ .m_methods = py_policy_methods,
+};
+
+MODULE_INIT_FUNC(policy)
+{
+ PyObject *m = NULL;
+
+ m = PyModule_Create(&moduledef);
+ if (!m)
+ return m;
+
+ PyModule_AddObject(m, "GPO_FLAG_USER_DISABLE",
+ PyLong_FromLong(GPO_FLAG_USER_DISABLE));
+ PyModule_AddObject(m, "GPO_MACHINE_USER_DISABLE",
+ PyLong_FromLong(GPO_FLAG_MACHINE_DISABLE));
+ PyModule_AddObject(m, "GPLINK_OPT_DISABLE",
+ PyLong_FromLong(GPLINK_OPT_DISABLE ));
+ PyModule_AddObject(m, "GPLINK_OPT_ENFORCE ",
+ PyLong_FromLong(GPLINK_OPT_ENFORCE ));
+ return m;
+}
diff --git a/source4/lib/policy/samba-policy.pc.in b/source4/lib/policy/samba-policy.pc.in
new file mode 100644
index 0000000..3247ae3
--- /dev/null
+++ b/source4/lib/policy/samba-policy.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: samba-policy
+Description: Active Directory Group Policy library
+Requires: talloc
+Requires.private: ldb
+Version: @PACKAGE_VERSION@
+Libs: @LIB_RPATH@ -L${libdir} -lsamba-policy
+Cflags: -I${includedir} -DHAVE_IMMEDIATE_STRUCTURES=1
diff --git a/source4/lib/policy/wscript_build b/source4/lib/policy/wscript_build
new file mode 100644
index 0000000..027d4be
--- /dev/null
+++ b/source4/lib/policy/wscript_build
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+
+
+
+pytalloc_util = bld.pyembed_libname('pytalloc-util')
+samba_policy = bld.pyembed_libname('samba-policy')
+samba_net = bld.pyembed_libname('samba-net')
+bld.SAMBA_LIBRARY(samba_policy,
+ source='gp_ldap.c gp_filesys.c gp_manage.c gp_ini.c',
+ pc_files='samba-policy.pc',
+ public_deps='ldb %s' % samba_net,
+ vnum='0.0.1',
+ pyembed=True,
+ public_headers='policy.h',
+ enabled=bld.PYTHON_BUILD_IS_ENABLED()
+ )
+bld.SAMBA_PYTHON(
+ 'py_policy',
+ source='pypolicy.c',
+ public_deps='%s %s' % (samba_policy, pytalloc_util),
+ realname='samba/policy.so'
+ )
diff --git a/source4/lib/registry/Doxyfile b/source4/lib/registry/Doxyfile
new file mode 100644
index 0000000..efc01cd
--- /dev/null
+++ b/source4/lib/registry/Doxyfile
@@ -0,0 +1,24 @@
+PROJECT_NAME = REGISTRY
+OUTPUT_DIRECTORY = apidocs
+BRIEF_MEMBER_DESC = YES
+REPEAT_BRIEF = YES
+OPTIMIZE_OUTPUT_FOR_C = YES
+SORT_MEMBER_DOCS = YES
+SORT_BRIEF_DOCS = NO
+GENERATE_TODOLIST = YES
+GENERATE_BUGLIST = YES
+GENERATE_DEPRECATEDLIST= YES
+SHOW_USED_FILES = NO
+SHOW_DIRECTORIES = NO
+WARNINGS = YES
+WARN_IF_UNDOCUMENTED = YES
+WARN_IF_DOC_ERROR = YES
+WARN_NO_PARAMDOC = NO
+WARN_FORMAT = "$file:$line: $text"
+INPUT = .
+FILE_PATTERNS = *.c *.h *.dox
+GENERATE_HTML = YES
+HTML_OUTPUT = html
+GENERATE_MAN = YES
+ALWAYS_DETAILED_SEC = YES
+JAVADOC_AUTOBRIEF = YES
diff --git a/source4/lib/registry/README b/source4/lib/registry/README
new file mode 100644
index 0000000..07b2c01
--- /dev/null
+++ b/source4/lib/registry/README
@@ -0,0 +1,42 @@
+This is the registry library. The registry is basically a bunch of
+hives, each of which is loaded from a file. When using a local registry,
+it is possible to specify where hives should be loaded from, etc.
+
+There are separate APIs for accessing the data in a hive and the
+data in the registry itself. Each supports different backends.
+
+The following "full registry" backends are currently provided:
+
+ * Remote (over DCE/RPC)
+ * Local (allows "mounting" hives)
+ * Wine (uses the wine plain-text file)
+
+The following hive backends are supported:
+
+ - ldb
+ - regf (NTUSER.DAT-style files)
+ - rpc (Remote individual hives)
+ - directory
+
+reg_open_samba() loads a set of hives based on smb.conf settings.
+Lines in smb.conf should have the following syntax:
+
+registry:<hivename> = <backend>:<location>
+
+So an example usage could be:
+
+registry:HKEY_CURRENT_USER = regf:NTUSER.DAT
+registry:HKEY_LOCAL_MACHINE = ldb:tdb://registry.tdb
+
+WERR_NOT_SUPPORTED will be returned for all hives that haven't been set.
+
+On Windows the various registry hives are loaded from:
+
+HKEY_CURRENT_CONFIG: %SystemRoot%\System32\Config\System
+HKEY_CURRENT_USER: %Profile%\NTUser.dat
+HKEY_LOCAL_MACHINE\SAM: %SystemRoot%\System32\Config\Sam
+HKEY_LOCAL_MACHINE\Security: %SystemRoot%\System32\Config\Security
+HKEY_LOCAL_MACHINE\Software: %SystemRoot%\System32\Config\Software
+HKEY_LOCAL_MACHINE\System: %SystemRoot%\System32\Config\System
+HKEY_USERS\.DEFAULT: %SystemRoot%\System32\Config\Default
+HKEY_LOCAL_MACHINE\HARDWARE: is autogenerated
diff --git a/source4/lib/registry/TODO b/source4/lib/registry/TODO
new file mode 100644
index 0000000..b5809b8
--- /dev/null
+++ b/source4/lib/registry/TODO
@@ -0,0 +1,5 @@
+- ..\..\, \bla\blie support in regshell
+- finish rpc_server
+
+regshell:
+ - support for security descriptors
diff --git a/source4/lib/registry/hive.c b/source4/lib/registry/hive.c
new file mode 100644
index 0000000..1f0672d
--- /dev/null
+++ b/source4/lib/registry/hive.c
@@ -0,0 +1,176 @@
+
+/*
+ Unix SMB/CIFS implementation.
+ Registry hive interface
+ Copyright (C) Jelmer Vernooij 2003-2007.
+
+ 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "registry.h"
+#include "system/filesys.h"
+#include "param/param.h"
+
+/** Open a registry file/host/etc */
+_PUBLIC_ WERROR reg_open_hive(TALLOC_CTX *parent_ctx, const char *location,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct hive_key **root)
+{
+ int fd, num;
+ char peek[20];
+
+ fd = open(location, O_RDWR);
+ if (fd == -1) {
+ if (errno == ENOENT)
+ return WERR_FILE_NOT_FOUND;
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ num = read(fd, peek, 20);
+ close(fd);
+ if (num == -1) {
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ if (!strncmp(peek, "regf", 4)) {
+ return reg_open_regf_file(parent_ctx, location, root);
+ } else if (!strncmp(peek, "TDB file", 8)) {
+ return reg_open_ldb_file(parent_ctx, location, session_info,
+ credentials, ev_ctx, lp_ctx, root);
+ }
+
+ return WERR_FILE_NOT_FOUND;
+}
+
+_PUBLIC_ WERROR hive_key_get_info(TALLOC_CTX *mem_ctx,
+ const struct hive_key *key,
+ const char **classname, uint32_t *num_subkeys,
+ uint32_t *num_values,
+ NTTIME *last_change_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize)
+{
+ return key->ops->get_key_info(mem_ctx, key, classname, num_subkeys,
+ num_values, last_change_time,
+ max_subkeynamelen,
+ max_valnamelen, max_valbufsize);
+}
+
+_PUBLIC_ WERROR hive_key_add_name(TALLOC_CTX *ctx,
+ const struct hive_key *parent_key,
+ const char *name, const char *classname,
+ struct security_descriptor *desc,
+ struct hive_key **key)
+{
+ SMB_ASSERT(strchr(name, '\\') == NULL);
+
+ return parent_key->ops->add_key(ctx, parent_key, name, classname,
+ desc, key);
+}
+
+_PUBLIC_ WERROR hive_key_del(TALLOC_CTX *mem_ctx, const struct hive_key *key,
+ const char *name)
+{
+ return key->ops->del_key(mem_ctx, key, name);
+}
+
+_PUBLIC_ WERROR hive_get_key_by_name(TALLOC_CTX *mem_ctx,
+ const struct hive_key *key,
+ const char *name,
+ struct hive_key **subkey)
+{
+ return key->ops->get_key_by_name(mem_ctx, key, name, subkey);
+}
+
+WERROR hive_enum_key(TALLOC_CTX *mem_ctx,
+ const struct hive_key *key, uint32_t idx,
+ const char **name,
+ const char **classname,
+ NTTIME *last_mod_time)
+{
+ return key->ops->enum_key(mem_ctx, key, idx, name, classname,
+ last_mod_time);
+}
+
+WERROR hive_key_set_value(struct hive_key *key, const char *name, uint32_t type,
+ const DATA_BLOB data)
+{
+ if (key->ops->set_value == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->ops->set_value(key, name, type, data);
+}
+
+WERROR hive_get_value(TALLOC_CTX *mem_ctx,
+ struct hive_key *key, const char *name,
+ uint32_t *type, DATA_BLOB *data)
+{
+ if (key->ops->get_value_by_name == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->ops->get_value_by_name(mem_ctx, key, name, type, data);
+}
+
+WERROR hive_get_value_by_index(TALLOC_CTX *mem_ctx,
+ struct hive_key *key, uint32_t idx,
+ const char **name,
+ uint32_t *type, DATA_BLOB *data)
+{
+ if (key->ops->enum_value == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->ops->enum_value(mem_ctx, key, idx, name, type, data);
+}
+
+WERROR hive_get_sec_desc(TALLOC_CTX *mem_ctx,
+ struct hive_key *key,
+ struct security_descriptor **security)
+{
+ if (key->ops->get_sec_desc == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->ops->get_sec_desc(mem_ctx, key, security);
+}
+
+WERROR hive_set_sec_desc(struct hive_key *key,
+ const struct security_descriptor *security)
+{
+ if (key->ops->set_sec_desc == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->ops->set_sec_desc(key, security);
+}
+
+WERROR hive_key_del_value(TALLOC_CTX *mem_ctx, struct hive_key *key,
+ const char *name)
+{
+ if (key->ops->delete_value == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->ops->delete_value(mem_ctx, key, name);
+}
+
+WERROR hive_key_flush(struct hive_key *key)
+{
+ if (key->ops->flush_key == NULL)
+ return WERR_OK;
+
+ return key->ops->flush_key(key);
+}
diff --git a/source4/lib/registry/interface.c b/source4/lib/registry/interface.c
new file mode 100644
index 0000000..2900c10
--- /dev/null
+++ b/source4/lib/registry/interface.c
@@ -0,0 +1,298 @@
+/*
+ Unix SMB/CIFS implementation.
+ Transparent registry backend handling
+ Copyright (C) Jelmer Vernooij 2003-2007.
+
+ 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 "../lib/util/dlinklist.h"
+#include "lib/registry/registry.h"
+#include "system/filesys.h"
+
+#undef strcasecmp
+
+/**
+ * @file
+ * @brief Main registry functions
+ */
+
+const struct reg_predefined_key reg_predefined_keys[] = {
+ {HKEY_CLASSES_ROOT,"HKEY_CLASSES_ROOT" },
+ {HKEY_CURRENT_USER,"HKEY_CURRENT_USER" },
+ {HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE" },
+ {HKEY_PERFORMANCE_DATA, "HKEY_PERFORMANCE_DATA" },
+ {HKEY_USERS, "HKEY_USERS" },
+ {HKEY_CURRENT_CONFIG, "HKEY_CURRENT_CONFIG" },
+ {HKEY_DYN_DATA, "HKEY_DYN_DATA" },
+ {HKEY_PERFORMANCE_TEXT, "HKEY_PERFORMANCE_TEXT" },
+ {HKEY_PERFORMANCE_NLSTEXT, "HKEY_PERFORMANCE_NLSTEXT" },
+ { 0, NULL }
+};
+
+/** Obtain name of specific hkey. */
+_PUBLIC_ const char *reg_get_predef_name(uint32_t hkey)
+{
+ unsigned int i;
+ for (i = 0; reg_predefined_keys[i].name; i++) {
+ if (reg_predefined_keys[i].handle == hkey)
+ return reg_predefined_keys[i].name;
+ }
+
+ return NULL;
+}
+
+/** Get predefined key by name. */
+_PUBLIC_ WERROR reg_get_predefined_key_by_name(struct registry_context *ctx,
+ const char *name,
+ struct registry_key **key)
+{
+ unsigned int i;
+
+ for (i = 0; reg_predefined_keys[i].name; i++) {
+ if (!strcasecmp(reg_predefined_keys[i].name, name))
+ return reg_get_predefined_key(ctx,
+ reg_predefined_keys[i].handle,
+ key);
+ }
+
+ DEBUG(1, ("No predefined key with name '%s'\n", name));
+
+ return WERR_FILE_NOT_FOUND;
+}
+
+/** Get predefined key by id. */
+_PUBLIC_ WERROR reg_get_predefined_key(struct registry_context *ctx,
+ uint32_t hkey, struct registry_key **key)
+{
+ return ctx->ops->get_predefined_key(ctx, hkey, key);
+}
+
+/**
+ * Open a key
+ * First tries to use the open_key function from the backend
+ * then falls back to get_subkey_by_name and later get_subkey_by_index
+ */
+_PUBLIC_ WERROR reg_open_key(TALLOC_CTX *mem_ctx, struct registry_key *parent,
+ const char *name, struct registry_key **result)
+{
+ if (parent == NULL) {
+ DEBUG(0, ("Invalid parent key specified for open of '%s'\n",
+ name));
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (parent->context->ops->open_key == NULL) {
+ DEBUG(0, ("Registry backend doesn't have open_key!\n"));
+ return WERR_NOT_SUPPORTED;
+ }
+
+ return parent->context->ops->open_key(mem_ctx, parent, name, result);
+}
+
+/**
+ * Get value by index
+ */
+_PUBLIC_ WERROR reg_key_get_value_by_index(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ uint32_t idx, const char **name,
+ uint32_t *type, DATA_BLOB *data)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAMETER;
+
+ if (key->context->ops->enum_value == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->context->ops->enum_value(mem_ctx, key, idx, name,
+ type, data);
+}
+
+/**
+ * Get the number of subkeys.
+ */
+_PUBLIC_ WERROR reg_key_get_info(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char **classname,
+ uint32_t *num_subkeys,
+ uint32_t *num_values,
+ NTTIME *last_change_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAMETER;
+
+ if (key->context->ops->get_key_info == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->context->ops->get_key_info(mem_ctx,
+ key, classname, num_subkeys,
+ num_values, last_change_time,
+ max_subkeynamelen,
+ max_valnamelen, max_valbufsize);
+}
+
+/**
+ * Get subkey by index.
+ */
+_PUBLIC_ WERROR reg_key_get_subkey_by_index(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ uint32_t idx, const char **name,
+ const char **keyclass,
+ NTTIME *last_changed_time)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAMETER;
+
+ if (key->context->ops->enum_key == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->context->ops->enum_key(mem_ctx, key, idx, name,
+ keyclass, last_changed_time);
+}
+
+/**
+ * Get value by name.
+ */
+_PUBLIC_ WERROR reg_key_get_value_by_name(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char *name,
+ uint32_t *type,
+ DATA_BLOB *data)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAMETER;
+
+ if (key->context->ops->get_value == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->context->ops->get_value(mem_ctx, key, name, type, data);
+}
+
+/**
+ * Delete a key.
+ */
+_PUBLIC_ WERROR reg_key_del(TALLOC_CTX *mem_ctx, struct registry_key *parent,
+ const char *name)
+{
+ if (parent == NULL)
+ return WERR_INVALID_PARAMETER;
+
+ if (parent->context->ops->delete_key == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return parent->context->ops->delete_key(mem_ctx, parent, name);
+}
+
+/**
+ * Add a key.
+ */
+_PUBLIC_ WERROR reg_key_add_name(TALLOC_CTX *mem_ctx,
+ struct registry_key *parent,
+ const char *path, const char *key_class,
+ struct security_descriptor *desc,
+ struct registry_key **newkey)
+{
+ if (parent == NULL)
+ return WERR_INVALID_PARAMETER;
+
+ if (parent->context->ops->create_key == NULL) {
+ DEBUG(1, ("Backend '%s' doesn't support method add_key\n",
+ parent->context->ops->name));
+ return WERR_NOT_SUPPORTED;
+ }
+
+ return parent->context->ops->create_key(mem_ctx, parent, path,
+ key_class, desc, newkey);
+}
+
+/**
+ * Set a value.
+ */
+_PUBLIC_ WERROR reg_val_set(struct registry_key *key, const char *value,
+ uint32_t type, const DATA_BLOB data)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAMETER;
+
+ /* A 'real' set function has preference */
+ if (key->context->ops->set_value == NULL) {
+ DEBUG(1, ("Backend '%s' doesn't support method set_value\n",
+ key->context->ops->name));
+ return WERR_NOT_SUPPORTED;
+ }
+
+ return key->context->ops->set_value(key, value, type, data);
+}
+
+/**
+ * Get the security descriptor on a key.
+ */
+_PUBLIC_ WERROR reg_get_sec_desc(TALLOC_CTX *ctx,
+ const struct registry_key *key,
+ struct security_descriptor **secdesc)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAMETER;
+
+ /* A 'real' set function has preference */
+ if (key->context->ops->get_sec_desc == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->context->ops->get_sec_desc(ctx, key, secdesc);
+}
+
+/**
+ * Delete a value.
+ */
+_PUBLIC_ WERROR reg_del_value(TALLOC_CTX *mem_ctx, struct registry_key *key,
+ const char *valname)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAMETER;
+
+ if (key->context->ops->delete_value == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->context->ops->delete_value(mem_ctx, key, valname);
+}
+
+/**
+ * Flush a key to disk.
+ */
+_PUBLIC_ WERROR reg_key_flush(struct registry_key *key)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAMETER;
+
+ if (key->context->ops->flush_key == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->context->ops->flush_key(key);
+}
+
+_PUBLIC_ WERROR reg_set_sec_desc(struct registry_key *key,
+ const struct security_descriptor *security)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAMETER;
+
+ if (key->context->ops->set_sec_desc == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->context->ops->set_sec_desc(key, security);
+}
diff --git a/source4/lib/registry/ldb.c b/source4/lib/registry/ldb.c
new file mode 100644
index 0000000..db383a5
--- /dev/null
+++ b/source4/lib/registry/ldb.c
@@ -0,0 +1,1018 @@
+/*
+ Unix SMB/CIFS implementation.
+ Registry interface
+ Copyright (C) 2004-2007, Jelmer Vernooij, jelmer@samba.org
+ Copyright (C) 2008-2010, Matthias Dieter Wallnöfer, mdw@samba.org
+
+ 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 "registry.h"
+#include <ldb.h>
+#include <ldb_errors.h>
+#include "ldb_wrap.h"
+#include "librpc/gen_ndr/winreg.h"
+#include "param/param.h"
+#include "lib/util/smb_strtox.h"
+
+#undef strcasecmp
+
+static struct hive_operations reg_backend_ldb;
+
+struct ldb_key_data
+{
+ struct hive_key key;
+ struct ldb_context *ldb;
+ struct ldb_dn *dn;
+ struct ldb_message **subkeys, **values;
+ unsigned int subkey_count, value_count;
+ const char *classname;
+};
+
+static void reg_ldb_unpack_value(TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ const char **name, uint32_t *type,
+ DATA_BLOB *data)
+{
+ const struct ldb_val *val;
+ uint32_t value_type;
+
+ if (name != NULL) {
+ *name = talloc_strdup(mem_ctx,
+ ldb_msg_find_attr_as_string(msg, "value",
+ ""));
+ }
+
+ value_type = ldb_msg_find_attr_as_uint(msg, "type", 0);
+ *type = value_type;
+
+ val = ldb_msg_find_ldb_val(msg, "data");
+
+ switch (value_type)
+ {
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ if (val != NULL) {
+ /* The data should be provided as UTF16 string */
+ convert_string_talloc(mem_ctx, CH_UTF8, CH_UTF16,
+ val->data, val->length,
+ (void **)&data->data, &data->length);
+ } else {
+ data->data = NULL;
+ data->length = 0;
+ }
+ break;
+
+ case REG_DWORD:
+ case REG_DWORD_BIG_ENDIAN:
+ if (val != NULL) {
+ int error = 0;
+ /* The data is a plain DWORD */
+ uint32_t tmp;
+
+ tmp = smb_strtoul((char *)val->data,
+ NULL,
+ 0,
+ &error,
+ SMB_STR_STANDARD);
+ if (error != 0) {
+ data->data = NULL;
+ data->length = 0;
+ break;
+ }
+ data->data = talloc_size(mem_ctx, sizeof(uint32_t));
+ if (data->data != NULL) {
+ SIVAL(data->data, 0, tmp);
+ }
+ data->length = sizeof(uint32_t);
+ } else {
+ data->data = NULL;
+ data->length = 0;
+ }
+ break;
+
+ case REG_QWORD:
+ if (val != NULL) {
+ int error = 0;
+ /* The data is a plain QWORD */
+ uint64_t tmp;
+
+ tmp = smb_strtoull((char *)val->data,
+ NULL,
+ 0,
+ &error,
+ SMB_STR_STANDARD);
+ if (error != 0) {
+ data->data = NULL;
+ data->length = 0;
+ break;
+ }
+ data->data = talloc_size(mem_ctx, sizeof(uint64_t));
+ if (data->data != NULL) {
+ SBVAL(data->data, 0, tmp);
+ }
+ data->length = sizeof(uint64_t);
+ } else {
+ data->data = NULL;
+ data->length = 0;
+ }
+ break;
+
+ case REG_BINARY:
+ default:
+ if (val != NULL) {
+ data->data = talloc_memdup(mem_ctx, val->data,
+ val->length);
+ data->length = val->length;
+ } else {
+ data->data = NULL;
+ data->length = 0;
+ }
+ break;
+ }
+}
+
+static struct ldb_message *reg_ldb_pack_value(struct ldb_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *name,
+ uint32_t type, DATA_BLOB data)
+{
+ struct ldb_message *msg;
+ char *name_dup, *type_str;
+ int ret;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NULL;
+ }
+
+ name_dup = talloc_strdup(msg, name);
+ if (name_dup == NULL) {
+ talloc_free(msg);
+ return NULL;
+ }
+
+ ret = ldb_msg_add_string(msg, "value", name_dup);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(msg);
+ return NULL;
+ }
+
+ switch (type) {
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ if ((data.length > 0) && (data.data != NULL)) {
+ struct ldb_val *val;
+ bool ret2 = false;
+
+ val = talloc_zero(msg, struct ldb_val);
+ if (val == NULL) {
+ talloc_free(msg);
+ return NULL;
+ }
+
+ /* The data is provided as UTF16 string */
+ ret2 = convert_string_talloc(mem_ctx, CH_UTF16, CH_UTF8,
+ (void *)data.data, data.length,
+ (void **)&val->data, &val->length);
+ if (ret2) {
+ ret = ldb_msg_add_value(msg, "data", val, NULL);
+ } else {
+ /* workaround for non-standard data */
+ ret = ldb_msg_add_empty(msg, "data", LDB_FLAG_MOD_DELETE, NULL);
+ }
+ } else {
+ ret = ldb_msg_add_empty(msg, "data", LDB_FLAG_MOD_DELETE, NULL);
+ }
+ break;
+
+ case REG_DWORD:
+ case REG_DWORD_BIG_ENDIAN:
+ if ((data.length > 0) && (data.data != NULL)) {
+ if (data.length == sizeof(uint32_t)) {
+ char *conv_str;
+
+ conv_str = talloc_asprintf(msg, "0x%8.8x",
+ IVAL(data.data, 0));
+ if (conv_str == NULL) {
+ talloc_free(msg);
+ return NULL;
+ }
+ ret = ldb_msg_add_string(msg, "data", conv_str);
+ } else {
+ /* workaround for non-standard data */
+ talloc_free(msg);
+ return NULL;
+ }
+ } else {
+ ret = ldb_msg_add_empty(msg, "data", LDB_FLAG_MOD_DELETE, NULL);
+ }
+ break;
+
+ case REG_QWORD:
+ if ((data.length > 0) && (data.data != NULL)) {
+ if (data.length == sizeof(uint64_t)) {
+ char *conv_str;
+
+ conv_str = talloc_asprintf(msg, "0x%16.16llx",
+ (unsigned long long)BVAL(data.data, 0));
+ if (conv_str == NULL) {
+ talloc_free(msg);
+ return NULL;
+ }
+ ret = ldb_msg_add_string(msg, "data", conv_str);
+ } else {
+ /* workaround for non-standard data */
+ talloc_free(msg);
+ return NULL;
+
+ }
+ } else {
+ ret = ldb_msg_add_empty(msg, "data", LDB_FLAG_MOD_DELETE, NULL);
+ }
+ break;
+
+ case REG_BINARY:
+ default:
+ if ((data.length > 0) && (data.data != NULL)) {
+ ret = ldb_msg_add_value(msg, "data", &data, NULL);
+ } else {
+ ret = ldb_msg_add_empty(msg, "data", LDB_FLAG_MOD_DELETE, NULL);
+ }
+ break;
+ }
+
+ if (ret != LDB_SUCCESS) {
+ talloc_free(msg);
+ return NULL;
+ }
+
+ type_str = talloc_asprintf(mem_ctx, "%u", type);
+ if (type_str == NULL) {
+ talloc_free(msg);
+ return NULL;
+ }
+
+ ret = ldb_msg_add_string(msg, "type", type_str);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+static char *reg_ldb_escape(TALLOC_CTX *mem_ctx, const char *value)
+{
+ struct ldb_val val;
+
+ val.data = discard_const_p(uint8_t, value);
+ val.length = strlen(value);
+
+ return ldb_dn_escape_value(mem_ctx, val);
+}
+
+static int reg_close_ldb_key(struct ldb_key_data *key)
+{
+ if (key->subkeys != NULL) {
+ talloc_free(key->subkeys);
+ key->subkeys = NULL;
+ }
+
+ if (key->values != NULL) {
+ talloc_free(key->values);
+ key->values = NULL;
+ }
+ return 0;
+}
+
+static struct ldb_dn *reg_path_to_ldb(TALLOC_CTX *mem_ctx,
+ const struct hive_key *from,
+ const char *path, const char *add)
+{
+ struct ldb_dn *ret;
+ char *mypath;
+ char *begin;
+ struct ldb_key_data *kd = talloc_get_type(from, struct ldb_key_data);
+ struct ldb_context *ldb = kd->ldb;
+
+ mypath = talloc_strdup(mem_ctx, path);
+ if (mypath == NULL) {
+ return NULL;
+ }
+
+ ret = ldb_dn_new(mem_ctx, ldb, add);
+ if (!ldb_dn_validate(ret)) {
+ talloc_free(ret);
+ return NULL;
+ }
+
+ if (!ldb_dn_add_base(ret, kd->dn)) {
+ talloc_free(ret);
+ return NULL;
+ }
+
+ while (mypath[0] != '\0') {
+ begin = strchr(mypath, '\\');
+ if (begin != NULL) {
+ *begin = '\0';
+ }
+
+ if (!ldb_dn_add_child_fmt(ret, "key=%s",
+ reg_ldb_escape(mem_ctx, mypath))) {
+ talloc_free(ret);
+ return NULL;
+ }
+
+ if (begin != NULL) {
+ mypath = begin + 1;
+ } else {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static WERROR cache_subkeys(struct ldb_key_data *kd)
+{
+ struct ldb_context *c = kd->ldb;
+ struct ldb_result *res;
+ int ret;
+
+ ret = ldb_search(c, c, &res, kd->dn, LDB_SCOPE_ONELEVEL,
+ NULL, "(key=*)");
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, ("Error getting subkeys for '%s': %s\n",
+ ldb_dn_get_linearized(kd->dn), ldb_errstring(c)));
+ return WERR_FOOBAR;
+ }
+
+ kd->subkey_count = res->count;
+ kd->subkeys = talloc_steal(kd, res->msgs);
+ talloc_free(res);
+
+ return WERR_OK;
+}
+
+static WERROR cache_values(struct ldb_key_data *kd)
+{
+ struct ldb_context *c = kd->ldb;
+ struct ldb_result *res;
+ int ret;
+
+ ret = ldb_search(c, c, &res, kd->dn, LDB_SCOPE_ONELEVEL,
+ NULL, "(value=*)");
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, ("Error getting values for '%s': %s\n",
+ ldb_dn_get_linearized(kd->dn), ldb_errstring(c)));
+ return WERR_FOOBAR;
+ }
+
+ kd->value_count = res->count;
+ kd->values = talloc_steal(kd, res->msgs);
+ talloc_free(res);
+
+ return WERR_OK;
+}
+
+
+static WERROR ldb_get_subkey_by_id(TALLOC_CTX *mem_ctx,
+ const struct hive_key *k, uint32_t idx,
+ const char **name,
+ const char **classname,
+ NTTIME *last_mod_time)
+{
+ struct ldb_key_data *kd = talloc_get_type(k, struct ldb_key_data);
+
+ /* Initialization */
+ if (name != NULL)
+ *name = NULL;
+ if (classname != NULL)
+ *classname = NULL;
+ if (last_mod_time != NULL)
+ *last_mod_time = 0; /* TODO: we need to add this to the
+ ldb backend properly */
+
+ /* Do a search if necessary */
+ if (kd->subkeys == NULL) {
+ W_ERROR_NOT_OK_RETURN(cache_subkeys(kd));
+ }
+
+ if (idx >= kd->subkey_count)
+ return WERR_NO_MORE_ITEMS;
+
+ if (name != NULL)
+ *name = talloc_strdup(mem_ctx,
+ ldb_msg_find_attr_as_string(kd->subkeys[idx], "key", NULL));
+ if (classname != NULL)
+ *classname = talloc_strdup(mem_ctx,
+ ldb_msg_find_attr_as_string(kd->subkeys[idx], "classname", NULL));
+
+ return WERR_OK;
+}
+
+static WERROR ldb_get_default_value(TALLOC_CTX *mem_ctx,
+ const struct hive_key *k,
+ const char **name, uint32_t *data_type,
+ DATA_BLOB *data)
+{
+ struct ldb_key_data *kd = talloc_get_type(k, struct ldb_key_data);
+ struct ldb_context *c = kd->ldb;
+ const char* attrs[] = { "data", "type", NULL };
+ struct ldb_result *res;
+ int ret;
+
+ ret = ldb_search(c, mem_ctx, &res, kd->dn, LDB_SCOPE_BASE, attrs,
+ NULL);
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, ("Error getting default value for '%s': %s\n",
+ ldb_dn_get_linearized(kd->dn), ldb_errstring(c)));
+ return WERR_FOOBAR;
+ }
+
+ if (res->count == 0 || res->msgs[0]->num_elements == 0) {
+ talloc_free(res);
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ if ((data_type != NULL) && (data != NULL)) {
+ reg_ldb_unpack_value(mem_ctx, res->msgs[0], name, data_type,
+ data);
+ }
+
+ talloc_free(res);
+
+ return WERR_OK;
+}
+
+static WERROR ldb_get_value_by_id(TALLOC_CTX *mem_ctx, struct hive_key *k,
+ uint32_t idx, const char **name,
+ uint32_t *data_type, DATA_BLOB *data)
+{
+ struct ldb_key_data *kd = talloc_get_type(k, struct ldb_key_data);
+
+ /* if the default value exists, give it back */
+ if (W_ERROR_IS_OK(ldb_get_default_value(mem_ctx, k, name, data_type,
+ data))) {
+ if (idx == 0)
+ return WERR_OK;
+ else
+ --idx;
+ }
+
+ /* Do the search if necessary */
+ if (kd->values == NULL) {
+ W_ERROR_NOT_OK_RETURN(cache_values(kd));
+ }
+
+ if (idx >= kd->value_count)
+ return WERR_NO_MORE_ITEMS;
+
+ reg_ldb_unpack_value(mem_ctx, kd->values[idx], name, data_type, data);
+
+ return WERR_OK;
+}
+
+static WERROR ldb_get_value(TALLOC_CTX *mem_ctx, struct hive_key *k,
+ const char *name, uint32_t *data_type,
+ DATA_BLOB *data)
+{
+ struct ldb_key_data *kd = talloc_get_type(k, struct ldb_key_data);
+ const char *res_name;
+ uint32_t idx;
+
+ /* the default value was requested, give it back */
+ if (name[0] == '\0') {
+ return ldb_get_default_value(mem_ctx, k, NULL, data_type, data);
+ }
+
+ /* Do the search if necessary */
+ if (kd->values == NULL) {
+ W_ERROR_NOT_OK_RETURN(cache_values(kd));
+ }
+
+ for (idx = 0; idx < kd->value_count; idx++) {
+ res_name = ldb_msg_find_attr_as_string(kd->values[idx], "value",
+ "");
+ if (ldb_attr_cmp(name, res_name) == 0) {
+ reg_ldb_unpack_value(mem_ctx, kd->values[idx], NULL,
+ data_type, data);
+ return WERR_OK;
+ }
+ }
+
+ return WERR_FILE_NOT_FOUND;
+}
+
+static WERROR ldb_open_key(TALLOC_CTX *mem_ctx, const struct hive_key *h,
+ const char *name, struct hive_key **key)
+{
+ struct ldb_result *res;
+ struct ldb_dn *ldb_path;
+ int ret;
+ struct ldb_key_data *newkd;
+ struct ldb_key_data *kd = talloc_get_type(h, struct ldb_key_data);
+ struct ldb_context *c = kd->ldb;
+
+ ldb_path = reg_path_to_ldb(mem_ctx, h, name, NULL);
+ W_ERROR_HAVE_NO_MEMORY(ldb_path);
+
+ ret = ldb_search(c, mem_ctx, &res, ldb_path, LDB_SCOPE_BASE, NULL,
+ NULL);
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(3, ("Error opening key '%s': %s\n",
+ ldb_dn_get_linearized(ldb_path), ldb_errstring(c)));
+ return WERR_FOOBAR;
+ } else if (res->count == 0) {
+ DEBUG(3, ("Key '%s' not found\n",
+ ldb_dn_get_linearized(ldb_path)));
+ talloc_free(res);
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ newkd = talloc_zero(mem_ctx, struct ldb_key_data);
+ W_ERROR_HAVE_NO_MEMORY(newkd);
+ newkd->key.ops = &reg_backend_ldb;
+ newkd->ldb = talloc_reference(newkd, kd->ldb);
+ newkd->dn = ldb_dn_copy(newkd, res->msgs[0]->dn);
+ newkd->classname = talloc_steal(newkd,
+ ldb_msg_find_attr_as_string(res->msgs[0], "classname", NULL));
+
+ talloc_free(res);
+
+ *key = (struct hive_key *)newkd;
+
+ return WERR_OK;
+}
+
+WERROR reg_open_ldb_file(TALLOC_CTX *parent_ctx, const char *location,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct hive_key **k)
+{
+ struct ldb_key_data *kd;
+ struct ldb_context *wrap;
+ struct ldb_message *attrs_msg;
+
+ if (location == NULL)
+ return WERR_INVALID_PARAMETER;
+
+ wrap = ldb_wrap_connect(parent_ctx, ev_ctx, lp_ctx,
+ location, session_info, credentials, 0);
+
+ if (wrap == NULL) {
+ DEBUG(1, (__FILE__": unable to connect\n"));
+ return WERR_FOOBAR;
+ }
+
+ attrs_msg = ldb_msg_new(wrap);
+ W_ERROR_HAVE_NO_MEMORY(attrs_msg);
+ attrs_msg->dn = ldb_dn_new(attrs_msg, wrap, "@ATTRIBUTES");
+ W_ERROR_HAVE_NO_MEMORY(attrs_msg->dn);
+ ldb_msg_add_string(attrs_msg, "key", "CASE_INSENSITIVE");
+ ldb_msg_add_string(attrs_msg, "value", "CASE_INSENSITIVE");
+
+ ldb_add(wrap, attrs_msg);
+
+ ldb_set_debug_stderr(wrap);
+
+ kd = talloc_zero(parent_ctx, struct ldb_key_data);
+ kd->key.ops = &reg_backend_ldb;
+ kd->ldb = talloc_reference(kd, wrap);
+ talloc_set_destructor (kd, reg_close_ldb_key);
+ kd->dn = ldb_dn_new(kd, wrap, "hive=NONE");
+
+ *k = (struct hive_key *)kd;
+
+ return WERR_OK;
+}
+
+static WERROR ldb_add_key(TALLOC_CTX *mem_ctx, const struct hive_key *parent,
+ const char *name, const char *classname,
+ struct security_descriptor *sd,
+ struct hive_key **newkey)
+{
+ struct ldb_key_data *parentkd = discard_const_p(struct ldb_key_data, parent);
+ struct ldb_dn *ldb_path;
+ struct ldb_message *msg;
+ struct ldb_key_data *newkd;
+ int ret;
+
+ ldb_path = reg_path_to_ldb(mem_ctx, parent, name, NULL);
+ W_ERROR_HAVE_NO_MEMORY(ldb_path);
+
+ msg = ldb_msg_new(mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(msg);
+
+ msg->dn = ldb_path;
+
+ ldb_msg_add_string(msg, "key", name);
+ if (classname != NULL) {
+ ldb_msg_add_string(msg, "classname", classname);
+ }
+
+ ret = ldb_add(parentkd->ldb, msg);
+
+ talloc_free(msg);
+
+ if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
+ return WERR_ALREADY_EXISTS;
+ }
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("ldb_add: %s\n", ldb_errstring(parentkd->ldb)));
+ return WERR_FOOBAR;
+ }
+
+ DEBUG(2, ("key added: %s\n", ldb_dn_get_linearized(ldb_path)));
+
+ newkd = talloc_zero(mem_ctx, struct ldb_key_data);
+ W_ERROR_HAVE_NO_MEMORY(newkd);
+ newkd->ldb = talloc_reference(newkd, parentkd->ldb);
+ newkd->key.ops = &reg_backend_ldb;
+ newkd->dn = talloc_steal(newkd, ldb_path);
+ newkd->classname = talloc_steal(newkd, classname);
+
+ *newkey = (struct hive_key *)newkd;
+
+ /* reset cache */
+ talloc_free(parentkd->subkeys);
+ parentkd->subkeys = NULL;
+
+ return WERR_OK;
+}
+
+static WERROR ldb_del_value(TALLOC_CTX *mem_ctx, struct hive_key *key,
+ const char *child)
+{
+ int ret;
+ struct ldb_key_data *kd = talloc_get_type(key, struct ldb_key_data);
+ struct ldb_message *msg;
+ struct ldb_dn *childdn;
+
+ if (child[0] == '\0') {
+ /* default value */
+ msg = ldb_msg_new(mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(msg);
+ msg->dn = ldb_dn_copy(msg, kd->dn);
+ W_ERROR_HAVE_NO_MEMORY(msg->dn);
+ ret = ldb_msg_add_empty(msg, "data", LDB_FLAG_MOD_DELETE, NULL);
+ if (ret != LDB_SUCCESS) {
+ return WERR_FOOBAR;
+ }
+ ret = ldb_msg_add_empty(msg, "type", LDB_FLAG_MOD_DELETE,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ return WERR_FOOBAR;
+ }
+
+ ret = ldb_modify(kd->ldb, msg);
+
+ talloc_free(msg);
+
+ if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
+ return WERR_FILE_NOT_FOUND;
+ } else if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("ldb_del_value: %s\n", ldb_errstring(kd->ldb)));
+ return WERR_FOOBAR;
+ }
+ } else {
+ /* normal value */
+ childdn = ldb_dn_copy(kd->ldb, kd->dn);
+ if (!ldb_dn_add_child_fmt(childdn, "value=%s",
+ reg_ldb_escape(childdn, child)))
+ {
+ talloc_free(childdn);
+ return WERR_FOOBAR;
+ }
+
+ ret = ldb_delete(kd->ldb, childdn);
+
+ talloc_free(childdn);
+
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ return WERR_FILE_NOT_FOUND;
+ } else if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("ldb_del_value: %s\n", ldb_errstring(kd->ldb)));
+ return WERR_FOOBAR;
+ }
+ }
+
+ /* reset cache */
+ talloc_free(kd->values);
+ kd->values = NULL;
+
+ return WERR_OK;
+}
+
+static WERROR ldb_del_key(TALLOC_CTX *mem_ctx, const struct hive_key *key,
+ const char *name)
+{
+ unsigned int i;
+ int ret;
+ struct ldb_key_data *parentkd = talloc_get_type(key, struct ldb_key_data);
+ struct ldb_dn *ldb_path;
+ struct ldb_context *c = parentkd->ldb;
+ struct ldb_result *res_keys;
+ struct ldb_result *res_vals;
+ WERROR werr;
+ struct hive_key *hk;
+
+ /* Verify key exists by opening it */
+ werr = ldb_open_key(mem_ctx, key, name, &hk);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ ldb_path = reg_path_to_ldb(mem_ctx, key, name, NULL);
+ W_ERROR_HAVE_NO_MEMORY(ldb_path);
+
+ /* Search for subkeys */
+ ret = ldb_search(c, mem_ctx, &res_keys, ldb_path, LDB_SCOPE_ONELEVEL,
+ NULL, "(key=*)");
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, ("Error getting subkeys for '%s': %s\n",
+ ldb_dn_get_linearized(ldb_path), ldb_errstring(c)));
+ return WERR_FOOBAR;
+ }
+
+ /* Search for values */
+ ret = ldb_search(c, mem_ctx, &res_vals, ldb_path, LDB_SCOPE_ONELEVEL,
+ NULL, "(value=*)");
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, ("Error getting values for '%s': %s\n",
+ ldb_dn_get_linearized(ldb_path), ldb_errstring(c)));
+ return WERR_FOOBAR;
+ }
+
+ /* Start an explicit transaction */
+ ret = ldb_transaction_start(c);
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, ("ldb_transaction_start: %s\n", ldb_errstring(c)));
+ return WERR_FOOBAR;
+ }
+
+ if (res_keys->count || res_vals->count)
+ {
+ /* Delete any subkeys */
+ for (i = 0; i < res_keys->count; i++)
+ {
+ werr = ldb_del_key(mem_ctx, hk,
+ ldb_msg_find_attr_as_string(
+ res_keys->msgs[i],
+ "key", NULL));
+ if (!W_ERROR_IS_OK(werr)) {
+ ret = ldb_transaction_cancel(c);
+ return werr;
+ }
+ }
+
+ /* Delete any values */
+ for (i = 0; i < res_vals->count; i++)
+ {
+ werr = ldb_del_value(mem_ctx, hk,
+ ldb_msg_find_attr_as_string(
+ res_vals->msgs[i],
+ "value", NULL));
+ if (!W_ERROR_IS_OK(werr)) {
+ ret = ldb_transaction_cancel(c);
+ return werr;
+ }
+ }
+ }
+ talloc_free(res_keys);
+ talloc_free(res_vals);
+
+ /* Delete the key itself */
+ ret = ldb_delete(c, ldb_path);
+
+ if (ret != LDB_SUCCESS)
+ {
+ DEBUG(1, ("ldb_del_key: %s\n", ldb_errstring(c)));
+ ret = ldb_transaction_cancel(c);
+ return WERR_FOOBAR;
+ }
+
+ /* Commit the transaction */
+ ret = ldb_transaction_commit(c);
+
+ if (ret != LDB_SUCCESS)
+ {
+ DEBUG(0, ("ldb_transaction_commit: %s\n", ldb_errstring(c)));
+ ret = ldb_transaction_cancel(c);
+ return WERR_FOOBAR;
+ }
+
+ /* reset cache */
+ talloc_free(parentkd->subkeys);
+ parentkd->subkeys = NULL;
+
+ return WERR_OK;
+}
+
+static WERROR ldb_set_value(struct hive_key *parent,
+ const char *name, uint32_t type,
+ const DATA_BLOB data)
+{
+ struct ldb_message *msg;
+ struct ldb_key_data *kd = talloc_get_type(parent, struct ldb_key_data);
+ unsigned int i;
+ int ret;
+ TALLOC_CTX *mem_ctx = talloc_init("ldb_set_value");
+
+ msg = reg_ldb_pack_value(kd->ldb, mem_ctx, name, type, data);
+ W_ERROR_HAVE_NO_MEMORY(msg);
+
+ msg->dn = ldb_dn_copy(msg, kd->dn);
+ W_ERROR_HAVE_NO_MEMORY(msg->dn);
+
+ if (name[0] != '\0') {
+ /* For a default value, we add/overwrite the attributes to/of the hive.
+ For a normal value, we create a new child. */
+ if (!ldb_dn_add_child_fmt(msg->dn, "value=%s",
+ reg_ldb_escape(mem_ctx, name)))
+ {
+ talloc_free(mem_ctx);
+ return WERR_FOOBAR;
+ }
+ }
+
+ /* Try first a "modify" and if this doesn't work do try an "add" */
+ for (i = 0; i < msg->num_elements; i++) {
+ if (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) != LDB_FLAG_MOD_DELETE) {
+ msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ }
+ }
+ ret = ldb_modify(kd->ldb, msg);
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ i = 0;
+ while (i < msg->num_elements) {
+ if (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) == LDB_FLAG_MOD_DELETE) {
+ ldb_msg_remove_element(msg, &msg->elements[i]);
+ } else {
+ ++i;
+ }
+ }
+ ret = ldb_add(kd->ldb, msg);
+ }
+ if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
+ /* ignore this -> the value didn't exist and also now doesn't */
+ ret = LDB_SUCCESS;
+ }
+
+ talloc_free(msg);
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("ldb_set_value: %s\n", ldb_errstring(kd->ldb)));
+ talloc_free(mem_ctx);
+ return WERR_FOOBAR;
+ }
+
+ /* reset cache */
+ talloc_free(kd->values);
+ kd->values = NULL;
+
+ talloc_free(mem_ctx);
+ return WERR_OK;
+}
+
+static WERROR ldb_get_key_info(TALLOC_CTX *mem_ctx,
+ const struct hive_key *key,
+ const char **classname,
+ uint32_t *num_subkeys,
+ uint32_t *num_values,
+ NTTIME *last_change_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize)
+{
+ struct ldb_key_data *kd = talloc_get_type(key, struct ldb_key_data);
+ uint32_t default_value_type = REG_NONE;
+ DATA_BLOB default_value = { NULL, 0 };
+ WERROR werr;
+
+ /* Initialization */
+ if (classname != NULL)
+ *classname = NULL;
+ if (num_subkeys != NULL)
+ *num_subkeys = 0;
+ if (num_values != NULL)
+ *num_values = 0;
+ if (last_change_time != NULL)
+ *last_change_time = 0;
+ if (max_subkeynamelen != NULL)
+ *max_subkeynamelen = 0;
+ if (max_valnamelen != NULL)
+ *max_valnamelen = 0;
+ if (max_valbufsize != NULL)
+ *max_valbufsize = 0;
+
+ /* We need this to get the default value (if it exists) for counting
+ * the values under the key and for finding out the longest value buffer
+ * size. If no default value exists the DATA_BLOB "default_value" will
+ * remain { NULL, 0 }. */
+ werr = ldb_get_default_value(mem_ctx, key, NULL, &default_value_type,
+ &default_value);
+ if ((!W_ERROR_IS_OK(werr)) && (!W_ERROR_EQUAL(werr, WERR_FILE_NOT_FOUND))) {
+ return werr;
+ }
+
+ if (kd->subkeys == NULL) {
+ W_ERROR_NOT_OK_RETURN(cache_subkeys(kd));
+ }
+ if (kd->values == NULL) {
+ W_ERROR_NOT_OK_RETURN(cache_values(kd));
+ }
+
+ if (classname != NULL) {
+ *classname = kd->classname;
+ }
+
+ if (num_subkeys != NULL) {
+ *num_subkeys = kd->subkey_count;
+ }
+ if (num_values != NULL) {
+ *num_values = kd->value_count;
+ /* also consider the default value if it exists */
+ if (default_value.data != NULL) {
+ ++(*num_values);
+ }
+ }
+
+
+ if (max_subkeynamelen != NULL) {
+ unsigned int i;
+ struct ldb_message_element *el;
+
+ for (i = 0; i < kd->subkey_count; i++) {
+ el = ldb_msg_find_element(kd->subkeys[i], "key");
+ *max_subkeynamelen = MAX(*max_subkeynamelen, el->values[0].length);
+ }
+ }
+
+ if (max_valnamelen != NULL || max_valbufsize != NULL) {
+ unsigned int i;
+ struct ldb_message_element *el;
+ W_ERROR_NOT_OK_RETURN(cache_values(kd));
+
+ /* also consider the default value if it exists */
+ if ((max_valbufsize != NULL) && (default_value.data != NULL)) {
+ *max_valbufsize = MAX(*max_valbufsize,
+ default_value.length);
+ }
+
+ for (i = 0; i < kd->value_count; i++) {
+ if (max_valnamelen != NULL) {
+ el = ldb_msg_find_element(kd->values[i], "value");
+ *max_valnamelen = MAX(*max_valnamelen, el->values[0].length);
+ }
+
+ if (max_valbufsize != NULL) {
+ uint32_t data_type;
+ DATA_BLOB data;
+ reg_ldb_unpack_value(mem_ctx,
+ kd->values[i], NULL,
+ &data_type, &data);
+ *max_valbufsize = MAX(*max_valbufsize, data.length);
+ talloc_free(data.data);
+ }
+ }
+ }
+
+ talloc_free(default_value.data);
+
+ return WERR_OK;
+}
+
+static struct hive_operations reg_backend_ldb = {
+ .name = "ldb",
+ .add_key = ldb_add_key,
+ .del_key = ldb_del_key,
+ .get_key_by_name = ldb_open_key,
+ .enum_value = ldb_get_value_by_id,
+ .enum_key = ldb_get_subkey_by_id,
+ .set_value = ldb_set_value,
+ .get_value_by_name = ldb_get_value,
+ .delete_value = ldb_del_value,
+ .get_key_info = ldb_get_key_info,
+};
diff --git a/source4/lib/registry/local.c b/source4/lib/registry/local.c
new file mode 100644
index 0000000..4b00953
--- /dev/null
+++ b/source4/lib/registry/local.c
@@ -0,0 +1,408 @@
+/*
+ Unix SMB/CIFS implementation.
+ Transparent registry backend handling
+ Copyright (C) Jelmer Vernooij 2003-2007.
+
+ 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/registry/registry.h"
+#include "system/filesys.h"
+
+struct reg_key_path {
+ uint32_t predefined_key;
+ const char **elements;
+};
+
+struct registry_local {
+ const struct registry_operations *ops;
+
+ struct mountpoint {
+ struct reg_key_path path;
+ struct hive_key *key;
+ struct mountpoint *prev, *next;
+ } *mountpoints;
+};
+
+struct local_key {
+ struct registry_key global;
+ struct reg_key_path path;
+ struct hive_key *hive_key;
+};
+
+
+struct registry_key *reg_import_hive_key(struct registry_context *ctx,
+ struct hive_key *hive,
+ uint32_t predefined_key,
+ const char **elements)
+{
+ struct local_key *local_key;
+ struct reg_key_path parent_path;
+
+ parent_path.predefined_key = predefined_key;
+ parent_path.elements = elements;
+
+ local_key = talloc(ctx, struct local_key);
+ if (local_key != NULL) {
+ local_key->hive_key = talloc_reference(local_key, hive);
+ local_key->global.context = talloc_reference(local_key, ctx);
+ local_key->path = parent_path;
+ }
+
+ return (struct registry_key *)local_key;
+}
+
+
+static WERROR local_open_key(TALLOC_CTX *mem_ctx,
+ struct registry_key *parent,
+ const char *path,
+ struct registry_key **result)
+{
+ char *orig, *curbegin, *curend;
+ struct local_key *local_parent = talloc_get_type(parent,
+ struct local_key);
+ struct hive_key *curkey = local_parent->hive_key;
+ WERROR error;
+ const char **elements = NULL;
+ int el;
+
+ if (path == NULL || path[0] == '\0') {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ orig = talloc_strdup(mem_ctx, path);
+ W_ERROR_HAVE_NO_MEMORY(orig);
+ curbegin = orig;
+ curend = strchr(orig, '\\');
+
+ if (local_parent->path.elements != NULL) {
+ elements = talloc_array(mem_ctx, const char *,
+ str_list_length(local_parent->path.elements) + 1);
+ W_ERROR_HAVE_NO_MEMORY(elements);
+ for (el = 0; local_parent->path.elements[el] != NULL; el++) {
+ elements[el] = talloc_reference(elements,
+ local_parent->path.elements[el]);
+ }
+ elements[el] = NULL;
+ } else {
+ elements = NULL;
+ el = 0;
+ }
+
+ do {
+ if (curend != NULL)
+ *curend = '\0';
+ elements = talloc_realloc(mem_ctx, elements, const char *, el+2);
+ W_ERROR_HAVE_NO_MEMORY(elements);
+ elements[el] = talloc_strdup(elements, curbegin);
+ W_ERROR_HAVE_NO_MEMORY(elements[el]);
+ el++;
+ elements[el] = NULL;
+ error = hive_get_key_by_name(mem_ctx, curkey,
+ curbegin, &curkey);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(2, ("Opening key %s failed: %s\n", curbegin,
+ win_errstr(error)));
+ talloc_free(orig);
+ return error;
+ }
+ if (curend == NULL)
+ break;
+ curbegin = curend + 1;
+ curend = strchr(curbegin, '\\');
+ } while (curbegin[0] != '\0');
+ talloc_free(orig);
+
+ *result = reg_import_hive_key(local_parent->global.context, curkey,
+ local_parent->path.predefined_key,
+ talloc_steal(curkey, elements));
+
+ return WERR_OK;
+}
+
+WERROR local_get_predefined_key(struct registry_context *ctx,
+ uint32_t key_id, struct registry_key **key)
+{
+ struct registry_local *rctx = talloc_get_type(ctx,
+ struct registry_local);
+ struct mountpoint *mp;
+
+ for (mp = rctx->mountpoints; mp != NULL; mp = mp->next) {
+ if (mp->path.predefined_key == key_id &&
+ mp->path.elements == NULL)
+ break;
+ }
+
+ if (mp == NULL)
+ return WERR_FILE_NOT_FOUND;
+
+ *key = reg_import_hive_key(ctx, mp->key,
+ mp->path.predefined_key,
+ mp->path.elements);
+
+ return WERR_OK;
+}
+
+static WERROR local_enum_key(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key, uint32_t idx,
+ const char **name,
+ const char **keyclass,
+ NTTIME *last_changed_time)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ return hive_enum_key(mem_ctx, local->hive_key, idx, name, keyclass,
+ last_changed_time);
+}
+
+static WERROR local_create_key(TALLOC_CTX *mem_ctx,
+ struct registry_key *parent,
+ const char *path,
+ const char *key_class,
+ struct security_descriptor *security,
+ struct registry_key **result)
+{
+ char *orig, *curbegin, *curend;
+ struct local_key *local_parent = talloc_get_type(parent,
+ struct local_key);
+ struct hive_key *curkey = local_parent->hive_key;
+ WERROR error;
+ const char **elements = NULL;
+ int el;
+
+ if (path == NULL || path[0] == '\0') {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ orig = talloc_strdup(mem_ctx, path);
+ W_ERROR_HAVE_NO_MEMORY(orig);
+ curbegin = orig;
+ curend = strchr(orig, '\\');
+
+ if (local_parent->path.elements != NULL) {
+ elements = talloc_array(mem_ctx, const char *,
+ str_list_length(local_parent->path.elements) + 1);
+ W_ERROR_HAVE_NO_MEMORY(elements);
+ for (el = 0; local_parent->path.elements[el] != NULL; el++) {
+ elements[el] = talloc_reference(elements,
+ local_parent->path.elements[el]);
+ }
+ elements[el] = NULL;
+ } else {
+ elements = NULL;
+ el = 0;
+ }
+
+ do {
+ if (curend != NULL)
+ *curend = '\0';
+ elements = talloc_realloc(mem_ctx, elements, const char *, el+2);
+ W_ERROR_HAVE_NO_MEMORY(elements);
+ elements[el] = talloc_strdup(elements, curbegin);
+ W_ERROR_HAVE_NO_MEMORY(elements[el]);
+ el++;
+ elements[el] = NULL;
+ error = hive_get_key_by_name(mem_ctx, curkey,
+ curbegin, &curkey);
+ if (W_ERROR_EQUAL(error, WERR_FILE_NOT_FOUND)) {
+ error = hive_key_add_name(mem_ctx, curkey, curbegin,
+ key_class, security,
+ &curkey);
+ }
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(2, ("Open/Creation of key %s failed: %s\n",
+ curbegin, win_errstr(error)));
+ talloc_free(orig);
+ return error;
+ }
+ if (curend == NULL)
+ break;
+ curbegin = curend + 1;
+ curend = strchr(curbegin, '\\');
+ } while (curbegin[0] != '\0');
+ talloc_free(orig);
+
+ *result = reg_import_hive_key(local_parent->global.context, curkey,
+ local_parent->path.predefined_key,
+ talloc_steal(curkey, elements));
+
+ return WERR_OK;
+}
+
+static WERROR local_set_value(struct registry_key *key, const char *name,
+ uint32_t type, const DATA_BLOB data)
+{
+ struct local_key *local = (struct local_key *)key;
+
+ if (name == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ return hive_key_set_value(local->hive_key, name, type, data);
+}
+
+static WERROR local_get_value(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char *name, uint32_t *type, DATA_BLOB *data)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ if (name == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ return hive_get_value(mem_ctx, local->hive_key, name, type, data);
+}
+
+static WERROR local_enum_value(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key, uint32_t idx,
+ const char **name,
+ uint32_t *type,
+ DATA_BLOB *data)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ return hive_get_value_by_index(mem_ctx, local->hive_key, idx,
+ name, type, data);
+}
+
+static WERROR local_delete_key(TALLOC_CTX *mem_ctx, struct registry_key *key,
+ const char *name)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ if (name == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ return hive_key_del(mem_ctx, local->hive_key, name);
+}
+
+static WERROR local_delete_value(TALLOC_CTX *mem_ctx, struct registry_key *key,
+ const char *name)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ if (name == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ return hive_key_del_value(mem_ctx, local->hive_key, name);
+}
+
+static WERROR local_flush_key(struct registry_key *key)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ return hive_key_flush(local->hive_key);
+}
+
+static WERROR local_get_key_info(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char **classname,
+ uint32_t *num_subkeys,
+ uint32_t *num_values,
+ NTTIME *last_change_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ return hive_key_get_info(mem_ctx, local->hive_key,
+ classname, num_subkeys, num_values,
+ last_change_time, max_subkeynamelen,
+ max_valnamelen, max_valbufsize);
+}
+static WERROR local_get_sec_desc(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ struct security_descriptor **security)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ return hive_get_sec_desc(mem_ctx, local->hive_key, security);
+}
+static WERROR local_set_sec_desc(struct registry_key *key,
+ const struct security_descriptor *security)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ return hive_set_sec_desc(local->hive_key, security);
+}
+const static struct registry_operations local_ops = {
+ .name = "local",
+ .open_key = local_open_key,
+ .get_predefined_key = local_get_predefined_key,
+ .enum_key = local_enum_key,
+ .create_key = local_create_key,
+ .set_value = local_set_value,
+ .get_value = local_get_value,
+ .enum_value = local_enum_value,
+ .delete_key = local_delete_key,
+ .delete_value = local_delete_value,
+ .flush_key = local_flush_key,
+ .get_key_info = local_get_key_info,
+ .get_sec_desc = local_get_sec_desc,
+ .set_sec_desc = local_set_sec_desc,
+};
+
+WERROR reg_open_local(TALLOC_CTX *mem_ctx, struct registry_context **ctx)
+{
+ struct registry_local *ret = talloc_zero(mem_ctx,
+ struct registry_local);
+
+ W_ERROR_HAVE_NO_MEMORY(ret);
+
+ ret->ops = &local_ops;
+
+ *ctx = (struct registry_context *)ret;
+
+ return WERR_OK;
+}
+
+WERROR reg_mount_hive(struct registry_context *rctx,
+ struct hive_key *hive_key,
+ uint32_t key_id,
+ const char **elements)
+{
+ struct registry_local *reg_local = talloc_get_type(rctx,
+ struct registry_local);
+ struct mountpoint *mp;
+ unsigned int i = 0;
+
+ mp = talloc(rctx, struct mountpoint);
+ W_ERROR_HAVE_NO_MEMORY(mp);
+ mp->path.predefined_key = key_id;
+ mp->prev = mp->next = NULL;
+ mp->key = hive_key;
+ if (elements != NULL && elements[0] != NULL) {
+ mp->path.elements = talloc_array(mp, const char *,
+ str_list_length(elements));
+ W_ERROR_HAVE_NO_MEMORY(mp->path.elements);
+ for (i = 0; elements[i] != NULL; i++) {
+ mp->path.elements[i] = talloc_reference(mp->path.elements,
+ elements[i]);
+ }
+ mp->path.elements[i] = NULL;
+ } else {
+ mp->path.elements = NULL;
+ }
+
+ DLIST_ADD(reg_local->mountpoints, mp);
+
+ return WERR_OK;
+}
diff --git a/source4/lib/registry/man/regdiff.1.xml b/source4/lib/registry/man/regdiff.1.xml
new file mode 100644
index 0000000..23aae34
--- /dev/null
+++ b/source4/lib/registry/man/regdiff.1.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="regdiff.1">
+
+<refmeta>
+ <refentrytitle>regdiff</refentrytitle>
+ <manvolnum>1</manvolnum>
+ <refmiscinfo class="source">Samba</refmiscinfo>
+ <refmiscinfo class="manual">System Administration tools</refmiscinfo>
+ <refmiscinfo class="version">4.0</refmiscinfo>
+</refmeta>
+
+
+<refnamediv>
+ <refname>regdiff</refname>
+ <refpurpose>Diff program for Windows registry files</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>regdiff</command>
+ <arg choice="opt">--help</arg>
+ <arg choice="opt">--backend=BACKEND</arg>
+ <arg choice="opt">--credentials=CREDENTIALS</arg>
+ <arg choice="opt">location</arg>
+ </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>regdiff compares two Windows registry files key by key
+ and value by value and generates a text file that contains the
+ differences between the two files.</para>
+
+ <para>A file generated by regdiff can later be applied to a
+ registry file by the regpatch utility. </para>
+
+ <para>regdiff and regpatch use the same file format as
+ the regedit32.exe utility from Windows.</para>
+
+</refsect1>
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>--help</term>
+ <listitem><para>
+ Show list of available options.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--backend BACKEND</term>
+ <listitem><para>Name of backend to load. Possible values are:
+ creg, regf, dir and rpc. The default is <emphasis>dir</emphasis>.
+ </para>
+ <para>
+ This argument can be specified twice: once for the first
+ registry file and once for the second.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--credentials=CREDENTIALS</term>
+ <listitem><para>
+ Credentials to use, if any. Password should be separated from user name by a percent sign.
+ </para>
+ <para>
+ This argument can be specified twice: once for the first
+ registry file and once for the second.
+ </para></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>SEE ALSO</title>
+
+ <para>gregedit, regshell, regpatch, regtree, samba, patch, diff</para>
+
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para>This utility is part of the <ulink url="http://www.samba.org/">Samba</ulink> suite, which is developed by the global <ulink url="http://www.samba.org/samba/team/">Samba Team</ulink>.</para>
+
+ <para>This manpage and regdiff were written by Jelmer Vernooij. </para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/lib/registry/man/regpatch.1.xml b/source4/lib/registry/man/regpatch.1.xml
new file mode 100644
index 0000000..3a15082
--- /dev/null
+++ b/source4/lib/registry/man/regpatch.1.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="regpatch.1">
+
+<refmeta>
+ <refentrytitle>regpatch</refentrytitle>
+ <manvolnum>1</manvolnum>
+ <refmiscinfo class="source">Samba</refmiscinfo>
+ <refmiscinfo class="manual">System Administration tools</refmiscinfo>
+ <refmiscinfo class="version">4.0</refmiscinfo>
+</refmeta>
+
+
+<refnamediv>
+ <refname>regpatch</refname>
+ <refpurpose>Applies registry patches to registry files</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>regpatch</command>
+ <arg choice="opt">--help</arg>
+ <arg choice="opt">--backend=BACKEND</arg>
+ <arg choice="opt">--credentials=CREDENTIALS</arg>
+ <arg choice="opt">location</arg>
+ <arg choice="opt">patch-file</arg>
+ </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>The regpatch utility applies registry patches to Windows registry
+ files. The patch files should have the same format as is being used
+ by the regdiff utility and regedit32.exe from Windows.</para>
+
+ <para>If no patch file is specified on the command line,
+ regpatch attempts to read it from standard input.</para>
+</refsect1>
+
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>--help</term>
+ <listitem><para>
+ Show list of available options.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--backend BACKEND</term>
+ <listitem><para>Name of backend to load. Possible values are:
+ creg, regf, dir and rpc. The default is <emphasis>dir</emphasis>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--credentials=CREDENTIALS</term>
+ <listitem><para>
+ Credentials to use, if any. Password should be separated from user name by a percent sign.</para></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>SEE ALSO</title>
+
+ <para>regdiff, regtree, regshell, gregedit, samba, diff, patch</para>
+
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para>This utility is part of the <ulink url="http://www.samba.org/">Samba</ulink> suite, which is developed by the global <ulink url="http://www.samba.org/samba/team/">Samba Team</ulink>.</para>
+
+ <para>This manpage and regpatch were written by Jelmer Vernooij. </para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/lib/registry/man/regshell.1.xml b/source4/lib/registry/man/regshell.1.xml
new file mode 100644
index 0000000..4653fbb
--- /dev/null
+++ b/source4/lib/registry/man/regshell.1.xml
@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="regshell.1">
+
+<refmeta>
+ <refentrytitle>regshell</refentrytitle>
+ <manvolnum>1</manvolnum>
+ <refmiscinfo class="source">Samba</refmiscinfo>
+ <refmiscinfo class="manual">System Administration tools</refmiscinfo>
+ <refmiscinfo class="version">4.0</refmiscinfo>
+</refmeta>
+
+
+<refnamediv>
+ <refname>regshell</refname>
+ <refpurpose>Windows registry file browser using readline</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>regshell</command>
+ <arg choice="opt">--help</arg>
+ <arg choice="opt">--backend=BACKEND</arg>
+ <arg choice="opt">--credentials=CREDENTIALS</arg>
+ <arg choice="opt">location</arg>
+ </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>
+ regshell is a utility that lets you browse thru a Windows registry
+ file as if you were using a regular unix shell to browse thru a
+ file system.
+ </para>
+
+</refsect1>
+
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>--help</term>
+ <listitem><para>
+ Show list of available options.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--backend BACKEND</term>
+ <listitem><para>Name of backend to load. Possible values are:
+ creg, regf, dir and rpc. The default is <emphasis>dir</emphasis>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--credentials=CREDENTIALS</term>
+ <listitem><para>
+ Credentials to use, if any. Password should be separated from user name by a percent sign.</para></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect1>
+
+<refsect1>
+ <title>COMMANDS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>ck|cd &lt;keyname&gt;</term>
+ <listitem><para>
+ Go to the specified subkey.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>ch|predef [predefined-key-name]</term>
+ <listitem><para>
+ Go to the specified predefined key.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>list|ls</term>
+ <listitem><para>
+ List subkeys and values of the current key.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>mkkey|mkdir &lt;keyname&gt;</term>
+ <listitem><para>
+ Create a key with the specified <replaceable>keyname</replaceable> as a subkey of the current key.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>rmval|rm &lt;valname&gt;</term>
+ <listitem><para>
+ Delete the specified value.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>rmkey|rmdir &lt;keyname&gt;</term>
+ <listitem><para>
+ Delete the specified subkey recursively.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>pwd|pwk</term>
+ <listitem><para>Print the full name of the current key.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>set|update</term>
+ <listitem><para>Update the value of a key value. Not implemented at the moment.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>help|?</term>
+ <listitem><para>Print a list of available commands.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>exit|quit</term>
+ <listitem><para>Leave regshell.</para></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect1>
+
+<refsect1>
+ <title>EXAMPLES</title>
+
+ <para>Browsing thru a nt4 registry file</para>
+ <screen>
+<userinput>regshell -b nt4 NTUSER.DAT</userinput>
+$$$PROTO.HIV> <userinput>ls</userinput>
+K AppEvents
+K Console
+K Control Panel
+K Environment
+K Identities
+K Keyboard Layout
+K Network
+K Printers
+K Software
+K UNICODE Program Groups
+K Windows 3.1 Migration Status
+$$$PROTO.HIV> <userinput>exit</userinput>
+</screen>
+
+<para>Listing the subkeys of HKEY_CURRENT_USER\AppEvents on a remote computer:</para>
+<screen>
+<userinput>regshell --remote=ncacn_np:aurelia -c "jelmer%secret"</userinput>
+HKEY_CURRENT_MACHINE> <userinput>predef HKEY_CURRENT_USER</userinput>
+HKEY_CURRENT_USER> <userinput>cd AppEvents</userinput>
+Current path is: HKEY_CURRENT_USER\AppEvents
+HKEY_CURRENT_USER\AppEvents> <userinput>ls</userinput>
+K EventLabels
+K Schemes
+HKEY_CURRENT_USER\AppEvents> <userinput>exit</userinput>
+</screen>
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>SEE ALSO</title>
+
+ <para>regtree, regdiff, regpatch, gregedit, samba</para>
+
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para>This utility is part of the <ulink url="http://www.samba.org/">Samba</ulink> suite, which is developed by the global <ulink url="http://www.samba.org/samba/team/">Samba Team</ulink>.</para>
+
+ <para>This manpage and regshell were written by Jelmer Vernooij. </para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/lib/registry/man/regtree.1.xml b/source4/lib/registry/man/regtree.1.xml
new file mode 100644
index 0000000..0d798e4
--- /dev/null
+++ b/source4/lib/registry/man/regtree.1.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="regtree.1">
+
+<refmeta>
+ <refentrytitle>regtree</refentrytitle>
+ <manvolnum>1</manvolnum>
+ <refmiscinfo class="source">Samba</refmiscinfo>
+ <refmiscinfo class="manual">System Administration tools</refmiscinfo>
+ <refmiscinfo class="version">4.0</refmiscinfo>
+</refmeta>
+
+
+<refnamediv>
+ <refname>regtree</refname>
+ <refpurpose>Text-mode registry viewer</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>regtree</command>
+ <arg choice="opt">--help</arg>
+ <arg choice="opt">--backend=BACKEND</arg>
+ <arg choice="opt">--fullpath</arg>
+ <arg choice="opt">--no-values</arg>
+ <arg choice="opt">--credentials=CREDENTIALS</arg>
+ <arg choice="opt">location</arg>
+ </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>The regtree utility prints out all the contents of a
+ Windows registry file. Subkeys are printed with one level
+ more indentation than their parents. </para>
+
+</refsect1>
+
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>--help</term>
+ <listitem><para>
+ Show list of available options.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--backend BACKEND</term>
+ <listitem><para>Name of backend to load. Possible values are:
+ creg, regf, dir and rpc. The default is <emphasis>dir</emphasis>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--credentials=CREDENTIALS</term>
+ <listitem><para>
+ Credentials to use, if any. Password should be separated from user name by a percent sign.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--fullpath</term>
+ <listitem><para>
+ Print the full path to each key instead of only its name.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry><term>--no-values</term>
+ <listitem><para>Don't print values, just keys.</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>SEE ALSO</title>
+
+ <para>gregedit, regshell, regdiff, regpatch, samba</para>
+
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para>This utility is part of the <ulink url="http://www.samba.org/">Samba</ulink> suite, which is developed by the global <ulink url="http://www.samba.org/samba/team/">Samba Team</ulink>.</para>
+
+ <para>This manpage and regtree were written by Jelmer Vernooij. </para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/lib/registry/patchfile.c b/source4/lib/registry/patchfile.c
new file mode 100644
index 0000000..8069ed7
--- /dev/null
+++ b/source4/lib/registry/patchfile.c
@@ -0,0 +1,543 @@
+/*
+ Unix SMB/CIFS implementation.
+ Reading registry patch files
+
+ Copyright (C) Jelmer Vernooij 2004-2007
+ Copyright (C) Wilco Baan Hofman 2006
+ Copyright (C) Matthias Dieter Wallnöfer 2008-2010
+
+ 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 "lib/registry/registry.h"
+#include "system/filesys.h"
+
+
+_PUBLIC_ WERROR reg_preg_diff_load(int fd,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data);
+
+_PUBLIC_ WERROR reg_dotreg_diff_load(int fd,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data);
+
+/*
+ * Generate difference between two keys
+ */
+WERROR reg_generate_diff_key(struct registry_key *oldkey,
+ struct registry_key *newkey,
+ const char *path,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data)
+{
+ unsigned int i;
+ struct registry_key *t1 = NULL, *t2 = NULL;
+ char *tmppath;
+ const char *keyname1;
+ WERROR error, error1, error2;
+ TALLOC_CTX *mem_ctx = talloc_init("writediff");
+ uint32_t old_num_subkeys, old_num_values,
+ new_num_subkeys, new_num_values;
+
+ if (oldkey != NULL) {
+ error = reg_key_get_info(mem_ctx, oldkey, NULL,
+ &old_num_subkeys, &old_num_values,
+ NULL, NULL, NULL, NULL);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error occurred while getting key info: %s\n",
+ win_errstr(error)));
+ talloc_free(mem_ctx);
+ return error;
+ }
+ } else {
+ old_num_subkeys = 0;
+ old_num_values = 0;
+ }
+
+ /* Subkeys that were changed or deleted */
+ for (i = 0; i < old_num_subkeys; i++) {
+ error1 = reg_key_get_subkey_by_index(mem_ctx, oldkey, i,
+ &keyname1, NULL, NULL);
+ if (!W_ERROR_IS_OK(error1)) {
+ DEBUG(0, ("Error occurred while getting subkey by index: %s\n",
+ win_errstr(error1)));
+ continue;
+ }
+
+ if (newkey != NULL) {
+ error2 = reg_open_key(mem_ctx, newkey, keyname1, &t2);
+ } else {
+ error2 = WERR_FILE_NOT_FOUND;
+ t2 = NULL;
+ }
+
+ if (!W_ERROR_IS_OK(error2) && !W_ERROR_EQUAL(error2, WERR_FILE_NOT_FOUND)) {
+ DEBUG(0, ("Error occurred while getting subkey by name: %s\n",
+ win_errstr(error2)));
+ talloc_free(mem_ctx);
+ return error2;
+ }
+
+ /* if "error2" is going to be "WERR_FILE_NOT_FOUND", then newkey */
+ /* didn't have such a subkey and therefore add a del diff */
+ tmppath = talloc_asprintf(mem_ctx, "%s\\%s", path, keyname1);
+ if (tmppath == NULL) {
+ DEBUG(0, ("Out of memory\n"));
+ talloc_free(mem_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ if (!W_ERROR_IS_OK(error2))
+ callbacks->del_key(callback_data, tmppath);
+
+ /* perform here also the recursive invocation */
+ error1 = reg_open_key(mem_ctx, oldkey, keyname1, &t1);
+ if (!W_ERROR_IS_OK(error1)) {
+ DEBUG(0, ("Error occurred while getting subkey by name: %s\n",
+ win_errstr(error1)));
+ talloc_free(mem_ctx);
+ return error1;
+ }
+ reg_generate_diff_key(t1, t2, tmppath, callbacks, callback_data);
+
+ talloc_free(tmppath);
+ }
+
+ if (newkey != NULL) {
+ error = reg_key_get_info(mem_ctx, newkey, NULL,
+ &new_num_subkeys, &new_num_values,
+ NULL, NULL, NULL, NULL);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error occurred while getting key info: %s\n",
+ win_errstr(error)));
+ talloc_free(mem_ctx);
+ return error;
+ }
+ } else {
+ new_num_subkeys = 0;
+ new_num_values = 0;
+ }
+
+ /* Subkeys that were added */
+ for(i = 0; i < new_num_subkeys; i++) {
+ error1 = reg_key_get_subkey_by_index(mem_ctx, newkey, i,
+ &keyname1, NULL, NULL);
+ if (!W_ERROR_IS_OK(error1)) {
+ DEBUG(0, ("Error occurred while getting subkey by index: %s\n",
+ win_errstr(error1)));
+ talloc_free(mem_ctx);
+ return error1;
+ }
+
+ if (oldkey != NULL) {
+ error2 = reg_open_key(mem_ctx, oldkey, keyname1, &t1);
+
+ if (W_ERROR_IS_OK(error2))
+ continue;
+ } else {
+ error2 = WERR_FILE_NOT_FOUND;
+ t1 = NULL;
+ }
+
+ if (!W_ERROR_EQUAL(error2, WERR_FILE_NOT_FOUND)) {
+ DEBUG(0, ("Error occurred while getting subkey by name: %s\n",
+ win_errstr(error2)));
+ talloc_free(mem_ctx);
+ return error2;
+ }
+
+ /* oldkey didn't have such a subkey, add a add diff */
+ tmppath = talloc_asprintf(mem_ctx, "%s\\%s", path, keyname1);
+ if (tmppath == NULL) {
+ DEBUG(0, ("Out of memory\n"));
+ talloc_free(mem_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ callbacks->add_key(callback_data, tmppath);
+
+ /* perform here also the recursive invocation */
+ error1 = reg_open_key(mem_ctx, newkey, keyname1, &t2);
+ if (!W_ERROR_IS_OK(error1)) {
+ DEBUG(0, ("Error occurred while getting subkey by name: %s\n",
+ win_errstr(error1)));
+ talloc_free(mem_ctx);
+ return error1;
+ }
+ reg_generate_diff_key(t1, t2, tmppath, callbacks, callback_data);
+
+ talloc_free(tmppath);
+ }
+
+ /* Values that were added or changed */
+ for(i = 0; i < new_num_values; i++) {
+ const char *name;
+ uint32_t type1, type2;
+ DATA_BLOB contents1 = { NULL, 0 }, contents2 = { NULL, 0 };
+
+ error1 = reg_key_get_value_by_index(mem_ctx, newkey, i,
+ &name, &type1, &contents1);
+ if (!W_ERROR_IS_OK(error1)) {
+ DEBUG(0, ("Unable to get value by index: %s\n",
+ win_errstr(error1)));
+ talloc_free(mem_ctx);
+ return error1;
+ }
+
+ if (oldkey != NULL) {
+ error2 = reg_key_get_value_by_name(mem_ctx, oldkey,
+ name, &type2,
+ &contents2);
+ } else
+ error2 = WERR_FILE_NOT_FOUND;
+
+ if (!W_ERROR_IS_OK(error2)
+ && !W_ERROR_EQUAL(error2, WERR_FILE_NOT_FOUND)) {
+ DEBUG(0, ("Error occurred while getting value by name: %s\n",
+ win_errstr(error2)));
+ talloc_free(mem_ctx);
+ return error2;
+ }
+
+ if (W_ERROR_IS_OK(error2)
+ && (data_blob_cmp(&contents1, &contents2) == 0)
+ && (type1 == type2)) {
+ talloc_free(discard_const_p(char, name));
+ talloc_free(contents1.data);
+ talloc_free(contents2.data);
+ continue;
+ }
+
+ callbacks->set_value(callback_data, path, name,
+ type1, contents1);
+
+ talloc_free(discard_const_p(char, name));
+ talloc_free(contents1.data);
+ talloc_free(contents2.data);
+ }
+
+ /* Values that were deleted */
+ for (i = 0; i < old_num_values; i++) {
+ const char *name;
+ uint32_t type;
+ DATA_BLOB contents = { NULL, 0 };
+
+ error1 = reg_key_get_value_by_index(mem_ctx, oldkey, i, &name,
+ &type, &contents);
+ if (!W_ERROR_IS_OK(error1)) {
+ DEBUG(0, ("Unable to get value by index: %s\n",
+ win_errstr(error1)));
+ talloc_free(mem_ctx);
+ return error1;
+ }
+
+ if (newkey != NULL)
+ error2 = reg_key_get_value_by_name(mem_ctx, newkey,
+ name, &type, &contents);
+ else
+ error2 = WERR_FILE_NOT_FOUND;
+
+ if (W_ERROR_IS_OK(error2)) {
+ talloc_free(discard_const_p(char, name));
+ talloc_free(contents.data);
+ continue;
+ }
+
+ if (!W_ERROR_EQUAL(error2, WERR_FILE_NOT_FOUND)) {
+ DEBUG(0, ("Error occurred while getting value by name: %s\n",
+ win_errstr(error2)));
+ talloc_free(mem_ctx);
+ return error2;
+ }
+
+ callbacks->del_value(callback_data, path, name);
+
+ talloc_free(discard_const_p(char, name));
+ talloc_free(contents.data);
+ }
+
+ talloc_free(mem_ctx);
+ return WERR_OK;
+}
+
+/**
+ * Generate diff between two registry contexts
+ */
+_PUBLIC_ WERROR reg_generate_diff(struct registry_context *ctx1,
+ struct registry_context *ctx2,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data)
+{
+ unsigned int i;
+ WERROR error;
+
+ for (i = 0; reg_predefined_keys[i].name; i++) {
+ struct registry_key *r1 = NULL, *r2 = NULL;
+
+ error = reg_get_predefined_key(ctx1,
+ reg_predefined_keys[i].handle, &r1);
+ if (!W_ERROR_IS_OK(error) &&
+ !W_ERROR_EQUAL(error, WERR_FILE_NOT_FOUND)) {
+ DEBUG(0, ("Unable to open hive %s for backend 1\n",
+ reg_predefined_keys[i].name));
+ continue;
+ }
+
+ error = reg_get_predefined_key(ctx2,
+ reg_predefined_keys[i].handle, &r2);
+ if (!W_ERROR_IS_OK(error) &&
+ !W_ERROR_EQUAL(error, WERR_FILE_NOT_FOUND)) {
+ DEBUG(0, ("Unable to open hive %s for backend 2\n",
+ reg_predefined_keys[i].name));
+ continue;
+ }
+
+ /* if "r1" is NULL (old hive) and "r2" isn't (new hive) then
+ * the hive doesn't exist yet and we have to generate an add
+ * diff */
+ if ((r1 == NULL) && (r2 != NULL)) {
+ callbacks->add_key(callback_data,
+ reg_predefined_keys[i].name);
+ }
+ /* if "r1" isn't NULL (old hive) and "r2" is (new hive) then
+ * the hive shouldn't exist anymore and we have to generate a
+ * del diff */
+ if ((r1 != NULL) && (r2 == NULL)) {
+ callbacks->del_key(callback_data,
+ reg_predefined_keys[i].name);
+ }
+
+ error = reg_generate_diff_key(r1, r2,
+ reg_predefined_keys[i].name, callbacks,
+ callback_data);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Unable to determine diff: %s\n",
+ win_errstr(error)));
+ return error;
+ }
+ }
+ if (callbacks->done != NULL) {
+ callbacks->done(callback_data);
+ }
+ return WERR_OK;
+}
+
+/**
+ * Load diff file
+ */
+_PUBLIC_ WERROR reg_diff_load(const char *filename,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data)
+{
+ int fd;
+ char hdr[4];
+
+ fd = open(filename, O_RDONLY, 0);
+ if (fd == -1) {
+ DEBUG(0, ("Error opening registry patch file `%s'\n",
+ filename));
+ return WERR_GEN_FAILURE;
+ }
+
+ if (read(fd, &hdr, 4) != 4) {
+ DEBUG(0, ("Error reading registry patch file `%s'\n",
+ filename));
+ close(fd);
+ return WERR_GEN_FAILURE;
+ }
+
+ /* Reset position in file */
+ lseek(fd, 0, SEEK_SET);
+#if 0 /* These backends are not supported yet. */
+ if (strncmp(hdr, "CREG", 4) == 0) {
+ /* Must be a W9x CREG Config.pol file */
+ return reg_creg_diff_load(diff, fd);
+ } else if (strncmp(hdr, "regf", 4) == 0) {
+ /* Must be a REGF NTConfig.pol file */
+ return reg_regf_diff_load(diff, fd);
+ } else
+#endif
+ if (strncmp(hdr, "PReg", 4) == 0) {
+ /* Must be a GPO Registry.pol file */
+ return reg_preg_diff_load(fd, callbacks, callback_data);
+ } else {
+ /* Must be a normal .REG file */
+ return reg_dotreg_diff_load(fd, callbacks, callback_data);
+ }
+}
+
+/**
+ * The reg_diff_apply functions
+ */
+static WERROR reg_diff_apply_add_key(void *_ctx, const char *key_name)
+{
+ struct registry_context *ctx = (struct registry_context *)_ctx;
+ struct registry_key *tmp;
+ char *buf, *buf_ptr;
+ WERROR error;
+
+ /* Recursively create the path */
+ buf = talloc_strdup(ctx, key_name);
+ W_ERROR_HAVE_NO_MEMORY(buf);
+ buf_ptr = buf;
+
+ while (*buf_ptr++ != '\0' ) {
+ if (*buf_ptr == '\\') {
+ *buf_ptr = '\0';
+ error = reg_key_add_abs(ctx, ctx, buf, 0, NULL, &tmp);
+
+ if (!W_ERROR_EQUAL(error, WERR_ALREADY_EXISTS) &&
+ !W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error adding new key '%s': %s\n",
+ key_name, win_errstr(error)));
+ return error;
+ }
+ *buf_ptr++ = '\\';
+ talloc_free(tmp);
+ }
+ }
+
+ talloc_free(buf);
+
+ /* Add the key */
+ error = reg_key_add_abs(ctx, ctx, key_name, 0, NULL, &tmp);
+
+ if (!W_ERROR_EQUAL(error, WERR_ALREADY_EXISTS) &&
+ !W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error adding new key '%s': %s\n",
+ key_name, win_errstr(error)));
+ return error;
+ }
+ talloc_free(tmp);
+
+ return WERR_OK;
+}
+
+static WERROR reg_diff_apply_del_key(void *_ctx, const char *key_name)
+{
+ struct registry_context *ctx = (struct registry_context *)_ctx;
+
+ /* We can't proof here for success, because a common superkey could */
+ /* have been deleted before the subkey's (diff order). This removed */
+ /* therefore all children recursively and the "WERR_FILE_NOT_FOUND" result is */
+ /* expected. */
+
+ reg_key_del_abs(ctx, key_name);
+
+ return WERR_OK;
+}
+
+static WERROR reg_diff_apply_set_value(void *_ctx, const char *path,
+ const char *value_name,
+ uint32_t value_type, DATA_BLOB value)
+{
+ struct registry_context *ctx = (struct registry_context *)_ctx;
+ struct registry_key *tmp;
+ WERROR error;
+
+ /* Open key */
+ error = reg_open_key_abs(ctx, ctx, path, &tmp);
+
+ if (W_ERROR_EQUAL(error, WERR_FILE_NOT_FOUND)) {
+ DEBUG(0, ("Error opening key '%s'\n", path));
+ return error;
+ }
+
+ /* Set value */
+ error = reg_val_set(tmp, value_name,
+ value_type, value);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error setting value '%s'\n", value_name));
+ return error;
+ }
+
+ talloc_free(tmp);
+
+ return WERR_OK;
+}
+
+static WERROR reg_diff_apply_del_value(void *_ctx, const char *key_name,
+ const char *value_name)
+{
+ struct registry_context *ctx = (struct registry_context *)_ctx;
+ struct registry_key *tmp;
+ WERROR error;
+
+ /* Open key */
+ error = reg_open_key_abs(ctx, ctx, key_name, &tmp);
+
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error opening key '%s'\n", key_name));
+ return error;
+ }
+
+ error = reg_del_value(ctx, tmp, value_name);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error deleting value '%s'\n", value_name));
+ return error;
+ }
+
+ talloc_free(tmp);
+
+ return WERR_OK;
+}
+
+static WERROR reg_diff_apply_del_all_values(void *_ctx, const char *key_name)
+{
+ struct registry_context *ctx = (struct registry_context *)_ctx;
+ struct registry_key *key;
+ WERROR error;
+ const char *value_name;
+
+ error = reg_open_key_abs(ctx, ctx, key_name, &key);
+
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error opening key '%s'\n", key_name));
+ return error;
+ }
+
+ W_ERROR_NOT_OK_RETURN(reg_key_get_info(ctx, key, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL));
+
+ while (W_ERROR_IS_OK(reg_key_get_value_by_index(
+ ctx, key, 0, &value_name, NULL, NULL))) {
+ error = reg_del_value(ctx, key, value_name);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error deleting value '%s'\n", value_name));
+ return error;
+ }
+ talloc_free(discard_const_p(char, value_name));
+ }
+
+ talloc_free(key);
+
+ return WERR_OK;
+}
+
+/**
+ * Apply diff to a registry context
+ */
+_PUBLIC_ WERROR reg_diff_apply(struct registry_context *ctx,
+ const char *filename)
+{
+ struct reg_diff_callbacks callbacks;
+
+ callbacks.add_key = reg_diff_apply_add_key;
+ callbacks.del_key = reg_diff_apply_del_key;
+ callbacks.set_value = reg_diff_apply_set_value;
+ callbacks.del_value = reg_diff_apply_del_value;
+ callbacks.del_all_values = reg_diff_apply_del_all_values;
+ callbacks.done = NULL;
+
+ return reg_diff_load(filename, &callbacks, ctx);
+}
diff --git a/source4/lib/registry/patchfile_dotreg.c b/source4/lib/registry/patchfile_dotreg.c
new file mode 100644
index 0000000..5fb342b
--- /dev/null
+++ b/source4/lib/registry/patchfile_dotreg.c
@@ -0,0 +1,435 @@
+/*
+ Unix SMB/CIFS implementation.
+ Reading .REG files
+
+ Copyright (C) Jelmer Vernooij 2004-2007
+ Copyright (C) Wilco Baan Hofman 2006-2010
+
+ 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* FIXME:
+ * - Newer .REG files, created by Windows XP and above use unicode UCS-2
+ * - @="" constructions should write value with empty name.
+*/
+
+#include "includes.h"
+#include "lib/registry/registry.h"
+#include "system/filesys.h"
+
+/**
+ * @file
+ * @brief Registry patch files
+ */
+
+#define HEADER_STRING "REGEDIT4"
+
+struct dotreg_data {
+ int fd;
+};
+
+/*
+ * This is basically a copy of data_blob_hex_string_upper, but with comma's
+ * between the bytes in hex.
+ */
+static char *dotreg_data_blob_hex_string(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob)
+{
+ size_t i;
+ char *hex_string;
+
+ hex_string = talloc_array(mem_ctx, char, (blob->length*3)+1);
+ if (!hex_string) {
+ return NULL;
+ }
+
+ for (i = 0; i < blob->length; i++)
+ slprintf(&hex_string[i*3], 4, "%02X,", blob->data[i]);
+
+ /* Remove last comma and NULL-terminate the string */
+ hex_string[(blob->length*3)-1] = '\0';
+ return hex_string;
+}
+
+/*
+ * This is basically a copy of reg_val_data_string, except that this function
+ * has no 0x for dwords, everything else is regarded as binary, and binary
+ * strings are represented with bytes comma-separated.
+ */
+static char *reg_val_dotreg_string(TALLOC_CTX *mem_ctx, uint32_t type,
+ const DATA_BLOB data)
+{
+ size_t converted_size = 0;
+ char *ret = NULL;
+
+ if (data.length == 0)
+ return talloc_strdup(mem_ctx, "");
+
+ switch (type) {
+ case REG_EXPAND_SZ:
+ case REG_SZ:
+ convert_string_talloc(mem_ctx,
+ CH_UTF16, CH_UNIX, data.data, data.length,
+ (void **)&ret, &converted_size);
+ break;
+ case REG_DWORD:
+ case REG_DWORD_BIG_ENDIAN:
+ SMB_ASSERT(data.length == sizeof(uint32_t));
+ ret = talloc_asprintf(mem_ctx, "%08x",
+ IVAL(data.data, 0));
+ break;
+ default: /* default means treat as binary */
+ case REG_BINARY:
+ ret = dotreg_data_blob_hex_string(mem_ctx, &data);
+ break;
+ }
+
+ return ret;
+}
+
+static WERROR reg_dotreg_diff_add_key(void *_data, const char *key_name)
+{
+ struct dotreg_data *data = (struct dotreg_data *)_data;
+
+ fdprintf(data->fd, "\n[%s]\n", key_name);
+
+ return WERR_OK;
+}
+
+static WERROR reg_dotreg_diff_del_key(void *_data, const char *key_name)
+{
+ struct dotreg_data *data = (struct dotreg_data *)_data;
+
+ fdprintf(data->fd, "\n[-%s]\n", key_name);
+
+ return WERR_OK;
+}
+
+static WERROR reg_dotreg_diff_set_value(void *_data, const char *path,
+ const char *value_name,
+ uint32_t value_type, DATA_BLOB value)
+{
+ struct dotreg_data *data = (struct dotreg_data *)_data;
+ char *data_string = reg_val_dotreg_string(NULL,
+ value_type, value);
+ char *data_incl_type;
+
+ W_ERROR_HAVE_NO_MEMORY(data_string);
+
+ switch (value_type) {
+ case REG_SZ:
+ data_incl_type = talloc_asprintf(data_string, "\"%s\"",
+ data_string);
+ break;
+ case REG_DWORD:
+ data_incl_type = talloc_asprintf(data_string,
+ "dword:%s", data_string);
+ break;
+ case REG_BINARY:
+ data_incl_type = talloc_asprintf(data_string, "hex:%s",
+ data_string);
+ break;
+ default:
+ data_incl_type = talloc_asprintf(data_string, "hex(%x):%s",
+ value_type, data_string);
+ break;
+ }
+
+ if (value_name[0] == '\0') {
+ fdprintf(data->fd, "@=%s\n", data_incl_type);
+ } else {
+ fdprintf(data->fd, "\"%s\"=%s\n",
+ value_name, data_incl_type);
+ }
+
+ talloc_free(data_string);
+
+ return WERR_OK;
+}
+
+static WERROR reg_dotreg_diff_del_value(void *_data, const char *path,
+ const char *value_name)
+{
+ struct dotreg_data *data = (struct dotreg_data *)_data;
+
+ fdprintf(data->fd, "\"%s\"=-\n", value_name);
+
+ return WERR_OK;
+}
+
+static WERROR reg_dotreg_diff_done(void *_data)
+{
+ struct dotreg_data *data = (struct dotreg_data *)_data;
+
+ close(data->fd);
+ talloc_free(data);
+
+ return WERR_OK;
+}
+
+static WERROR reg_dotreg_diff_del_all_values(void *callback_data,
+ const char *key_name)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+/**
+ * Save registry diff
+ */
+_PUBLIC_ WERROR reg_dotreg_diff_save(TALLOC_CTX *ctx, const char *filename,
+ struct reg_diff_callbacks **callbacks,
+ void **callback_data)
+{
+ struct dotreg_data *data;
+
+ data = talloc_zero(ctx, struct dotreg_data);
+ *callback_data = data;
+
+ if (filename) {
+ data->fd = open(filename, O_CREAT|O_WRONLY, 0755);
+ if (data->fd < 0) {
+ DEBUG(0, ("Unable to open %s\n", filename));
+ return WERR_FILE_NOT_FOUND;
+ }
+ } else {
+ data->fd = STDOUT_FILENO;
+ }
+
+ fdprintf(data->fd, "%s\n\n", HEADER_STRING);
+
+ *callbacks = talloc(ctx, struct reg_diff_callbacks);
+
+ (*callbacks)->add_key = reg_dotreg_diff_add_key;
+ (*callbacks)->del_key = reg_dotreg_diff_del_key;
+ (*callbacks)->set_value = reg_dotreg_diff_set_value;
+ (*callbacks)->del_value = reg_dotreg_diff_del_value;
+ (*callbacks)->del_all_values = reg_dotreg_diff_del_all_values;
+ (*callbacks)->done = reg_dotreg_diff_done;
+
+ return WERR_OK;
+}
+
+/**
+ * Load diff file
+ */
+_PUBLIC_ WERROR reg_dotreg_diff_load(int fd,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data)
+{
+ char *line, *p, *q;
+ char *curkey = NULL;
+ TALLOC_CTX *mem_ctx = talloc_init("reg_dotreg_diff_load");
+ WERROR error;
+ uint32_t value_type;
+ DATA_BLOB data;
+ bool result;
+ char *type_str = NULL;
+ char *data_str = NULL;
+ char *value = NULL;
+ bool continue_next_line = 0;
+
+ line = afdgets(fd, mem_ctx, 0);
+ if (!line) {
+ DEBUG(0, ("Can't read from file.\n"));
+ talloc_free(mem_ctx);
+ close(fd);
+ return WERR_GEN_FAILURE;
+ }
+
+ while ((line = afdgets(fd, mem_ctx, 0))) {
+ /* Remove '\r' if it's a Windows text file */
+ if (strlen(line) && line[strlen(line)-1] == '\r') {
+ line[strlen(line)-1] = '\0';
+ }
+
+ /* Ignore comments and empty lines */
+ if (strlen(line) == 0 || line[0] == ';') {
+ talloc_free(line);
+
+ if (curkey) {
+ talloc_free(curkey);
+ }
+ curkey = NULL;
+ continue;
+ }
+
+ /* Start of key */
+ if (line[0] == '[') {
+ if (line[strlen(line)-1] != ']') {
+ DEBUG(0, ("Missing ']' on line: %s\n", line));
+ talloc_free(line);
+ continue;
+ }
+
+ /* Deleting key */
+ if (line[1] == '-') {
+ curkey = talloc_strndup(line, line+2, strlen(line)-3);
+ W_ERROR_HAVE_NO_MEMORY(curkey);
+
+ error = callbacks->del_key(callback_data,
+ curkey);
+
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0,("Error deleting key %s\n",
+ curkey));
+ talloc_free(mem_ctx);
+ return error;
+ }
+
+ talloc_free(line);
+ curkey = NULL;
+ continue;
+ }
+ curkey = talloc_strndup(mem_ctx, line+1, strlen(line)-2);
+ W_ERROR_HAVE_NO_MEMORY(curkey);
+
+ error = callbacks->add_key(callback_data, curkey);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0,("Error adding key %s\n", curkey));
+ talloc_free(mem_ctx);
+ return error;
+ }
+
+ talloc_free(line);
+ continue;
+ }
+
+ /* Deleting/Changing value */
+ if (continue_next_line) {
+ continue_next_line = 0;
+
+ /* Continued data start with two whitespaces */
+ if (line[0] != ' ' || line[1] != ' ') {
+ DEBUG(0, ("Malformed line: %s\n", line));
+ talloc_free(line);
+ continue;
+ }
+ p = line + 2;
+
+ /* Continue again if line ends with a backslash */
+ if (line[strlen(line)-1] == '\\') {
+ line[strlen(line)-1] = '\0';
+ continue_next_line = 1;
+ data_str = talloc_strdup_append(data_str, p);
+ talloc_free(line);
+ continue;
+ }
+ data_str = talloc_strdup_append(data_str, p);
+ } else {
+ p = strchr_m(line, '=');
+ if (p == NULL) {
+ DEBUG(0, ("Malformed line: %s\n", line));
+ talloc_free(line);
+ continue;
+ }
+
+ *p = '\0'; p++;
+
+
+ if (curkey == NULL) {
+ DEBUG(0, ("Value change without key\n"));
+ talloc_free(line);
+ continue;
+ }
+
+ /* Values should be double-quoted */
+ if (line[0] != '"') {
+ DEBUG(0, ("Malformed line\n"));
+ talloc_free(line);
+ continue;
+ }
+
+ /* Chop of the quotes and store as value */
+ value = talloc_strndup(mem_ctx, line+1,strlen(line)-2);
+
+ /* Delete value */
+ if (p[0] == '-') {
+ error = callbacks->del_value(callback_data,
+ curkey, value);
+
+ /* Ignore if key does not exist (WERR_FILE_NOT_FOUND)
+ * Consistent with Windows behaviour */
+ if (!W_ERROR_IS_OK(error) &&
+ !W_ERROR_EQUAL(error, WERR_FILE_NOT_FOUND)) {
+ DEBUG(0, ("Error deleting value %s in key %s\n",
+ value, curkey));
+ talloc_free(mem_ctx);
+ return error;
+ }
+
+ talloc_free(line);
+ talloc_free(value);
+ continue;
+ }
+
+ /* Do not look for colons in strings */
+ if (p[0] == '"') {
+ q = NULL;
+ data_str = talloc_strndup(mem_ctx, p+1,strlen(p)-2);
+ } else {
+ /* Split the value type from the data */
+ q = strchr_m(p, ':');
+ if (q) {
+ *q = '\0';
+ q++;
+ type_str = talloc_strdup(mem_ctx, p);
+ data_str = talloc_strdup(mem_ctx, q);
+ } else {
+ data_str = talloc_strdup(mem_ctx, p);
+ }
+ }
+
+ /* Backslash before the CRLF means continue on next line */
+ if (data_str[strlen(data_str)-1] == '\\') {
+ data_str[strlen(data_str)-1] = '\0';
+ talloc_free(line);
+ continue_next_line = 1;
+ continue;
+ }
+ }
+ DEBUG(9, ("About to write %s with type %s, length %ld: %s\n", value, type_str, (long) strlen(data_str), data_str));
+ result = reg_string_to_val(value,
+ type_str?type_str:"REG_SZ", data_str,
+ &value_type, &data);
+ if (!result) {
+ DEBUG(0, ("Error converting string to value for line:\n%s\n",
+ line));
+ return WERR_GEN_FAILURE;
+ }
+
+ error = callbacks->set_value(callback_data, curkey, value,
+ value_type, data);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error setting value for %s in %s\n",
+ value, curkey));
+ talloc_free(mem_ctx);
+ return error;
+ }
+
+ /* Clean up buffers */
+ if (type_str != NULL) {
+ talloc_free(type_str);
+ type_str = NULL;
+ }
+ talloc_free(data_str);
+ talloc_free(value);
+ talloc_free(line);
+ }
+
+ close(fd);
+
+ talloc_free(mem_ctx);
+
+ return WERR_OK;
+}
diff --git a/source4/lib/registry/patchfile_preg.c b/source4/lib/registry/patchfile_preg.c
new file mode 100644
index 0000000..50bd141
--- /dev/null
+++ b/source4/lib/registry/patchfile_preg.c
@@ -0,0 +1,387 @@
+/*
+ Unix SMB/CIFS implementation.
+ Reading Registry.pol PReg registry files
+
+ Copyright (C) Wilco Baan Hofman 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "lib/registry/registry.h"
+#include "system/filesys.h"
+#include "librpc/gen_ndr/winreg.h"
+#include "lib/util/sys_rw.h"
+
+#undef strcasecmp
+#undef strncasecmp
+
+struct preg_data {
+ int fd;
+ TALLOC_CTX *ctx;
+};
+
+static WERROR preg_read_utf16(int fd, char *c)
+{
+ uint16_t v;
+
+ if (read(fd, &v, sizeof(uint16_t)) < sizeof(uint16_t)) {
+ return WERR_GEN_FAILURE;
+ }
+ push_codepoint(c, v);
+ return WERR_OK;
+}
+static WERROR preg_write_utf16(int fd, const char *string)
+{
+ uint16_t v;
+ size_t i, size;
+
+ for (i = 0; i < strlen(string); i+=size) {
+ v = next_codepoint(&string[i], &size);
+ if (write(fd, &v, sizeof(uint16_t)) < sizeof(uint16_t)) {
+ return WERR_GEN_FAILURE;
+ }
+ }
+ return WERR_OK;
+}
+/* PReg does not support adding keys. */
+static WERROR reg_preg_diff_add_key(void *_data, const char *key_name)
+{
+ return WERR_OK;
+}
+
+static WERROR reg_preg_diff_set_value(void *_data, const char *key_name,
+ const char *value_name,
+ uint32_t value_type, DATA_BLOB value_data)
+{
+ struct preg_data *data = (struct preg_data *)_data;
+ uint32_t buf;
+
+ preg_write_utf16(data->fd, "[");
+ preg_write_utf16(data->fd, key_name);
+ preg_write_utf16(data->fd, ";");
+ preg_write_utf16(data->fd, value_name);
+ preg_write_utf16(data->fd, ";");
+ SIVAL(&buf, 0, value_type);
+ sys_write_v(data->fd, &buf, sizeof(uint32_t));
+ preg_write_utf16(data->fd, ";");
+ SIVAL(&buf, 0, value_data.length);
+ sys_write_v(data->fd, &buf, sizeof(uint32_t));
+ preg_write_utf16(data->fd, ";");
+ sys_write_v(data->fd, value_data.data, value_data.length);
+ preg_write_utf16(data->fd, "]");
+
+ return WERR_OK;
+}
+
+static WERROR reg_preg_diff_del_key(void *_data, const char *key_name)
+{
+ struct preg_data *data = (struct preg_data *)_data;
+ char *parent_name;
+ DATA_BLOB blob;
+ WERROR werr;
+
+ parent_name = talloc_strndup(data->ctx, key_name,
+ strrchr(key_name, '\\')-key_name);
+ W_ERROR_HAVE_NO_MEMORY(parent_name);
+ blob.data = (uint8_t*)talloc_strndup(data->ctx,
+ key_name+(strrchr(key_name, '\\')-key_name)+1,
+ strlen(key_name)-(strrchr(key_name, '\\')-key_name));
+ W_ERROR_HAVE_NO_MEMORY(blob.data);
+ blob.length = strlen((char *)blob.data)+1;
+
+
+ /* FIXME: These values should be accumulated to be written at done(). */
+ werr = reg_preg_diff_set_value(data, parent_name, "**DeleteKeys",
+ REG_SZ, blob);
+
+ talloc_free(parent_name);
+ talloc_free(blob.data);
+
+ return werr;
+}
+
+static WERROR reg_preg_diff_del_value(void *_data, const char *key_name,
+ const char *value_name)
+{
+ struct preg_data *data = (struct preg_data *)_data;
+ char *val;
+ DATA_BLOB blob;
+ WERROR werr;
+
+ val = talloc_asprintf(data->ctx, "**Del.%s", value_name);
+ W_ERROR_HAVE_NO_MEMORY(val);
+ blob.data = (uint8_t *)talloc(data->ctx, uint32_t);
+ W_ERROR_HAVE_NO_MEMORY(blob.data);
+ SIVAL(blob.data, 0, 0);
+ blob.length = sizeof(uint32_t);
+
+ werr = reg_preg_diff_set_value(data, key_name, val, REG_DWORD, blob);
+
+ talloc_free(val);
+ talloc_free(blob.data);
+
+ return werr;
+}
+
+static WERROR reg_preg_diff_del_all_values(void *_data, const char *key_name)
+{
+ struct preg_data *data = (struct preg_data *)_data;
+ DATA_BLOB blob;
+ WERROR werr;
+
+ blob.data = (uint8_t *)talloc(data->ctx, uint32_t);
+ W_ERROR_HAVE_NO_MEMORY(blob.data);
+ SIVAL(blob.data, 0, 0);
+ blob.length = sizeof(uint32_t);
+
+ werr = reg_preg_diff_set_value(data, key_name, "**DelVals.", REG_DWORD,
+ blob);
+
+ talloc_free(blob.data);
+
+ return werr;
+}
+
+static WERROR reg_preg_diff_done(void *_data)
+{
+ struct preg_data *data = (struct preg_data *)_data;
+
+ close(data->fd);
+ talloc_free(data);
+ return WERR_OK;
+}
+
+/**
+ * Save registry diff
+ */
+_PUBLIC_ WERROR reg_preg_diff_save(TALLOC_CTX *ctx, const char *filename,
+ struct reg_diff_callbacks **callbacks,
+ void **callback_data)
+{
+ struct preg_data *data;
+ struct {
+ char hdr[4];
+ uint32_t version;
+ } preg_header;
+
+
+ data = talloc_zero(ctx, struct preg_data);
+ *callback_data = data;
+
+ if (filename) {
+ data->fd = open(filename, O_CREAT|O_WRONLY, 0755);
+ if (data->fd < 0) {
+ DEBUG(0, ("Unable to open %s\n", filename));
+ return WERR_FILE_NOT_FOUND;
+ }
+ } else {
+ data->fd = STDOUT_FILENO;
+ }
+
+ memcpy(preg_header.hdr, "PReg", sizeof(preg_header.hdr));
+ SIVAL(&preg_header.version, 0, 1);
+ sys_write_v(data->fd, (uint8_t *)&preg_header, sizeof(preg_header));
+
+ data->ctx = ctx;
+
+ *callbacks = talloc(ctx, struct reg_diff_callbacks);
+
+ (*callbacks)->add_key = reg_preg_diff_add_key;
+ (*callbacks)->del_key = reg_preg_diff_del_key;
+ (*callbacks)->set_value = reg_preg_diff_set_value;
+ (*callbacks)->del_value = reg_preg_diff_del_value;
+ (*callbacks)->del_all_values = reg_preg_diff_del_all_values;
+ (*callbacks)->done = reg_preg_diff_done;
+
+ return WERR_OK;
+}
+/**
+ * Load diff file
+ */
+_PUBLIC_ WERROR reg_preg_diff_load(int fd,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data)
+{
+ struct {
+ char hdr[4];
+ uint32_t version;
+ } preg_header;
+ char *buf;
+ size_t buf_size = 1024;
+ char *buf_ptr;
+ TALLOC_CTX *mem_ctx = talloc_init("reg_preg_diff_load");
+ WERROR ret = WERR_OK;
+
+ buf = talloc_array(mem_ctx, char, buf_size);
+ buf_ptr = buf;
+
+ /* Read first 8 bytes (the header) */
+ if (read(fd, &preg_header, sizeof(preg_header)) != sizeof(preg_header)) {
+ DEBUG(0, ("Could not read PReg file: %s\n",
+ strerror(errno)));
+ ret = WERR_GEN_FAILURE;
+ goto cleanup;
+ }
+ preg_header.version = IVAL(&preg_header.version, 0);
+
+ if (strncmp(preg_header.hdr, "PReg", 4) != 0) {
+ DEBUG(0, ("This file is not a valid preg registry file\n"));
+ ret = WERR_GEN_FAILURE;
+ goto cleanup;
+ }
+ if (preg_header.version > 1) {
+ DEBUG(0, ("Warning: file format version is higher than expected.\n"));
+ }
+
+ /* Read the entries */
+ while(1) {
+ uint32_t value_type, length;
+ char *key = NULL;
+ char *value_name = NULL;
+ DATA_BLOB data = {NULL, 0};
+
+ if (!W_ERROR_IS_OK(preg_read_utf16(fd, buf_ptr))) {
+ break;
+ }
+ if (*buf_ptr != '[') {
+ DEBUG(0, ("Error in PReg file.\n"));
+ ret = WERR_GEN_FAILURE;
+ goto cleanup;
+ }
+
+ /* Get the path */
+ buf_ptr = buf;
+ while (W_ERROR_IS_OK(preg_read_utf16(fd, buf_ptr)) &&
+ *buf_ptr != ';' && buf_ptr-buf < buf_size) {
+ buf_ptr++;
+ }
+ buf[buf_ptr-buf] = '\0';
+ key = talloc_strdup(mem_ctx, buf);
+
+ /* Get the name */
+ buf_ptr = buf;
+ while (W_ERROR_IS_OK(preg_read_utf16(fd, buf_ptr)) &&
+ *buf_ptr != ';' && buf_ptr-buf < buf_size) {
+ buf_ptr++;
+ }
+ buf[buf_ptr-buf] = '\0';
+ value_name = talloc_strdup(mem_ctx, buf);
+
+ /* Get the type */
+ if (read(fd, &value_type, sizeof(uint32_t)) < sizeof(uint32_t)) {
+ DEBUG(0, ("Error while reading PReg\n"));
+ ret = WERR_GEN_FAILURE;
+ goto cleanup;
+ }
+ value_type = IVAL(&value_type, 0);
+
+ /* Read past delimiter */
+ buf_ptr = buf;
+ if (!(W_ERROR_IS_OK(preg_read_utf16(fd, buf_ptr)) &&
+ *buf_ptr == ';') && buf_ptr-buf < buf_size) {
+ DEBUG(0, ("Error in PReg file.\n"));
+ ret = WERR_GEN_FAILURE;
+ goto cleanup;
+ }
+
+ /* Get data length */
+ if (read(fd, &length, sizeof(uint32_t)) < sizeof(uint32_t)) {
+ DEBUG(0, ("Error while reading PReg\n"));
+ ret = WERR_GEN_FAILURE;
+ goto cleanup;
+ }
+ length = IVAL(&length, 0);
+
+ /* Read past delimiter */
+ buf_ptr = buf;
+ if (!(W_ERROR_IS_OK(preg_read_utf16(fd, buf_ptr)) &&
+ *buf_ptr == ';') && buf_ptr-buf < buf_size) {
+ DEBUG(0, ("Error in PReg file.\n"));
+ ret = WERR_GEN_FAILURE;
+ goto cleanup;
+ }
+
+ /* Get the data */
+ buf_ptr = buf;
+ if (length < buf_size &&
+ read(fd, buf_ptr, length) != length) {
+ DEBUG(0, ("Error while reading PReg\n"));
+ ret = WERR_GEN_FAILURE;
+ goto cleanup;
+ }
+ data = data_blob_talloc(mem_ctx, buf, length);
+
+ /* Check if delimiter is in place (whine if it isn't) */
+ buf_ptr = buf;
+ if (!(W_ERROR_IS_OK(preg_read_utf16(fd, buf_ptr)) &&
+ *buf_ptr == ']') && buf_ptr-buf < buf_size) {
+ DEBUG(0, ("Warning: Missing ']' in PReg file, expected ']', got '%c' 0x%x.\n",
+ *buf_ptr, *buf_ptr));
+ }
+
+ if (strcasecmp(value_name, "**DelVals") == 0) {
+ callbacks->del_all_values(callback_data, key);
+ } else if (strncasecmp(value_name, "**Del.",6) == 0) {
+ char *p = value_name+6;
+
+ callbacks->del_value(callback_data, key, p);
+ } else if (strcasecmp(value_name, "**DeleteValues") == 0) {
+ char *p, *q;
+
+ p = (char *) data.data;
+
+ while ((q = strchr_m(p, ';'))) {
+ *q = '\0';
+ q++;
+
+ callbacks->del_value(callback_data, key, p);
+
+ p = q;
+ }
+ callbacks->del_value(callback_data, key, p);
+ } else if (strcasecmp(value_name, "**DeleteKeys") == 0) {
+ char *p, *q, *full_key;
+
+ p = (char *) data.data;
+
+ while ((q = strchr_m(p, ';'))) {
+ *q = '\0';
+ q++;
+
+ full_key = talloc_asprintf(mem_ctx, "%s\\%s",
+ key, p);
+ callbacks->del_key(callback_data, full_key);
+ talloc_free(full_key);
+
+ p = q;
+ }
+ full_key = talloc_asprintf(mem_ctx, "%s\\%s", key, p);
+ callbacks->del_key(callback_data, full_key);
+ talloc_free(full_key);
+ } else {
+ callbacks->add_key(callback_data, key);
+ callbacks->set_value(callback_data, key, value_name,
+ value_type, data);
+ }
+ TALLOC_FREE(key);
+ TALLOC_FREE(value_name);
+ data_blob_free(&data);
+ }
+cleanup:
+ close(fd);
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
diff --git a/source4/lib/registry/pyregistry.c b/source4/lib/registry/pyregistry.c
new file mode 100644
index 0000000..f4201c3
--- /dev/null
+++ b/source4/lib/registry/pyregistry.c
@@ -0,0 +1,494 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008
+ Copyright (C) Wilco Baan Hofman <wilco@baanhofman.nl> 2010
+
+ 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 "python/py3compat.h"
+#include "includes.h"
+#include "python/modules.h"
+#include "libcli/util/pyerrors.h"
+#include "lib/registry/registry.h"
+#include <pytalloc.h>
+#include "lib/events/events.h"
+#include "auth/credentials/pycredentials.h"
+#include "param/pyparam.h"
+
+extern PyTypeObject PyRegistryKey;
+extern PyTypeObject PyRegistry;
+extern PyTypeObject PyHiveKey;
+
+/*#define PyRegistryKey_AsRegistryKey(obj) pytalloc_get_type(obj, struct registry_key)*/
+#define PyRegistry_AsRegistryContext(obj) ((struct registry_context *)pytalloc_get_ptr(obj))
+#define PyHiveKey_AsHiveKey(obj) ((struct hive_key*)pytalloc_get_ptr(obj))
+
+
+static PyObject *py_get_predefined_key_by_name(PyObject *self, PyObject *args)
+{
+ char *name;
+ WERROR result;
+ struct registry_context *ctx = PyRegistry_AsRegistryContext(self);
+ struct registry_key *key;
+
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return NULL;
+
+ result = reg_get_predefined_key_by_name(ctx, name, &key);
+ PyErr_WERROR_NOT_OK_RAISE(result);
+
+ return pytalloc_steal(&PyRegistryKey, key);
+}
+
+static PyObject *py_key_del_abs(PyObject *self, PyObject *args)
+{
+ char *path;
+ WERROR result;
+ struct registry_context *ctx = PyRegistry_AsRegistryContext(self);
+
+ if (!PyArg_ParseTuple(args, "s", &path))
+ return NULL;
+
+ result = reg_key_del_abs(ctx, path);
+ PyErr_WERROR_NOT_OK_RAISE(result);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_get_predefined_key(PyObject *self, PyObject *args)
+{
+ uint32_t hkey;
+ struct registry_context *ctx = PyRegistry_AsRegistryContext(self);
+ WERROR result;
+ struct registry_key *key;
+
+ if (!PyArg_ParseTuple(args, "I", &hkey))
+ return NULL;
+
+ result = reg_get_predefined_key(ctx, hkey, &key);
+ PyErr_WERROR_NOT_OK_RAISE(result);
+
+ return pytalloc_steal(&PyRegistryKey, key);
+}
+
+static PyObject *py_diff_apply(PyObject *self, PyObject *args)
+{
+ char *filename;
+ WERROR result;
+ struct registry_context *ctx = PyRegistry_AsRegistryContext(self);
+ if (!PyArg_ParseTuple(args, "s", &filename))
+ return NULL;
+
+ result = reg_diff_apply(ctx, filename);
+ PyErr_WERROR_NOT_OK_RAISE(result);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_mount_hive(PyObject *self, PyObject *args)
+{
+ struct registry_context *ctx = PyRegistry_AsRegistryContext(self);
+ uint32_t hkey;
+ PyObject *py_hivekey, *py_elements = Py_None;
+ const char **elements;
+ WERROR result;
+
+ if (!PyArg_ParseTuple(args, "OI|O", &py_hivekey, &hkey, &py_elements))
+ return NULL;
+
+ if (!PyList_Check(py_elements) && py_elements != Py_None) {
+ PyErr_SetString(PyExc_TypeError, "Expected list of elements");
+ return NULL;
+ }
+
+ if (py_elements == Py_None) {
+ elements = NULL;
+ } else {
+ int i;
+ elements = talloc_array(NULL, const char *, PyList_Size(py_elements));
+ for (i = 0; i < PyList_Size(py_elements); i++)
+ elements[i] = PyUnicode_AsUTF8(PyList_GetItem(py_elements, i));
+ }
+
+ SMB_ASSERT(ctx != NULL);
+
+ result = reg_mount_hive(ctx, PyHiveKey_AsHiveKey(py_hivekey), hkey, elements);
+ PyErr_WERROR_NOT_OK_RAISE(result);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *registry_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ WERROR result;
+ struct registry_context *ctx;
+ result = reg_open_local(NULL, &ctx);
+ PyErr_WERROR_NOT_OK_RAISE(result);
+ return pytalloc_steal(&PyRegistry, ctx);
+}
+
+static PyMethodDef registry_methods[] = {
+ { "get_predefined_key_by_name", py_get_predefined_key_by_name, METH_VARARGS,
+ "S.get_predefined_key_by_name(name) -> key\n"
+ "Find a predefined key by name" },
+ { "key_del_abs", py_key_del_abs, METH_VARARGS, "S.key_del_abs(name) -> None\n"
+ "Delete a key by absolute path." },
+ { "get_predefined_key", py_get_predefined_key, METH_VARARGS, "S.get_predefined_key(hkey_id) -> key\n"
+ "Find a predefined key by id" },
+ { "diff_apply", py_diff_apply, METH_VARARGS, "S.diff_apply(filename) -> None\n"
+ "Apply the diff from the specified file" },
+ { "mount_hive", py_mount_hive, METH_VARARGS, "S.mount_hive(key, key_id, elements=None) -> None\n"
+ "Mount the specified key at the specified path." },
+ {0}
+};
+
+PyTypeObject PyRegistry = {
+ .tp_name = "Registry",
+ .tp_methods = registry_methods,
+ .tp_new = registry_new,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+};
+
+static PyObject *py_hive_key_del(PyObject *self, PyObject *args)
+{
+ char *name;
+ struct hive_key *key = PyHiveKey_AsHiveKey(self);
+ WERROR result;
+
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return NULL;
+
+ result = hive_key_del(NULL, key, name);
+
+ PyErr_WERROR_NOT_OK_RAISE(result);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_hive_key_flush(PyObject *self,
+ PyObject *Py_UNUSED(ignored))
+{
+ WERROR result;
+ struct hive_key *key = PyHiveKey_AsHiveKey(self);
+
+ result = hive_key_flush(key);
+ PyErr_WERROR_NOT_OK_RAISE(result);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_hive_key_del_value(PyObject *self, PyObject *args)
+{
+ char *name;
+ WERROR result;
+ struct hive_key *key = PyHiveKey_AsHiveKey(self);
+
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return NULL;
+
+ result = hive_key_del_value(NULL, key, name);
+
+ PyErr_WERROR_NOT_OK_RAISE(result);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_hive_key_set_value(PyObject *self, PyObject *args)
+{
+ char *name;
+ uint32_t type;
+ DATA_BLOB value;
+ Py_ssize_t value_length = 0;
+ WERROR result;
+ struct hive_key *key = PyHiveKey_AsHiveKey(self);
+
+ if (!PyArg_ParseTuple(args, "sIz#", &name, &type, &value.data, &value_length)) {
+ return NULL;
+ }
+ value.length = value_length;
+
+ if (value.data != NULL)
+ result = hive_key_set_value(key, name, type, value);
+ else
+ result = hive_key_del_value(NULL, key, name);
+
+ PyErr_WERROR_NOT_OK_RAISE(result);
+
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef hive_key_methods[] = {
+ { "del", py_hive_key_del, METH_VARARGS, "S.del(name) -> None\n"
+ "Delete a subkey" },
+ { "flush", (PyCFunction)py_hive_key_flush, METH_NOARGS, "S.flush() -> None\n"
+ "Flush this key to disk" },
+ { "del_value", py_hive_key_del_value, METH_VARARGS, "S.del_value(name) -> None\n"
+ "Delete a value" },
+ { "set_value", py_hive_key_set_value, METH_VARARGS, "S.set_value(name, type, data) -> None\n"
+ "Set a value" },
+ {0}
+};
+
+static PyObject *hive_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) {
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_open_hive(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ const char *kwnames[] = { "location", "lp_ctx", "session_info", "credentials", NULL };
+ WERROR result;
+ struct loadparm_context *lp_ctx;
+ PyObject *py_lp_ctx = Py_None;
+ PyObject *py_session_info = Py_None;
+ PyObject *py_credentials = Py_None;
+ struct auth_session_info *session_info;
+ struct cli_credentials *credentials;
+ char *location;
+ struct hive_key *hive_key;
+ TALLOC_CTX *mem_ctx;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|OOO",
+ discard_const_p(char *, kwnames),
+ &location,
+ &py_lp_ctx, &py_session_info,
+ &py_credentials))
+ return NULL;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ lp_ctx = lpcfg_from_py_object(mem_ctx, py_lp_ctx);
+ if (lp_ctx == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected loadparm context");
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ credentials = cli_credentials_from_py_object(py_credentials);
+ if (credentials == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected credentials");
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+ session_info = NULL;
+
+ result = reg_open_hive(NULL, location, session_info, credentials,
+ samba_tevent_context_init(NULL),
+ lp_ctx, &hive_key);
+ talloc_free(mem_ctx);
+ PyErr_WERROR_NOT_OK_RAISE(result);
+
+ return pytalloc_steal(&PyHiveKey, hive_key);
+}
+
+PyTypeObject PyHiveKey = {
+ .tp_name = "HiveKey",
+ .tp_methods = hive_key_methods,
+ .tp_new = hive_new,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+};
+
+PyTypeObject PyRegistryKey = {
+ .tp_name = "RegistryKey",
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+};
+
+static PyObject *py_open_samba(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ const char *kwnames[] = { "lp_ctx", "session_info", NULL };
+ struct registry_context *reg_ctx;
+ WERROR result;
+ struct loadparm_context *lp_ctx;
+ PyObject *py_lp_ctx = Py_None;
+ PyObject *py_session_info = Py_None;
+ PyObject *py_credentials = Py_None;
+ struct auth_session_info *session_info;
+ struct cli_credentials *credentials;
+ TALLOC_CTX *mem_ctx;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOO",
+ discard_const_p(char *, kwnames),
+ &py_lp_ctx, &py_session_info,
+ &py_credentials))
+ return NULL;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ lp_ctx = lpcfg_from_py_object(mem_ctx, py_lp_ctx);
+ if (lp_ctx == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected loadparm context");
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ credentials = cli_credentials_from_py_object(py_credentials);
+ if (credentials == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected credentials");
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ session_info = NULL; /* FIXME */
+
+ result = reg_open_samba(NULL, &reg_ctx, NULL,
+ lp_ctx, session_info, credentials);
+ talloc_free(mem_ctx);
+ if (!W_ERROR_IS_OK(result)) {
+ PyErr_SetWERROR(result);
+ return NULL;
+ }
+
+ return pytalloc_steal(&PyRegistry, reg_ctx);
+}
+
+static PyObject *py_open_ldb_file(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ const char *kwnames[] = { "location", "session_info", "credentials", "lp_ctx", NULL };
+ PyObject *py_session_info = Py_None, *py_credentials = Py_None, *py_lp_ctx = Py_None;
+ WERROR result;
+ char *location;
+ struct loadparm_context *lp_ctx;
+ struct cli_credentials *credentials;
+ struct hive_key *key;
+ struct auth_session_info *session_info;
+ TALLOC_CTX *mem_ctx;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|OOO",
+ discard_const_p(char *, kwnames),
+ &location, &py_session_info,
+ &py_credentials, &py_lp_ctx))
+ return NULL;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ lp_ctx = lpcfg_from_py_object(mem_ctx, py_lp_ctx);
+ if (lp_ctx == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected loadparm context");
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ credentials = cli_credentials_from_py_object(py_credentials);
+ if (credentials == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected credentials");
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ session_info = NULL; /* FIXME */
+
+ result = reg_open_ldb_file(NULL, location, session_info, credentials,
+ s4_event_context_init(NULL), lp_ctx, &key);
+ talloc_free(mem_ctx);
+ PyErr_WERROR_NOT_OK_RAISE(result);
+
+ return pytalloc_steal(&PyHiveKey, key);
+}
+
+static PyObject *py_str_regtype(PyObject *self, PyObject *args)
+{
+ int regtype;
+
+ if (!PyArg_ParseTuple(args, "i", &regtype))
+ return NULL;
+
+ return PyUnicode_FromString(str_regtype(regtype));
+}
+
+static PyObject *py_get_predef_name(PyObject *self, PyObject *args)
+{
+ uint32_t hkey;
+ const char *str;
+
+ if (!PyArg_ParseTuple(args, "I", &hkey))
+ return NULL;
+
+ str = reg_get_predef_name(hkey);
+ if (str == NULL)
+ Py_RETURN_NONE;
+ return PyUnicode_FromString(str);
+}
+
+static PyMethodDef py_registry_methods[] = {
+ { "open_samba", PY_DISCARD_FUNC_SIG(PyCFunction, py_open_samba),
+ METH_VARARGS|METH_KEYWORDS, "open_samba() -> reg" },
+ { "open_ldb", PY_DISCARD_FUNC_SIG(PyCFunction, py_open_ldb_file),
+ METH_VARARGS|METH_KEYWORDS, "open_ldb(location, session_info=None, credentials=None, loadparm_context=None) -> key" },
+ { "open_hive", PY_DISCARD_FUNC_SIG(PyCFunction, py_open_hive),
+ METH_VARARGS|METH_KEYWORDS, "open_hive(location, session_info=None, credentials=None, loadparm_context=None) -> key" },
+ { "str_regtype", py_str_regtype, METH_VARARGS, "str_regtype(int) -> str" },
+ { "get_predef_name", py_get_predef_name, METH_VARARGS, "get_predef_name(hkey) -> str" },
+ {0}
+};
+
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "registry",
+ .m_doc = "Registry",
+ .m_size = -1,
+ .m_methods = py_registry_methods,
+};
+
+MODULE_INIT_FUNC(registry)
+{
+ PyObject *m;
+
+ if (pytalloc_BaseObject_PyType_Ready(&PyHiveKey) < 0)
+ return NULL;
+
+ if (pytalloc_BaseObject_PyType_Ready(&PyRegistry) < 0)
+ return NULL;
+
+ if (pytalloc_BaseObject_PyType_Ready(&PyRegistryKey) < 0)
+ return NULL;
+
+ m = PyModule_Create(&moduledef);
+ if (m == NULL)
+ return NULL;
+
+ PyModule_AddObject(m, "HKEY_CLASSES_ROOT", PyLong_FromLong(HKEY_CLASSES_ROOT));
+ PyModule_AddObject(m, "HKEY_CURRENT_USER", PyLong_FromLong(HKEY_CURRENT_USER));
+ PyModule_AddObject(m, "HKEY_LOCAL_MACHINE", PyLong_FromLong(HKEY_LOCAL_MACHINE));
+ PyModule_AddObject(m, "HKEY_USERS", PyLong_FromLong(HKEY_USERS));
+ PyModule_AddObject(m, "HKEY_PERFORMANCE_DATA", PyLong_FromLong(HKEY_PERFORMANCE_DATA));
+ PyModule_AddObject(m, "HKEY_CURRENT_CONFIG", PyLong_FromLong(HKEY_CURRENT_CONFIG));
+ PyModule_AddObject(m, "HKEY_DYN_DATA", PyLong_FromLong(HKEY_DYN_DATA));
+ PyModule_AddObject(m, "HKEY_PERFORMANCE_TEXT", PyLong_FromLong(HKEY_PERFORMANCE_TEXT));
+ PyModule_AddObject(m, "HKEY_PERFORMANCE_NLSTEXT", PyLong_FromLong(HKEY_PERFORMANCE_NLSTEXT));
+
+ Py_INCREF(&PyRegistry);
+ PyModule_AddObject(m, "Registry", (PyObject *)&PyRegistry);
+
+ Py_INCREF(&PyHiveKey);
+ PyModule_AddObject(m, "HiveKey", (PyObject *)&PyHiveKey);
+
+ Py_INCREF(&PyRegistryKey);
+ PyModule_AddObject(m, "RegistryKey", (PyObject *)&PyRegistryKey);
+
+ return m;
+}
diff --git a/source4/lib/registry/regf.c b/source4/lib/registry/regf.c
new file mode 100644
index 0000000..1e0b143
--- /dev/null
+++ b/source4/lib/registry/regf.c
@@ -0,0 +1,2319 @@
+/*
+ Samba CIFS implementation
+ Registry backend for REGF files
+ Copyright (C) 2005-2007 Jelmer Vernooij, jelmer@samba.org
+ Copyright (C) 2006-2010 Wilco Baan Hofman, wilco@baanhofman.nl
+
+ 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 "system/time.h"
+#include "lib/registry/tdr_regf.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "librpc/gen_ndr/winreg.h"
+#include "lib/registry/registry.h"
+#include "libcli/security/security.h"
+
+#undef strcasecmp
+
+static struct hive_operations reg_backend_regf;
+
+/**
+ * There are several places on the web where the REGF format is explained;
+ *
+ * TODO: Links
+ */
+
+/* TODO:
+ * - Return error codes that make more sense
+ * - Locking
+ * - do more things in-memory
+ */
+
+/*
+ * Read HBIN blocks into memory
+ */
+
+struct regf_data {
+ int fd;
+ struct hbin_block **hbins;
+ struct regf_hdr *header;
+ time_t last_write;
+};
+
+static WERROR regf_save_hbin(struct regf_data *data, bool flush);
+
+struct regf_key_data {
+ struct hive_key key;
+ struct regf_data *hive;
+ uint32_t offset;
+ struct nk_block *nk;
+};
+
+static struct hbin_block *hbin_by_offset(const struct regf_data *data,
+ uint32_t offset, uint32_t *rel_offset)
+{
+ unsigned int i;
+
+ for (i = 0; data->hbins[i]; i++) {
+ if (offset >= data->hbins[i]->offset_from_first &&
+ offset < data->hbins[i]->offset_from_first+
+ data->hbins[i]->offset_to_next) {
+ if (rel_offset != NULL)
+ *rel_offset = offset - data->hbins[i]->offset_from_first - 0x20;
+ return data->hbins[i];
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Validate a regf header
+ * For now, do nothing, but we should check the checksum
+ */
+static uint32_t regf_hdr_checksum(const uint8_t *buffer)
+{
+ uint32_t checksum = 0, x;
+ unsigned int i;
+
+ for (i = 0; i < 0x01FB; i+= 4) {
+ x = IVAL(buffer, i);
+ checksum ^= x;
+ }
+
+ return checksum;
+}
+
+/**
+ * Obtain the contents of a HBIN block
+ */
+static DATA_BLOB hbin_get(const struct regf_data *data, uint32_t offset)
+{
+ DATA_BLOB ret;
+ struct hbin_block *hbin;
+ uint32_t rel_offset;
+
+ ret.data = NULL;
+ ret.length = 0;
+
+ hbin = hbin_by_offset(data, offset, &rel_offset);
+
+ if (hbin == NULL) {
+ DEBUG(1, ("Can't find HBIN at 0x%04x\n", offset));
+ return ret;
+ }
+
+ ret.length = IVAL(hbin->data, rel_offset);
+ if (!(ret.length & 0x80000000)) {
+ DEBUG(0, ("Trying to use dirty block at 0x%04x\n", offset));
+ return ret;
+ }
+
+ /* remove high bit */
+ ret.length = (ret.length ^ 0xffffffff) + 1;
+
+ ret.length -= 4; /* 4 bytes for the length... */
+ ret.data = hbin->data +
+ (offset - hbin->offset_from_first - 0x20) + 4;
+
+ return ret;
+}
+
+static bool hbin_get_tdr(struct regf_data *regf, uint32_t offset,
+ TALLOC_CTX *ctx, tdr_pull_fn_t pull_fn, void *p)
+{
+ struct tdr_pull *pull = tdr_pull_init(regf);
+
+ pull->data = hbin_get(regf, offset);
+ if (!pull->data.data) {
+ DEBUG(1, ("Unable to get data at 0x%04x\n", offset));
+ talloc_free(pull);
+ return false;
+ }
+
+ if (NT_STATUS_IS_ERR(pull_fn(pull, ctx, p))) {
+ DEBUG(1, ("Error parsing record at 0x%04x using tdr\n",
+ offset));
+ talloc_free(pull);
+ return false;
+ }
+ talloc_free(pull);
+
+ return true;
+}
+
+/* Allocate some new data */
+static DATA_BLOB hbin_alloc(struct regf_data *data, uint32_t size,
+ uint32_t *offset)
+{
+ DATA_BLOB ret;
+ uint32_t rel_offset = (uint32_t) -1; /* Relative offset ! */
+ struct hbin_block *hbin = NULL;
+ unsigned int i;
+
+ if (offset != NULL) {
+ *offset = 0;
+ }
+
+ if (size == 0)
+ return data_blob(NULL, 0);
+
+ size += 4; /* Need to include int32 for the length */
+
+ /* Allocate as a multiple of 8 */
+ size = (size + 7) & ~7;
+
+ ret.data = NULL;
+ ret.length = 0;
+
+ for (i = 0; (hbin = data->hbins[i]); i++) {
+ int j;
+ int32_t my_size;
+ for (j = 0; j < hbin->offset_to_next-0x20; j+= my_size) {
+ my_size = IVALS(hbin->data, j);
+
+ if (my_size == 0x0) {
+ DEBUG(0, ("Invalid zero-length block! File is corrupt.\n"));
+ return ret;
+ }
+
+ if (my_size % 8 != 0) {
+ DEBUG(0, ("Encountered non-aligned block!\n"));
+ }
+
+ if (my_size < 0) { /* Used... */
+ my_size = -my_size;
+ } else if (my_size == size) { /* exact match */
+ rel_offset = j;
+ DEBUG(4, ("Found free block of exact size %d in middle of HBIN\n",
+ size));
+ break;
+ } else if (my_size > size) { /* data will remain */
+ rel_offset = j;
+ /* Split this block and mark the next block as free */
+ SIVAL(hbin->data, rel_offset+size, my_size-size);
+ DEBUG(4, ("Found free block of size %d (needing %d) in middle of HBIN\n",
+ my_size, size));
+ break;
+ }
+ }
+
+ if (rel_offset != -1)
+ break;
+ }
+
+ /* No space available in previous hbins,
+ * allocate new one */
+ if (data->hbins[i] == NULL) {
+ DEBUG(4, ("No space available in other HBINs for block of size %d, allocating new HBIN\n",
+ size));
+
+ /* Add extra hbin block */
+ data->hbins = talloc_realloc(data, data->hbins,
+ struct hbin_block *, i+2);
+ hbin = talloc(data->hbins, struct hbin_block);
+ SMB_ASSERT(hbin != NULL);
+
+ data->hbins[i] = hbin;
+ data->hbins[i+1] = NULL;
+
+ /* Set hbin data */
+ hbin->HBIN_ID = talloc_strdup(hbin, "hbin");
+ hbin->offset_from_first = (i == 0?0:data->hbins[i-1]->offset_from_first+data->hbins[i-1]->offset_to_next);
+ hbin->offset_to_next = 0x1000;
+ hbin->unknown[0] = 0;
+ hbin->unknown[1] = 0;
+ unix_to_nt_time(&hbin->last_change, time(NULL));
+ hbin->block_size = hbin->offset_to_next;
+ hbin->data = talloc_zero_array(hbin, uint8_t, hbin->block_size - 0x20);
+ /* Update the regf header */
+ data->header->last_block += hbin->offset_to_next;
+
+ /* Set the next block to it's proper size and set the
+ * rel_offset for this block */
+ SIVAL(hbin->data, size, hbin->block_size - size - 0x20);
+ rel_offset = 0x0;
+ }
+
+ /* Set size and mark as used */
+ SIVAL(hbin->data, rel_offset, -size);
+
+ ret.data = hbin->data + rel_offset + 0x4; /* Skip past length */
+ ret.length = size - 0x4;
+ if (offset) {
+ uint32_t new_rel_offset = 0;
+ *offset = hbin->offset_from_first + rel_offset + 0x20;
+ SMB_ASSERT(hbin_by_offset(data, *offset, &new_rel_offset) == hbin);
+ SMB_ASSERT(new_rel_offset == rel_offset);
+ }
+
+ return ret;
+}
+
+/* Store a data blob. Return the offset at which it was stored */
+static uint32_t hbin_store (struct regf_data *data, DATA_BLOB blob)
+{
+ uint32_t ret;
+ DATA_BLOB dest = hbin_alloc(data, blob.length, &ret);
+
+ memcpy(dest.data, blob.data, blob.length);
+
+ /* Make sure that we have no tailing garbage in the block */
+ if (dest.length > blob.length) {
+ memset(dest.data + blob.length, 0, dest.length - blob.length);
+ }
+
+ return ret;
+}
+
+static uint32_t hbin_store_tdr(struct regf_data *data,
+ tdr_push_fn_t push_fn, void *p)
+{
+ struct tdr_push *push = tdr_push_init(data);
+ uint32_t ret;
+
+ if (NT_STATUS_IS_ERR(push_fn(push, p))) {
+ DEBUG(0, ("Error during push\n"));
+ return -1;
+ }
+
+ ret = hbin_store(data, push->data);
+
+ talloc_free(push);
+
+ return ret;
+}
+
+
+/* Free existing data */
+static void hbin_free (struct regf_data *data, uint32_t offset)
+{
+ int32_t size;
+ uint32_t rel_offset;
+ int32_t next_size;
+ struct hbin_block *hbin;
+
+ SMB_ASSERT (offset > 0);
+
+ hbin = hbin_by_offset(data, offset, &rel_offset);
+
+ if (hbin == NULL)
+ return;
+
+ /* Get original size */
+ size = IVALS(hbin->data, rel_offset);
+
+ if (size > 0) {
+ DEBUG(1, ("Trying to free already freed block at 0x%04x\n",
+ offset));
+ return;
+ }
+ /* Mark as unused */
+ size = -size;
+
+ /* If the next block is free, merge into big free block */
+ if (rel_offset + size < hbin->offset_to_next - 0x20) {
+ next_size = IVALS(hbin->data, rel_offset+size);
+ if (next_size > 0) {
+ size += next_size;
+ }
+ }
+
+ /* Write block size */
+ SIVALS(hbin->data, rel_offset, size);
+}
+
+/**
+ * Store a data blob data was already stored, but has changed in size
+ * Will try to save it at the current location if possible, otherwise
+ * does a free + store */
+static uint32_t hbin_store_resize(struct regf_data *data,
+ uint32_t orig_offset, DATA_BLOB blob)
+{
+ uint32_t rel_offset;
+ struct hbin_block *hbin = hbin_by_offset(data, orig_offset,
+ &rel_offset);
+ int32_t my_size;
+ int32_t orig_size;
+ int32_t needed_size;
+ int32_t possible_size;
+ unsigned int i;
+
+ SMB_ASSERT(orig_offset > 0);
+
+ if (!hbin)
+ return hbin_store(data, blob);
+
+ /* Get original size */
+ orig_size = -IVALS(hbin->data, rel_offset);
+
+ needed_size = blob.length + 4; /* Add int32 containing length */
+ needed_size = (needed_size + 7) & ~7; /* Align */
+
+ /* Fits into current allocated block */
+ if (orig_size >= needed_size) {
+ memcpy(hbin->data + rel_offset + 0x4, blob.data, blob.length);
+ /* If the difference in size is greater than 0x4, split the block
+ * and free/merge it */
+ if (orig_size - needed_size > 0x4) {
+ SIVALS(hbin->data, rel_offset, -needed_size);
+ SIVALS(hbin->data, rel_offset + needed_size,
+ needed_size-orig_size);
+ hbin_free(data, orig_offset + needed_size);
+ }
+ return orig_offset;
+ }
+
+ possible_size = orig_size;
+
+ /* Check if it can be combined with the next few free records */
+ for (i = rel_offset; i < hbin->offset_to_next - 0x20; i += my_size) {
+ if (IVALS(hbin->data, i) < 0) /* Used */
+ break;
+
+ my_size = IVALS(hbin->data, i);
+
+ if (my_size == 0x0) {
+ DEBUG(0, ("Invalid zero-length block! File is corrupt.\n"));
+ break;
+ } else {
+ possible_size += my_size;
+ }
+
+ if (possible_size >= blob.length) {
+ SIVAL(hbin->data, rel_offset, -possible_size);
+ memcpy(hbin->data + rel_offset + 0x4,
+ blob.data, blob.length);
+ return orig_offset;
+ }
+ }
+
+ hbin_free(data, orig_offset);
+ return hbin_store(data, blob);
+}
+
+static uint32_t hbin_store_tdr_resize(struct regf_data *regf,
+ tdr_push_fn_t push_fn,
+ uint32_t orig_offset, void *p)
+{
+ struct tdr_push *push = tdr_push_init(regf);
+ uint32_t ret;
+
+ if (NT_STATUS_IS_ERR(push_fn(push, p))) {
+ DEBUG(0, ("Error during push\n"));
+ return -1;
+ }
+
+ ret = hbin_store_resize(regf, orig_offset, push->data);
+
+ talloc_free(push);
+
+ return ret;
+}
+
+static uint32_t regf_create_lh_hash(const char *name)
+{
+ char *hash_name;
+ uint32_t ret = 0;
+ uint16_t i;
+
+ hash_name = strupper_talloc(NULL, name);
+ for (i = 0; *(hash_name + i) != 0; i++) {
+ ret *= 37;
+ ret += *(hash_name + i);
+ }
+ talloc_free(hash_name);
+ return ret;
+}
+
+static WERROR regf_get_info(TALLOC_CTX *mem_ctx,
+ const struct hive_key *key,
+ const char **classname,
+ uint32_t *num_subkeys,
+ uint32_t *num_values,
+ NTTIME *last_mod_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize)
+{
+ const struct regf_key_data *private_data =
+ (const struct regf_key_data *)key;
+
+ if (num_subkeys != NULL)
+ *num_subkeys = private_data->nk->num_subkeys;
+
+ if (num_values != NULL)
+ *num_values = private_data->nk->num_values;
+
+ if (classname != NULL) {
+ if (private_data->nk->clsname_offset != -1) {
+ DATA_BLOB data = hbin_get(private_data->hive,
+ private_data->nk->clsname_offset);
+ *classname = talloc_strndup(mem_ctx,
+ (char*)data.data,
+ private_data->nk->clsname_length);
+ W_ERROR_HAVE_NO_MEMORY(*classname);
+ } else
+ *classname = NULL;
+ }
+
+ /* TODO: Last mod time */
+
+ /* TODO: max valnamelen */
+
+ /* TODO: max valbufsize */
+
+ /* TODO: max subkeynamelen */
+
+ return WERR_OK;
+}
+
+static struct regf_key_data *regf_get_key(TALLOC_CTX *ctx,
+ struct regf_data *regf,
+ uint32_t offset)
+{
+ struct nk_block *nk;
+ struct regf_key_data *ret;
+
+ ret = talloc_zero(ctx, struct regf_key_data);
+ ret->key.ops = &reg_backend_regf;
+ ret->hive = talloc_reference(ret, regf);
+ ret->offset = offset;
+ nk = talloc(ret, struct nk_block);
+ if (nk == NULL)
+ return NULL;
+
+ ret->nk = nk;
+
+ if (!hbin_get_tdr(regf, offset, nk,
+ (tdr_pull_fn_t)tdr_pull_nk_block, nk)) {
+ DEBUG(0, ("Unable to find HBIN data for offset 0x%x\n", offset));
+ return NULL;
+ }
+
+ if (strcmp(nk->header, "nk") != 0) {
+ DEBUG(0, ("Expected nk record, got %s\n", nk->header));
+ talloc_free(ret);
+ return NULL;
+ }
+
+ return ret;
+}
+
+
+static WERROR regf_get_value(TALLOC_CTX *ctx, struct hive_key *key,
+ uint32_t idx, const char **name,
+ uint32_t *data_type, DATA_BLOB *data)
+{
+ const struct regf_key_data *private_data =
+ (const struct regf_key_data *)key;
+ struct vk_block *vk;
+ struct regf_data *regf = private_data->hive;
+ uint32_t vk_offset;
+ DATA_BLOB tmp;
+
+ if (idx >= private_data->nk->num_values)
+ return WERR_NO_MORE_ITEMS;
+
+ tmp = hbin_get(regf, private_data->nk->values_offset);
+ if (!tmp.data) {
+ DEBUG(0, ("Unable to find value list at 0x%x\n",
+ private_data->nk->values_offset));
+ return WERR_GEN_FAILURE;
+ }
+
+ if (tmp.length < private_data->nk->num_values * 4) {
+ DEBUG(1, ("Value counts mismatch\n"));
+ }
+
+ vk_offset = IVAL(tmp.data, idx * 4);
+
+ vk = talloc(NULL, struct vk_block);
+ W_ERROR_HAVE_NO_MEMORY(vk);
+
+ if (!hbin_get_tdr(regf, vk_offset, vk,
+ (tdr_pull_fn_t)tdr_pull_vk_block, vk)) {
+ DEBUG(0, ("Unable to get VK block at 0x%x\n", vk_offset));
+ talloc_free(vk);
+ return WERR_GEN_FAILURE;
+ }
+
+ /* FIXME: name character set ?*/
+ if (name != NULL) {
+ *name = talloc_strndup(ctx, vk->data_name, vk->name_length);
+ W_ERROR_HAVE_NO_MEMORY(*name);
+ }
+
+ if (data_type != NULL)
+ *data_type = vk->data_type;
+
+ if (vk->data_length & 0x80000000) {
+ /* this is data of type "REG_DWORD" or "REG_DWORD_BIG_ENDIAN" */
+ data->data = talloc_size(ctx, sizeof(uint32_t));
+ W_ERROR_HAVE_NO_MEMORY(data->data);
+ SIVAL(data->data, 0, vk->data_offset);
+ data->length = sizeof(uint32_t);
+ } else {
+ *data = hbin_get(regf, vk->data_offset);
+ }
+
+ if (data->length < vk->data_length) {
+ DEBUG(1, ("Read data less than indicated data length!\n"));
+ }
+
+ talloc_free(vk);
+
+ return WERR_OK;
+}
+
+static WERROR regf_get_value_by_name(TALLOC_CTX *mem_ctx,
+ struct hive_key *key, const char *name,
+ uint32_t *type, DATA_BLOB *data)
+{
+ unsigned int i;
+ const char *vname;
+ WERROR error;
+
+ /* FIXME: Do binary search? Is this list sorted at all? */
+
+ for (i = 0; W_ERROR_IS_OK(error = regf_get_value(mem_ctx, key, i,
+ &vname, type, data));
+ i++) {
+ if (!strcmp(vname, name))
+ return WERR_OK;
+ }
+
+ if (W_ERROR_EQUAL(error, WERR_NO_MORE_ITEMS))
+ return WERR_FILE_NOT_FOUND;
+
+ return error;
+}
+
+
+static WERROR regf_get_subkey_by_index(TALLOC_CTX *ctx,
+ const struct hive_key *key,
+ uint32_t idx, const char **name,
+ const char **classname,
+ NTTIME *last_mod_time)
+{
+ DATA_BLOB data;
+ struct regf_key_data *ret;
+ const struct regf_key_data *private_data = (const struct regf_key_data *)key;
+ struct nk_block *nk = private_data->nk;
+ uint32_t key_off=0;
+
+ if (idx >= nk->num_subkeys)
+ return WERR_NO_MORE_ITEMS;
+
+ /* Make sure that we don't crash if the key is empty */
+ if (nk->subkeys_offset == -1) {
+ return WERR_NO_MORE_ITEMS;
+ }
+
+ data = hbin_get(private_data->hive, nk->subkeys_offset);
+ if (!data.data) {
+ DEBUG(0, ("Unable to find subkey list at 0x%x\n",
+ nk->subkeys_offset));
+ return WERR_GEN_FAILURE;
+ }
+
+ if (!strncmp((char *)data.data, "li", 2)) {
+ struct li_block li;
+ struct tdr_pull *pull = tdr_pull_init(private_data->hive);
+
+ DEBUG(10, ("Subkeys in LI list\n"));
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_li_block(pull, nk, &li))) {
+ DEBUG(0, ("Error parsing LI list\n"));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+ talloc_free(pull);
+ SMB_ASSERT(!strncmp(li.header, "li", 2));
+
+ if (li.key_count != nk->num_subkeys) {
+ DEBUG(0, ("Subkey counts don't match\n"));
+ return WERR_GEN_FAILURE;
+ }
+ key_off = li.nk_offset[idx];
+
+ } else if (!strncmp((char *)data.data, "lf", 2)) {
+ struct lf_block lf;
+ struct tdr_pull *pull = tdr_pull_init(private_data->hive);
+
+ DEBUG(10, ("Subkeys in LF list\n"));
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lf_block(pull, nk, &lf))) {
+ DEBUG(0, ("Error parsing LF list\n"));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+ talloc_free(pull);
+ SMB_ASSERT(!strncmp(lf.header, "lf", 2));
+
+ if (lf.key_count != nk->num_subkeys) {
+ DEBUG(0, ("Subkey counts don't match\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ key_off = lf.hr[idx].nk_offset;
+ } else if (!strncmp((char *)data.data, "lh", 2)) {
+ struct lh_block lh;
+ struct tdr_pull *pull = tdr_pull_init(private_data->hive);
+
+ DEBUG(10, ("Subkeys in LH list\n"));
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lh_block(pull, nk, &lh))) {
+ DEBUG(0, ("Error parsing LH list\n"));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+ talloc_free(pull);
+ SMB_ASSERT(!strncmp(lh.header, "lh", 2));
+
+ if (lh.key_count != nk->num_subkeys) {
+ DEBUG(0, ("Subkey counts don't match\n"));
+ return WERR_GEN_FAILURE;
+ }
+ key_off = lh.hr[idx].nk_offset;
+ } else if (!strncmp((char *)data.data, "ri", 2)) {
+ struct ri_block ri;
+ struct tdr_pull *pull = tdr_pull_init(ctx);
+ uint16_t i;
+ uint16_t sublist_count = 0;
+
+ DEBUG(10, ("Subkeys in RI list\n"));
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_ri_block(pull, nk, &ri))) {
+ DEBUG(0, ("Error parsing RI list\n"));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+ SMB_ASSERT(!strncmp(ri.header, "ri", 2));
+
+ for (i = 0; i < ri.key_count; i++) {
+ DATA_BLOB list_data;
+
+ /* Get sublist data blob */
+ list_data = hbin_get(private_data->hive, ri.offset[i]);
+ if (!list_data.data) {
+ DEBUG(0, ("Error getting RI list."));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+
+ pull->data = list_data;
+
+ if (!strncmp((char *)list_data.data, "li", 2)) {
+ struct li_block li;
+
+ DEBUG(10, ("Subkeys in RI->LI list\n"));
+
+ if (NT_STATUS_IS_ERR(tdr_pull_li_block(pull,
+ nk,
+ &li))) {
+ DEBUG(0, ("Error parsing LI list from RI\n"));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+ SMB_ASSERT(!strncmp(li.header, "li", 2));
+
+ /* Advance to next sublist if necessary */
+ if (idx >= sublist_count + li.key_count) {
+ sublist_count += li.key_count;
+ continue;
+ }
+ key_off = li.nk_offset[idx - sublist_count];
+ sublist_count += li.key_count;
+ break;
+ } else if (!strncmp((char *)list_data.data, "lh", 2)) {
+ struct lh_block lh;
+
+ DEBUG(10, ("Subkeys in RI->LH list\n"));
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lh_block(pull,
+ nk,
+ &lh))) {
+ DEBUG(0, ("Error parsing LH list from RI\n"));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+ SMB_ASSERT(!strncmp(lh.header, "lh", 2));
+
+ /* Advance to next sublist if necessary */
+ if (idx >= sublist_count + lh.key_count) {
+ sublist_count += lh.key_count;
+ continue;
+ }
+ key_off = lh.hr[idx - sublist_count].nk_offset;
+ sublist_count += lh.key_count;
+ break;
+ } else {
+ DEBUG(0,("Unknown sublist in ri block\n"));
+ talloc_free(pull);
+
+ return WERR_GEN_FAILURE;
+ }
+
+ }
+ talloc_free(pull);
+
+
+ if (idx > sublist_count) {
+ return WERR_NO_MORE_ITEMS;
+ }
+
+ } else {
+ DEBUG(0, ("Unknown type for subkey list (0x%04x): %c%c\n",
+ nk->subkeys_offset, data.data[0], data.data[1]));
+ return WERR_GEN_FAILURE;
+ }
+
+ ret = regf_get_key (ctx, private_data->hive, key_off);
+
+ if (classname != NULL) {
+ if (ret->nk->clsname_offset != -1) {
+ DATA_BLOB db = hbin_get(ret->hive,
+ ret->nk->clsname_offset);
+ *classname = talloc_strndup(ctx,
+ (char*)db.data,
+ ret->nk->clsname_length);
+ W_ERROR_HAVE_NO_MEMORY(*classname);
+ } else
+ *classname = NULL;
+ }
+
+ if (last_mod_time != NULL)
+ *last_mod_time = ret->nk->last_change;
+
+ if (name != NULL)
+ *name = talloc_steal(ctx, ret->nk->key_name);
+
+ talloc_free(ret);
+
+ return WERR_OK;
+}
+
+static WERROR regf_match_subkey_by_name(TALLOC_CTX *ctx,
+ const struct hive_key *key,
+ uint32_t offset,
+ const char *name, uint32_t *ret)
+{
+ DATA_BLOB subkey_data;
+ struct nk_block subkey;
+ struct tdr_pull *pull;
+ const struct regf_key_data *private_data =
+ (const struct regf_key_data *)key;
+
+ subkey_data = hbin_get(private_data->hive, offset);
+ if (!subkey_data.data) {
+ DEBUG(0, ("Unable to retrieve subkey HBIN\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ pull = tdr_pull_init(ctx);
+
+ pull->data = subkey_data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_nk_block(pull, ctx, &subkey))) {
+ DEBUG(0, ("Error parsing NK structure.\n"));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+ talloc_free(pull);
+
+ if (strncmp(subkey.header, "nk", 2)) {
+ DEBUG(0, ("Not an NK structure.\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ if (!strcasecmp(subkey.key_name, name)) {
+ *ret = offset;
+ } else {
+ *ret = 0;
+ }
+ return WERR_OK;
+}
+
+static WERROR regf_get_subkey_by_name(TALLOC_CTX *ctx,
+ const struct hive_key *key,
+ const char *name,
+ struct hive_key **ret)
+{
+ DATA_BLOB data;
+ const struct regf_key_data *private_data =
+ (const struct regf_key_data *)key;
+ struct nk_block *nk = private_data->nk;
+ uint32_t key_off = 0;
+
+ /* Make sure that we don't crash if the key is empty */
+ if (nk->subkeys_offset == -1) {
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ data = hbin_get(private_data->hive, nk->subkeys_offset);
+ if (!data.data) {
+ DEBUG(0, ("Unable to find subkey list\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ if (!strncmp((char *)data.data, "li", 2)) {
+ struct li_block li;
+ struct tdr_pull *pull = tdr_pull_init(ctx);
+ uint16_t i;
+
+ DEBUG(10, ("Subkeys in LI list\n"));
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_li_block(pull, nk, &li))) {
+ DEBUG(0, ("Error parsing LI list\n"));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+ talloc_free(pull);
+ SMB_ASSERT(!strncmp(li.header, "li", 2));
+
+ if (li.key_count != nk->num_subkeys) {
+ DEBUG(0, ("Subkey counts don't match\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ for (i = 0; i < li.key_count; i++) {
+ W_ERROR_NOT_OK_RETURN(regf_match_subkey_by_name(nk, key,
+ li.nk_offset[i],
+ name,
+ &key_off));
+ if (key_off != 0)
+ break;
+ }
+ if (key_off == 0)
+ return WERR_FILE_NOT_FOUND;
+ } else if (!strncmp((char *)data.data, "lf", 2)) {
+ struct lf_block lf;
+ struct tdr_pull *pull = tdr_pull_init(ctx);
+ uint16_t i;
+
+ DEBUG(10, ("Subkeys in LF list\n"));
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lf_block(pull, nk, &lf))) {
+ DEBUG(0, ("Error parsing LF list\n"));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+ talloc_free(pull);
+ SMB_ASSERT(!strncmp(lf.header, "lf", 2));
+
+ if (lf.key_count != nk->num_subkeys) {
+ DEBUG(0, ("Subkey counts don't match\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ for (i = 0; i < lf.key_count; i++) {
+ if (strncmp(lf.hr[i].hash, name, 4)) {
+ continue;
+ }
+ W_ERROR_NOT_OK_RETURN(regf_match_subkey_by_name(nk,
+ key,
+ lf.hr[i].nk_offset,
+ name,
+ &key_off));
+ if (key_off != 0)
+ break;
+ }
+ if (key_off == 0)
+ return WERR_FILE_NOT_FOUND;
+ } else if (!strncmp((char *)data.data, "lh", 2)) {
+ struct lh_block lh;
+ struct tdr_pull *pull = tdr_pull_init(ctx);
+ uint16_t i;
+ uint32_t hash;
+
+ DEBUG(10, ("Subkeys in LH list\n"));
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lh_block(pull, nk, &lh))) {
+ DEBUG(0, ("Error parsing LH list\n"));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+ talloc_free(pull);
+ SMB_ASSERT(!strncmp(lh.header, "lh", 2));
+
+ if (lh.key_count != nk->num_subkeys) {
+ DEBUG(0, ("Subkey counts don't match\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ hash = regf_create_lh_hash(name);
+ for (i = 0; i < lh.key_count; i++) {
+ if (lh.hr[i].base37 != hash) {
+ continue;
+ }
+ W_ERROR_NOT_OK_RETURN(regf_match_subkey_by_name(nk,
+ key,
+ lh.hr[i].nk_offset,
+ name,
+ &key_off));
+ if (key_off != 0)
+ break;
+ }
+ if (key_off == 0)
+ return WERR_FILE_NOT_FOUND;
+ } else if (!strncmp((char *)data.data, "ri", 2)) {
+ struct ri_block ri;
+ struct tdr_pull *pull = tdr_pull_init(ctx);
+ uint16_t i, j;
+
+ DEBUG(10, ("Subkeys in RI list\n"));
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_ri_block(pull, nk, &ri))) {
+ DEBUG(0, ("Error parsing RI list\n"));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+ SMB_ASSERT(!strncmp(ri.header, "ri", 2));
+
+ for (i = 0; i < ri.key_count; i++) {
+ DATA_BLOB list_data;
+
+ /* Get sublist data blob */
+ list_data = hbin_get(private_data->hive, ri.offset[i]);
+ if (list_data.data == NULL) {
+ DEBUG(0, ("Error getting RI list."));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+
+ pull->data = list_data;
+
+ if (!strncmp((char *)list_data.data, "li", 2)) {
+ struct li_block li;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_li_block(pull,
+ nk,
+ &li))) {
+ DEBUG(0, ("Error parsing LI list from RI\n"));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+ SMB_ASSERT(!strncmp(li.header, "li", 2));
+
+ for (j = 0; j < li.key_count; j++) {
+ W_ERROR_NOT_OK_RETURN(regf_match_subkey_by_name(nk, key,
+ li.nk_offset[j],
+ name,
+ &key_off));
+ if (key_off)
+ break;
+ }
+ } else if (!strncmp((char *)list_data.data, "lh", 2)) {
+ struct lh_block lh;
+ uint32_t hash;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lh_block(pull,
+ nk,
+ &lh))) {
+ DEBUG(0, ("Error parsing LH list from RI\n"));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+ SMB_ASSERT(!strncmp(lh.header, "lh", 2));
+
+ hash = regf_create_lh_hash(name);
+ for (j = 0; j < lh.key_count; j++) {
+ if (lh.hr[j].base37 != hash) {
+ continue;
+ }
+ W_ERROR_NOT_OK_RETURN(regf_match_subkey_by_name(nk, key,
+ lh.hr[j].nk_offset,
+ name,
+ &key_off));
+ if (key_off)
+ break;
+ }
+ }
+ if (key_off)
+ break;
+ }
+ talloc_free(pull);
+ if (!key_off)
+ return WERR_FILE_NOT_FOUND;
+ } else {
+ DEBUG(0, ("Unknown subkey list type.\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ *ret = (struct hive_key *)regf_get_key(ctx, private_data->hive,
+ key_off);
+ return WERR_OK;
+}
+
+static WERROR regf_set_sec_desc(struct hive_key *key,
+ const struct security_descriptor *sec_desc)
+{
+ const struct regf_key_data *private_data =
+ (const struct regf_key_data *)key;
+ struct sk_block cur_sk, sk, new_sk;
+ struct regf_data *regf = private_data->hive;
+ struct nk_block root;
+ DATA_BLOB data;
+ uint32_t sk_offset, cur_sk_offset;
+ bool update_cur_sk = false;
+
+ /* Get the root nk */
+ hbin_get_tdr(regf, regf->header->data_offset, regf,
+ (tdr_pull_fn_t) tdr_pull_nk_block, &root);
+
+ /* Push the security descriptor to a blob */
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_struct_blob(&data, regf,
+ sec_desc, (ndr_push_flags_fn_t)ndr_push_security_descriptor))) {
+ DEBUG(0, ("Unable to push security descriptor\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ /* Get the current security descriptor for the key */
+ if (!hbin_get_tdr(regf, private_data->nk->sk_offset, regf,
+ (tdr_pull_fn_t) tdr_pull_sk_block, &cur_sk)) {
+ DEBUG(0, ("Unable to find security descriptor for current key\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+ /* If there's no change, change nothing. */
+ if (memcmp(data.data, cur_sk.sec_desc,
+ MIN(data.length, cur_sk.rec_size)) == 0) {
+ return WERR_OK;
+ }
+
+ /* Delete the current sk if only this key is using it */
+ if (cur_sk.ref_cnt == 1) {
+ /* Get the previous security descriptor for the key */
+ if (!hbin_get_tdr(regf, cur_sk.prev_offset, regf,
+ (tdr_pull_fn_t) tdr_pull_sk_block, &sk)) {
+ DEBUG(0, ("Unable to find prev security descriptor for current key\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+ /* Change and store the previous security descriptor */
+ sk.next_offset = cur_sk.next_offset;
+ hbin_store_tdr_resize(regf, (tdr_push_fn_t) tdr_push_sk_block,
+ cur_sk.prev_offset, &sk);
+
+ /* Get the next security descriptor for the key */
+ if (!hbin_get_tdr(regf, cur_sk.next_offset, regf,
+ (tdr_pull_fn_t) tdr_pull_sk_block, &sk)) {
+ DEBUG(0, ("Unable to find next security descriptor for current key\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+ /* Change and store the next security descriptor */
+ sk.prev_offset = cur_sk.prev_offset;
+ hbin_store_tdr_resize(regf, (tdr_push_fn_t) tdr_push_sk_block,
+ cur_sk.next_offset, &sk);
+
+ hbin_free(regf, private_data->nk->sk_offset);
+ } else {
+ /* This key will no longer be referring to this sk */
+ cur_sk.ref_cnt--;
+ update_cur_sk = true;
+ }
+
+ sk_offset = root.sk_offset;
+
+ do {
+ cur_sk_offset = sk_offset;
+ if (!hbin_get_tdr(regf, sk_offset, regf,
+ (tdr_pull_fn_t) tdr_pull_sk_block, &sk)) {
+ DEBUG(0, ("Unable to find security descriptor\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+ if (memcmp(data.data, sk.sec_desc, MIN(data.length, sk.rec_size)) == 0) {
+ private_data->nk->sk_offset = sk_offset;
+ sk.ref_cnt++;
+ hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_sk_block,
+ sk_offset, &sk);
+ hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_nk_block,
+ private_data->offset,
+ private_data->nk);
+ return WERR_OK;
+ }
+ sk_offset = sk.next_offset;
+ } while (sk_offset != root.sk_offset);
+
+ ZERO_STRUCT(new_sk);
+ new_sk.header = "sk";
+ new_sk.prev_offset = cur_sk_offset;
+ new_sk.next_offset = root.sk_offset;
+ new_sk.ref_cnt = 1;
+ new_sk.rec_size = data.length;
+ new_sk.sec_desc = data.data;
+
+ sk_offset = hbin_store_tdr(regf,
+ (tdr_push_fn_t) tdr_push_sk_block,
+ &new_sk);
+ if (sk_offset == -1) {
+ DEBUG(0, ("Error storing sk block\n"));
+ return WERR_GEN_FAILURE;
+ }
+ private_data->nk->sk_offset = sk_offset;
+
+ if (update_cur_sk) {
+ hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_sk_block,
+ private_data->nk->sk_offset, &cur_sk);
+ }
+
+ /* Get the previous security descriptor for the key */
+ if (!hbin_get_tdr(regf, new_sk.prev_offset, regf,
+ (tdr_pull_fn_t) tdr_pull_sk_block, &sk)) {
+ DEBUG(0, ("Unable to find security descriptor for previous key\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+ /* Change and store the previous security descriptor */
+ sk.next_offset = sk_offset;
+ hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_sk_block,
+ cur_sk.prev_offset, &sk);
+
+ /* Get the next security descriptor for the key (always root, as we append) */
+ if (!hbin_get_tdr(regf, new_sk.next_offset, regf,
+ (tdr_pull_fn_t) tdr_pull_sk_block, &sk)) {
+ DEBUG(0, ("Unable to find security descriptor for current key\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+ /* Change and store the next security descriptor (always root, as we append) */
+ sk.prev_offset = sk_offset;
+ hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_sk_block,
+ root.sk_offset, &sk);
+
+
+ /* Store the nk. */
+ hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_sk_block,
+ private_data->offset, private_data->nk);
+ return WERR_OK;
+}
+
+static WERROR regf_get_sec_desc(TALLOC_CTX *ctx, const struct hive_key *key,
+ struct security_descriptor **sd)
+{
+ const struct regf_key_data *private_data =
+ (const struct regf_key_data *)key;
+ struct sk_block sk;
+ struct regf_data *regf = private_data->hive;
+ DATA_BLOB data;
+
+ if (!hbin_get_tdr(regf, private_data->nk->sk_offset, ctx,
+ (tdr_pull_fn_t) tdr_pull_sk_block, &sk)) {
+ DEBUG(0, ("Unable to find security descriptor\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ if (strcmp(sk.header, "sk") != 0) {
+ DEBUG(0, ("Expected 'sk', got '%s'\n", sk.header));
+ return WERR_GEN_FAILURE;
+ }
+
+ *sd = talloc(ctx, struct security_descriptor);
+ W_ERROR_HAVE_NO_MEMORY(*sd);
+
+ data.data = sk.sec_desc;
+ data.length = sk.rec_size;
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_pull_struct_blob(&data, ctx, *sd,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor))) {
+ DEBUG(0, ("Error parsing security descriptor\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR regf_sl_add_entry(struct regf_data *regf, uint32_t list_offset,
+ const char *name,
+ uint32_t key_offset, uint32_t *ret)
+{
+ DATA_BLOB data;
+
+ /* Create a new key if necessary */
+ if (list_offset == -1) {
+ if (regf->header->version.major != 1) {
+ DEBUG(0, ("Can't store keys in unknown registry format\n"));
+ return WERR_NOT_SUPPORTED;
+ }
+ if (regf->header->version.minor < 3) {
+ /* Store LI */
+ struct li_block li;
+ ZERO_STRUCT(li);
+ li.header = "li";
+ li.key_count = 1;
+
+ li.nk_offset = talloc_array(regf, uint32_t, 1);
+ W_ERROR_HAVE_NO_MEMORY(li.nk_offset);
+ li.nk_offset[0] = key_offset;
+
+ *ret = hbin_store_tdr(regf,
+ (tdr_push_fn_t) tdr_push_li_block,
+ &li);
+
+ talloc_free(li.nk_offset);
+ } else if (regf->header->version.minor == 3 ||
+ regf->header->version.minor == 4) {
+ /* Store LF */
+ struct lf_block lf;
+ ZERO_STRUCT(lf);
+ lf.header = "lf";
+ lf.key_count = 1;
+
+ lf.hr = talloc_array(regf, struct hash_record, 1);
+ W_ERROR_HAVE_NO_MEMORY(lf.hr);
+ lf.hr[0].nk_offset = key_offset;
+ lf.hr[0].hash = talloc_strndup(lf.hr, name, 4);
+ W_ERROR_HAVE_NO_MEMORY(lf.hr[0].hash);
+
+ *ret = hbin_store_tdr(regf,
+ (tdr_push_fn_t) tdr_push_lf_block,
+ &lf);
+
+ talloc_free(lf.hr);
+ } else if (regf->header->version.minor == 5) {
+ /* Store LH */
+ struct lh_block lh;
+ ZERO_STRUCT(lh);
+ lh.header = "lh";
+ lh.key_count = 1;
+
+ lh.hr = talloc_array(regf, struct lh_hash, 1);
+ W_ERROR_HAVE_NO_MEMORY(lh.hr);
+ lh.hr[0].nk_offset = key_offset;
+ lh.hr[0].base37 = regf_create_lh_hash(name);
+
+ *ret = hbin_store_tdr(regf,
+ (tdr_push_fn_t) tdr_push_lh_block,
+ &lh);
+
+ talloc_free(lh.hr);
+ }
+ return WERR_OK;
+ }
+
+ data = hbin_get(regf, list_offset);
+ if (!data.data) {
+ DEBUG(0, ("Unable to find subkey list\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ if (!strncmp((char *)data.data, "li", 2)) {
+ struct tdr_pull *pull = tdr_pull_init(regf);
+ struct li_block li;
+ struct nk_block sub_nk;
+ int32_t i, j;
+
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_li_block(pull, regf, &li))) {
+ DEBUG(0, ("Error parsing LI list\n"));
+ talloc_free(pull);
+ return WERR_FILE_NOT_FOUND;
+ }
+ talloc_free(pull);
+
+ if (strncmp(li.header, "li", 2) != 0) {
+ abort();
+ DEBUG(0, ("LI header corrupt\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ /*
+ * Find the position to store the pointer
+ * Extensive testing reveils that at least on windows 7 subkeys
+ * *MUST* be stored in alphabetical order
+ */
+ for (i = 0; i < li.key_count; i++) {
+ /* Get the nk */
+ hbin_get_tdr(regf, li.nk_offset[i], regf,
+ (tdr_pull_fn_t) tdr_pull_nk_block, &sub_nk);
+ if (strcasecmp(name, sub_nk.key_name) < 0) {
+ break;
+ }
+ }
+
+ li.nk_offset = talloc_realloc(regf, li.nk_offset,
+ uint32_t, li.key_count+1);
+ W_ERROR_HAVE_NO_MEMORY(li.nk_offset);
+
+ /* Move everything behind this offset */
+ for (j = li.key_count - 1; j >= i; j--) {
+ li.nk_offset[j+1] = li.nk_offset[j];
+ }
+
+ li.nk_offset[i] = key_offset;
+ li.key_count++;
+ *ret = hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t)tdr_push_li_block,
+ list_offset, &li);
+
+ talloc_free(li.nk_offset);
+ } else if (!strncmp((char *)data.data, "lf", 2)) {
+ struct tdr_pull *pull = tdr_pull_init(regf);
+ struct lf_block lf;
+ struct nk_block sub_nk;
+ int32_t i, j;
+
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lf_block(pull, regf, &lf))) {
+ DEBUG(0, ("Error parsing LF list\n"));
+ talloc_free(pull);
+ return WERR_FILE_NOT_FOUND;
+ }
+ talloc_free(pull);
+ SMB_ASSERT(!strncmp(lf.header, "lf", 2));
+
+ /*
+ * Find the position to store the hash record
+ * Extensive testing reveils that at least on windows 7 subkeys
+ * *MUST* be stored in alphabetical order
+ */
+ for (i = 0; i < lf.key_count; i++) {
+ /* Get the nk */
+ hbin_get_tdr(regf, lf.hr[i].nk_offset, regf,
+ (tdr_pull_fn_t) tdr_pull_nk_block, &sub_nk);
+ if (strcasecmp(name, sub_nk.key_name) < 0) {
+ break;
+ }
+ }
+
+ lf.hr = talloc_realloc(regf, lf.hr, struct hash_record,
+ lf.key_count+1);
+ W_ERROR_HAVE_NO_MEMORY(lf.hr);
+
+ /* Move everything behind this hash record */
+ for (j = lf.key_count - 1; j >= i; j--) {
+ lf.hr[j+1] = lf.hr[j];
+ }
+
+ lf.hr[i].nk_offset = key_offset;
+ lf.hr[i].hash = talloc_strndup(lf.hr, name, 4);
+ W_ERROR_HAVE_NO_MEMORY(lf.hr[lf.key_count].hash);
+ lf.key_count++;
+ *ret = hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t)tdr_push_lf_block,
+ list_offset, &lf);
+
+ talloc_free(lf.hr);
+ } else if (!strncmp((char *)data.data, "lh", 2)) {
+ struct tdr_pull *pull = tdr_pull_init(regf);
+ struct lh_block lh;
+ struct nk_block sub_nk;
+ int32_t i, j;
+
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lh_block(pull, regf, &lh))) {
+ DEBUG(0, ("Error parsing LH list\n"));
+ talloc_free(pull);
+ return WERR_FILE_NOT_FOUND;
+ }
+ talloc_free(pull);
+ SMB_ASSERT(!strncmp(lh.header, "lh", 2));
+
+ /*
+ * Find the position to store the hash record
+ * Extensive testing reveils that at least on windows 7 subkeys
+ * *MUST* be stored in alphabetical order
+ */
+ for (i = 0; i < lh.key_count; i++) {
+ /* Get the nk */
+ hbin_get_tdr(regf, lh.hr[i].nk_offset, regf,
+ (tdr_pull_fn_t) tdr_pull_nk_block, &sub_nk);
+ if (strcasecmp(name, sub_nk.key_name) < 0) {
+ break;
+ }
+ }
+
+ lh.hr = talloc_realloc(regf, lh.hr, struct lh_hash,
+ lh.key_count+1);
+ W_ERROR_HAVE_NO_MEMORY(lh.hr);
+
+ /* Move everything behind this hash record */
+ for (j = lh.key_count - 1; j >= i; j--) {
+ lh.hr[j+1] = lh.hr[j];
+ }
+
+ lh.hr[i].nk_offset = key_offset;
+ lh.hr[i].base37 = regf_create_lh_hash(name);
+ lh.key_count++;
+ *ret = hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t)tdr_push_lh_block,
+ list_offset, &lh);
+
+ talloc_free(lh.hr);
+ } else if (!strncmp((char *)data.data, "ri", 2)) {
+ /* FIXME */
+ DEBUG(0, ("Adding to 'ri' subkey list is not supported yet.\n"));
+ return WERR_NOT_SUPPORTED;
+ } else {
+ DEBUG(0, ("Cannot add to unknown subkey list\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR regf_sl_del_entry(struct regf_data *regf, uint32_t list_offset,
+ uint32_t key_offset, uint32_t *ret)
+{
+ DATA_BLOB data;
+
+ data = hbin_get(regf, list_offset);
+ if (!data.data) {
+ DEBUG(0, ("Unable to find subkey list\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ if (strncmp((char *)data.data, "li", 2) == 0) {
+ struct li_block li;
+ struct tdr_pull *pull = tdr_pull_init(regf);
+ uint16_t i;
+ bool found_offset = false;
+
+ DEBUG(10, ("Subkeys in LI list\n"));
+
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_li_block(pull, regf, &li))) {
+ DEBUG(0, ("Error parsing LI list\n"));
+ talloc_free(pull);
+ return WERR_FILE_NOT_FOUND;
+ }
+ talloc_free(pull);
+
+ SMB_ASSERT(!strncmp(li.header, "li", 2));
+
+ for (i = 0; i < li.key_count; i++) {
+ if (found_offset) {
+ li.nk_offset[i-1] = li.nk_offset[i];
+ }
+ if (li.nk_offset[i] == key_offset) {
+ found_offset = true;
+ continue;
+ }
+ }
+ if (!found_offset) {
+ DEBUG(2, ("Subkey not found\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+ li.key_count--;
+
+ /* If the there are no entries left, free the subkey list */
+ if (li.key_count == 0) {
+ hbin_free(regf, list_offset);
+ *ret = -1;
+ }
+
+ /* Store li block */
+ *ret = hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_li_block,
+ list_offset, &li);
+ } else if (strncmp((char *)data.data, "lf", 2) == 0) {
+ struct lf_block lf;
+ struct tdr_pull *pull = tdr_pull_init(regf);
+ uint16_t i;
+ bool found_offset = false;
+
+ DEBUG(10, ("Subkeys in LF list\n"));
+
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lf_block(pull, regf, &lf))) {
+ DEBUG(0, ("Error parsing LF list\n"));
+ talloc_free(pull);
+ return WERR_FILE_NOT_FOUND;
+ }
+ talloc_free(pull);
+
+ SMB_ASSERT(!strncmp(lf.header, "lf", 2));
+
+ for (i = 0; i < lf.key_count; i++) {
+ if (found_offset) {
+ lf.hr[i-1] = lf.hr[i];
+ continue;
+ }
+ if (lf.hr[i].nk_offset == key_offset) {
+ found_offset = 1;
+ continue;
+ }
+ }
+ if (!found_offset) {
+ DEBUG(2, ("Subkey not found\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+ lf.key_count--;
+
+ /* If the there are no entries left, free the subkey list */
+ if (lf.key_count == 0) {
+ hbin_free(regf, list_offset);
+ *ret = -1;
+ return WERR_OK;
+ }
+
+ /* Store lf block */
+ *ret = hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_lf_block,
+ list_offset, &lf);
+ } else if (strncmp((char *)data.data, "lh", 2) == 0) {
+ struct lh_block lh;
+ struct tdr_pull *pull = tdr_pull_init(regf);
+ uint16_t i;
+ bool found_offset = false;
+
+ DEBUG(10, ("Subkeys in LH list\n"));
+
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lh_block(pull, regf, &lh))) {
+ DEBUG(0, ("Error parsing LF list\n"));
+ talloc_free(pull);
+ return WERR_FILE_NOT_FOUND;
+ }
+ talloc_free(pull);
+
+ SMB_ASSERT(!strncmp(lh.header, "lh", 2));
+
+ for (i = 0; i < lh.key_count; i++) {
+ if (found_offset) {
+ lh.hr[i-1] = lh.hr[i];
+ continue;
+ }
+ if (lh.hr[i].nk_offset == key_offset) {
+ found_offset = 1;
+ continue;
+ }
+ }
+ if (!found_offset) {
+ DEBUG(0, ("Subkey not found\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+ lh.key_count--;
+
+ /* If the there are no entries left, free the subkey list */
+ if (lh.key_count == 0) {
+ hbin_free(regf, list_offset);
+ *ret = -1;
+ return WERR_OK;
+ }
+
+ /* Store lh block */
+ *ret = hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_lh_block,
+ list_offset, &lh);
+ } else if (strncmp((char *)data.data, "ri", 2) == 0) {
+ /* FIXME */
+ DEBUG(0, ("Sorry, deletion from ri block is not supported yet.\n"));
+ return WERR_NOT_SUPPORTED;
+ } else {
+ DEBUG (0, ("Unknown header found in subkey list.\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+ return WERR_OK;
+}
+
+static WERROR regf_del_value(TALLOC_CTX *mem_ctx, struct hive_key *key,
+ const char *name)
+{
+ struct regf_key_data *private_data = (struct regf_key_data *)key;
+ struct regf_data *regf = private_data->hive;
+ struct nk_block *nk = private_data->nk;
+ struct vk_block vk;
+ uint32_t vk_offset;
+ bool found_offset = false;
+ DATA_BLOB values;
+ unsigned int i;
+
+ if (nk->values_offset == -1) {
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ values = hbin_get(regf, nk->values_offset);
+
+ for (i = 0; i < nk->num_values; i++) {
+ if (found_offset) {
+ ((uint32_t *)values.data)[i-1] = ((uint32_t *) values.data)[i];
+ } else {
+ vk_offset = IVAL(values.data, i * 4);
+ if (!hbin_get_tdr(regf, vk_offset, private_data,
+ (tdr_pull_fn_t)tdr_pull_vk_block,
+ &vk)) {
+ DEBUG(0, ("Unable to get VK block at %d\n",
+ vk_offset));
+ return WERR_FILE_NOT_FOUND;
+ }
+ if (strcmp(vk.data_name, name) == 0) {
+ hbin_free(regf, vk_offset);
+ found_offset = true;
+ }
+ }
+ }
+ if (!found_offset) {
+ return WERR_FILE_NOT_FOUND;
+ } else {
+ nk->num_values--;
+ values.length = (nk->num_values)*4;
+ }
+
+ /* Store values list and nk */
+ if (nk->num_values == 0) {
+ hbin_free(regf, nk->values_offset);
+ nk->values_offset = -1;
+ } else {
+ nk->values_offset = hbin_store_resize(regf,
+ nk->values_offset,
+ values);
+ }
+ hbin_store_tdr_resize(regf, (tdr_push_fn_t) tdr_push_nk_block,
+ private_data->offset, nk);
+
+ return regf_save_hbin(private_data->hive, 0);
+}
+
+
+static WERROR regf_del_key(TALLOC_CTX *mem_ctx, const struct hive_key *parent,
+ const char *name)
+{
+ const struct regf_key_data *private_data =
+ (const struct regf_key_data *)parent;
+ struct regf_key_data *key;
+ struct nk_block *parent_nk;
+ WERROR error;
+
+ SMB_ASSERT(private_data);
+
+ parent_nk = private_data->nk;
+
+ if (parent_nk->subkeys_offset == -1) {
+ DEBUG(4, ("Subkey list is empty, this key cannot contain subkeys.\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ /* Find the key */
+ if (!W_ERROR_IS_OK(regf_get_subkey_by_name(parent_nk, parent, name,
+ (struct hive_key **)&key))) {
+ DEBUG(2, ("Key '%s' not found\n", name));
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ if (key->nk->subkeys_offset != -1) {
+ struct hive_key *sk = (struct hive_key *)key;
+ unsigned int i = key->nk->num_subkeys;
+ while (i--) {
+ char *sk_name;
+ const char *p = NULL;
+
+ /* Get subkey information. */
+ error = regf_get_subkey_by_index(parent_nk, sk, 0,
+ &p,
+ NULL, NULL);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Can't retrieve subkey by index.\n"));
+ return error;
+ }
+ sk_name = discard_const_p(char, p);
+
+ /* Delete subkey. */
+ error = regf_del_key(NULL, sk, sk_name);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Can't delete key '%s'.\n", sk_name));
+ return error;
+ }
+
+ talloc_free(sk_name);
+ }
+ }
+
+ if (key->nk->values_offset != -1) {
+ struct hive_key *sk = (struct hive_key *)key;
+ DATA_BLOB data;
+ unsigned int i = key->nk->num_values;
+ while (i--) {
+ char *val_name;
+ const char *p = NULL;
+
+ /* Get value information. */
+ error = regf_get_value(parent_nk, sk, 0,
+ &p,
+ NULL, &data);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Can't retrieve value by index.\n"));
+ return error;
+ }
+ val_name = discard_const_p(char, p);
+
+ /* Delete value. */
+ error = regf_del_value(NULL, sk, val_name);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Can't delete value '%s'.\n", val_name));
+ return error;
+ }
+
+ talloc_free(val_name);
+ }
+ }
+
+ /* Delete it from the subkey list. */
+ error = regf_sl_del_entry(private_data->hive, parent_nk->subkeys_offset,
+ key->offset, &parent_nk->subkeys_offset);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Can't store new subkey list for parent key. Won't delete.\n"));
+ return error;
+ }
+
+ /* Re-store parent key */
+ parent_nk->num_subkeys--;
+ hbin_store_tdr_resize(private_data->hive,
+ (tdr_push_fn_t) tdr_push_nk_block,
+ private_data->offset, parent_nk);
+
+ if (key->nk->clsname_offset != -1) {
+ hbin_free(private_data->hive, key->nk->clsname_offset);
+ }
+ hbin_free(private_data->hive, key->offset);
+
+ return regf_save_hbin(private_data->hive, 0);
+}
+
+static WERROR regf_add_key(TALLOC_CTX *ctx, const struct hive_key *parent,
+ const char *name, const char *classname,
+ struct security_descriptor *sec_desc,
+ struct hive_key **ret)
+{
+ const struct regf_key_data *private_data =
+ (const struct regf_key_data *)parent;
+ struct nk_block *parent_nk = private_data->nk, nk;
+ struct nk_block *root;
+ struct regf_data *regf = private_data->hive;
+ uint32_t offset;
+ WERROR error;
+
+ nk.header = "nk";
+ nk.type = REG_SUB_KEY;
+ unix_to_nt_time(&nk.last_change, time(NULL));
+ nk.uk1 = 0;
+ nk.parent_offset = private_data->offset;
+ nk.num_subkeys = 0;
+ nk.uk2 = 0;
+ nk.subkeys_offset = -1;
+ nk.unknown_offset = -1;
+ nk.num_values = 0;
+ nk.values_offset = -1;
+ memset(nk.unk3, 0, sizeof(nk.unk3));
+ nk.clsname_offset = -1; /* FIXME: fill in */
+ nk.clsname_length = 0;
+ nk.key_name = name;
+
+ /* Get the security descriptor of the root key */
+ root = talloc_zero(ctx, struct nk_block);
+ W_ERROR_HAVE_NO_MEMORY(root);
+
+ if (!hbin_get_tdr(regf, regf->header->data_offset, root,
+ (tdr_pull_fn_t)tdr_pull_nk_block, root)) {
+ DEBUG(0, ("Unable to find HBIN data for offset 0x%x\n",
+ regf->header->data_offset));
+ return WERR_GEN_FAILURE;
+ }
+ nk.sk_offset = root->sk_offset;
+ talloc_free(root);
+
+ /* Store the new nk key */
+ offset = hbin_store_tdr(regf, (tdr_push_fn_t) tdr_push_nk_block, &nk);
+
+ error = regf_sl_add_entry(regf, parent_nk->subkeys_offset, name, offset,
+ &parent_nk->subkeys_offset);
+ if (!W_ERROR_IS_OK(error)) {
+ hbin_free(regf, offset);
+ return error;
+ }
+
+ parent_nk->num_subkeys++;
+
+ /* Since the subkey offset of the parent can change, store it again */
+ hbin_store_tdr_resize(regf, (tdr_push_fn_t) tdr_push_nk_block,
+ nk.parent_offset, parent_nk);
+
+ *ret = (struct hive_key *)regf_get_key(ctx, regf, offset);
+
+ DEBUG(9, ("Storing key %s\n", name));
+ return regf_save_hbin(private_data->hive, 0);
+}
+
+static WERROR regf_set_value(struct hive_key *key, const char *name,
+ uint32_t type, const DATA_BLOB data)
+{
+ struct regf_key_data *private_data = (struct regf_key_data *)key;
+ struct regf_data *regf = private_data->hive;
+ struct nk_block *nk = private_data->nk;
+ struct vk_block vk;
+ uint32_t i;
+ uint32_t tmp_vk_offset, vk_offset, old_vk_offset = (uint32_t) -1;
+ DATA_BLOB values = {0};
+
+ ZERO_STRUCT(vk);
+
+ /* find the value offset, if it exists */
+ if (nk->values_offset != -1) {
+ values = hbin_get(regf, nk->values_offset);
+
+ for (i = 0; i < nk->num_values; i++) {
+ tmp_vk_offset = IVAL(values.data, i * 4);
+ if (!hbin_get_tdr(regf, tmp_vk_offset, private_data,
+ (tdr_pull_fn_t)tdr_pull_vk_block,
+ &vk)) {
+ DEBUG(0, ("Unable to get VK block at 0x%x\n",
+ tmp_vk_offset));
+ return WERR_GEN_FAILURE;
+ }
+ if (strcmp(vk.data_name, name) == 0) {
+ old_vk_offset = tmp_vk_offset;
+ break;
+ }
+ }
+ }
+
+ /* If it's new, create the vk struct, if it's old, free the old data. */
+ if (old_vk_offset == -1) {
+ vk.header = "vk";
+ if (name != NULL && name[0] != '\0') {
+ vk.flag = 1;
+ vk.data_name = name;
+ vk.name_length = strlen(name);
+ } else {
+ vk.flag = 0;
+ vk.data_name = NULL;
+ vk.name_length = 0;
+ }
+ } else {
+ /* Free data, if any */
+ if (!(vk.data_length & 0x80000000)) {
+ hbin_free(regf, vk.data_offset);
+ }
+ }
+
+ /* Set the type and data */
+ vk.data_length = data.length;
+ vk.data_type = type;
+ if ((type == REG_DWORD) || (type == REG_DWORD_BIG_ENDIAN)) {
+ if (vk.data_length != sizeof(uint32_t)) {
+ DEBUG(0, ("DWORD or DWORD_BIG_ENDIAN value with size other than 4 byte!\n"));
+ return WERR_NOT_SUPPORTED;
+ }
+ vk.data_length |= 0x80000000;
+ vk.data_offset = IVAL(data.data, 0);
+ } else {
+ /* Store data somewhere */
+ vk.data_offset = hbin_store(regf, data);
+ }
+ if (old_vk_offset == -1) {
+ /* Store new vk */
+ vk_offset = hbin_store_tdr(regf,
+ (tdr_push_fn_t) tdr_push_vk_block,
+ &vk);
+ } else {
+ /* Store vk at offset */
+ vk_offset = hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_vk_block,
+ old_vk_offset ,&vk);
+ }
+
+ /* Re-allocate the value list */
+ if (nk->values_offset == -1) {
+ nk->values_offset = hbin_store_tdr(regf,
+ (tdr_push_fn_t) tdr_push_uint32,
+ &vk_offset);
+ nk->num_values = 1;
+ } else {
+
+ /* Change if we're changing, otherwise we're adding the value */
+ if (old_vk_offset != -1) {
+ /* Find and overwrite the offset. */
+ for (i = 0; i < nk->num_values; i++) {
+ if (IVAL(values.data, i * 4) == old_vk_offset) {
+ SIVAL(values.data, i * 4, vk_offset);
+ break;
+ }
+ }
+ } else {
+ /* Create a new value list */
+ DATA_BLOB value_list;
+
+ value_list.length = (nk->num_values+1)*4;
+ value_list.data = (uint8_t *)talloc_array(private_data,
+ uint32_t,
+ nk->num_values+1);
+ W_ERROR_HAVE_NO_MEMORY(value_list.data);
+ memcpy(value_list.data, values.data, nk->num_values * 4);
+
+ SIVAL(value_list.data, nk->num_values * 4, vk_offset);
+ nk->num_values++;
+ nk->values_offset = hbin_store_resize(regf,
+ nk->values_offset,
+ value_list);
+ }
+
+ }
+ hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_nk_block,
+ private_data->offset, nk);
+ return regf_save_hbin(private_data->hive, 0);
+}
+
+static WERROR regf_save_hbin(struct regf_data *regf, bool flush)
+{
+ struct tdr_push *push = tdr_push_init(regf);
+ unsigned int i;
+
+ W_ERROR_HAVE_NO_MEMORY(push);
+
+ /* Only write once every 5 seconds, or when flush is set */
+ if (!flush && regf->last_write + 5 >= time(NULL)) {
+ return WERR_OK;
+ }
+
+ regf->last_write = time(NULL);
+
+ if (lseek(regf->fd, 0, SEEK_SET) == -1) {
+ DEBUG(0, ("Error lseeking in regf file\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ /* Recompute checksum */
+ if (NT_STATUS_IS_ERR(tdr_push_regf_hdr(push, regf->header))) {
+ DEBUG(0, ("Failed to push regf header\n"));
+ return WERR_GEN_FAILURE;
+ }
+ regf->header->chksum = regf_hdr_checksum(push->data.data);
+ talloc_free(push);
+
+ if (NT_STATUS_IS_ERR(tdr_push_to_fd(regf->fd,
+ (tdr_push_fn_t)tdr_push_regf_hdr,
+ regf->header))) {
+ DEBUG(0, ("Error writing registry file header\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ if (lseek(regf->fd, 0x1000, SEEK_SET) == -1) {
+ DEBUG(0, ("Error lseeking to 0x1000 in regf file\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ for (i = 0; regf->hbins[i]; i++) {
+ if (NT_STATUS_IS_ERR(tdr_push_to_fd(regf->fd,
+ (tdr_push_fn_t)tdr_push_hbin_block,
+ regf->hbins[i]))) {
+ DEBUG(0, ("Error writing HBIN block\n"));
+ return WERR_GEN_FAILURE;
+ }
+ }
+
+ return WERR_OK;
+}
+
+WERROR reg_create_regf_file(TALLOC_CTX *parent_ctx,
+ const char *location,
+ int minor_version, struct hive_key **key)
+{
+ struct regf_data *regf;
+ struct regf_hdr *regf_hdr;
+ struct nk_block nk;
+ struct sk_block sk;
+ WERROR error;
+ DATA_BLOB data;
+ struct security_descriptor *sd;
+ uint32_t sk_offset;
+
+ regf = (struct regf_data *)talloc_zero(NULL, struct regf_data);
+
+ W_ERROR_HAVE_NO_MEMORY(regf);
+
+ DEBUG(5, ("Attempting to create registry file\n"));
+
+ /* Get the header */
+ regf->fd = creat(location, 0644);
+
+ if (regf->fd == -1) {
+ DEBUG(0,("Could not create file: %s, %s\n", location,
+ strerror(errno)));
+ talloc_free(regf);
+ return WERR_GEN_FAILURE;
+ }
+
+ regf_hdr = talloc_zero(regf, struct regf_hdr);
+ W_ERROR_HAVE_NO_MEMORY(regf_hdr);
+ regf_hdr->REGF_ID = "regf";
+ unix_to_nt_time(&regf_hdr->modtime, time(NULL));
+ regf_hdr->version.major = 1;
+ regf_hdr->version.minor = minor_version;
+ regf_hdr->last_block = 0x1000; /* Block size */
+ regf_hdr->description = talloc_strdup(regf_hdr,
+ "Registry created by Samba 4");
+ W_ERROR_HAVE_NO_MEMORY(regf_hdr->description);
+ regf_hdr->chksum = 0;
+
+ regf->header = regf_hdr;
+
+ /* Create all hbin blocks */
+ regf->hbins = talloc_array(regf, struct hbin_block *, 1);
+ W_ERROR_HAVE_NO_MEMORY(regf->hbins);
+ regf->hbins[0] = NULL;
+
+ nk.header = "nk";
+ nk.type = REG_ROOT_KEY;
+ unix_to_nt_time(&nk.last_change, time(NULL));
+ nk.uk1 = 0;
+ nk.parent_offset = -1;
+ nk.num_subkeys = 0;
+ nk.uk2 = 0;
+ nk.subkeys_offset = -1;
+ nk.unknown_offset = -1;
+ nk.num_values = 0;
+ nk.values_offset = -1;
+ memset(nk.unk3, 0, 5 * sizeof(uint32_t));
+ nk.clsname_offset = -1;
+ nk.clsname_length = 0;
+ nk.sk_offset = 0x80;
+ nk.key_name = "SambaRootKey";
+
+ /*
+ * It should be noted that changing the key_name to something shorter
+ * creates a shorter nk block, which makes the position of the sk block
+ * change. All Windows registries I've seen have the sk at 0x80.
+ * I therefore recommend that our regf files share that offset -- Wilco
+ */
+
+ /* Create a security descriptor. */
+ sd = security_descriptor_dacl_create(regf,
+ 0,
+ NULL, NULL,
+ SID_NT_AUTHENTICATED_USERS,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_GENERIC_ALL,
+ SEC_ACE_FLAG_OBJECT_INHERIT,
+ NULL);
+
+ /* Push the security descriptor to a blob */
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_struct_blob(&data, regf,
+ sd, (ndr_push_flags_fn_t)ndr_push_security_descriptor))) {
+ DEBUG(0, ("Unable to push security descriptor\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ ZERO_STRUCT(sk);
+ sk.header = "sk";
+ sk.prev_offset = 0x80;
+ sk.next_offset = 0x80;
+ sk.ref_cnt = 1;
+ sk.rec_size = data.length;
+ sk.sec_desc = data.data;
+
+ /* Store the new nk key */
+ regf->header->data_offset = hbin_store_tdr(regf,
+ (tdr_push_fn_t)tdr_push_nk_block,
+ &nk);
+ /* Store the sk block */
+ sk_offset = hbin_store_tdr(regf,
+ (tdr_push_fn_t) tdr_push_sk_block,
+ &sk);
+ if (sk_offset != 0x80) {
+ DEBUG(0, ("Error storing sk block, should be at 0x80, stored at 0x%x\n", nk.sk_offset));
+ return WERR_GEN_FAILURE;
+ }
+
+
+ *key = (struct hive_key *)regf_get_key(parent_ctx, regf,
+ regf->header->data_offset);
+
+ error = regf_save_hbin(regf, 1);
+ if (!W_ERROR_IS_OK(error)) {
+ return error;
+ }
+
+ /* We can drop our own reference now that *key will have created one */
+ talloc_unlink(NULL, regf);
+
+ return WERR_OK;
+}
+
+static WERROR regf_flush_key(struct hive_key *key)
+{
+ struct regf_key_data *private_data = (struct regf_key_data *)key;
+ struct regf_data *regf = private_data->hive;
+ WERROR error;
+
+ error = regf_save_hbin(regf, 1);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Failed to flush regf to disk\n"));
+ return error;
+ }
+
+ return WERR_OK;
+}
+
+static int regf_destruct(struct regf_data *regf)
+{
+ WERROR error;
+
+ /* Write to disk */
+ error = regf_save_hbin(regf, 1);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Failed to flush registry to disk\n"));
+ return -1;
+ }
+
+ /* Close file descriptor */
+ close(regf->fd);
+
+ return 0;
+}
+
+WERROR reg_open_regf_file(TALLOC_CTX *parent_ctx, const char *location,
+ struct hive_key **key)
+{
+ struct regf_data *regf;
+ struct regf_hdr *regf_hdr;
+ struct tdr_pull *pull;
+ unsigned int i;
+
+ regf = (struct regf_data *)talloc_zero(parent_ctx, struct regf_data);
+ W_ERROR_HAVE_NO_MEMORY(regf);
+
+ talloc_set_destructor(regf, regf_destruct);
+
+ DEBUG(5, ("Attempting to load registry file\n"));
+
+ /* Get the header */
+ regf->fd = open(location, O_RDWR);
+
+ if (regf->fd == -1) {
+ DEBUG(0,("Could not load file: %s, %s\n", location,
+ strerror(errno)));
+ talloc_free(regf);
+ return WERR_GEN_FAILURE;
+ }
+
+ pull = tdr_pull_init(regf);
+
+ pull->data.data = (uint8_t*)fd_load(regf->fd, &pull->data.length, 0, regf);
+
+ if (pull->data.data == NULL) {
+ DEBUG(0, ("Error reading data from file: %s\n", location));
+ talloc_free(regf);
+ return WERR_GEN_FAILURE;
+ }
+
+ regf_hdr = talloc(regf, struct regf_hdr);
+ W_ERROR_HAVE_NO_MEMORY(regf_hdr);
+
+ if (NT_STATUS_IS_ERR(tdr_pull_regf_hdr(pull, regf_hdr, regf_hdr))) {
+ DEBUG(0, ("Failed to pull regf header from file: %s\n", location));
+ talloc_free(regf);
+ return WERR_GEN_FAILURE;
+ }
+
+ regf->header = regf_hdr;
+
+ if (strcmp(regf_hdr->REGF_ID, "regf") != 0) {
+ DEBUG(0, ("Unrecognized NT registry header id: %s, %s\n",
+ regf_hdr->REGF_ID, location));
+ talloc_free(regf);
+ return WERR_GEN_FAILURE;
+ }
+
+ /* Validate the header ... */
+ if (regf_hdr_checksum(pull->data.data) != regf_hdr->chksum) {
+ DEBUG(0, ("Registry file checksum error: %s: %d,%d\n",
+ location, regf_hdr->chksum,
+ regf_hdr_checksum(pull->data.data)));
+ talloc_free(regf);
+ return WERR_GEN_FAILURE;
+ }
+
+ pull->offset = 0x1000;
+
+ i = 0;
+ /* Read in all hbin blocks */
+ regf->hbins = talloc_array(regf, struct hbin_block *, 1);
+ W_ERROR_HAVE_NO_MEMORY(regf->hbins);
+
+ regf->hbins[0] = NULL;
+
+ while (pull->offset < pull->data.length &&
+ pull->offset <= regf->header->last_block) {
+ struct hbin_block *hbin = talloc(regf->hbins,
+ struct hbin_block);
+
+ W_ERROR_HAVE_NO_MEMORY(hbin);
+
+ if (NT_STATUS_IS_ERR(tdr_pull_hbin_block(pull, hbin, hbin))) {
+ DEBUG(0, ("[%d] Error parsing HBIN block\n", i));
+ talloc_free(regf);
+ return WERR_FOOBAR;
+ }
+
+ if (strcmp(hbin->HBIN_ID, "hbin") != 0) {
+ DEBUG(0, ("[%d] Expected 'hbin', got '%s'\n",
+ i, hbin->HBIN_ID));
+ talloc_free(regf);
+ return WERR_FOOBAR;
+ }
+
+ regf->hbins[i] = hbin;
+ i++;
+ regf->hbins = talloc_realloc(regf, regf->hbins,
+ struct hbin_block *, i+2);
+ regf->hbins[i] = NULL;
+ }
+
+ talloc_free(pull);
+
+ DEBUG(1, ("%d HBIN blocks read\n", i));
+
+ *key = (struct hive_key *)regf_get_key(parent_ctx, regf,
+ regf->header->data_offset);
+
+ /* We can drop our own reference now that *key will have created one */
+ talloc_unlink(parent_ctx, regf);
+
+ return WERR_OK;
+}
+
+static struct hive_operations reg_backend_regf = {
+ .name = "regf",
+ .get_key_info = regf_get_info,
+ .enum_key = regf_get_subkey_by_index,
+ .get_key_by_name = regf_get_subkey_by_name,
+ .get_value_by_name = regf_get_value_by_name,
+ .enum_value = regf_get_value,
+ .get_sec_desc = regf_get_sec_desc,
+ .set_sec_desc = regf_set_sec_desc,
+ .add_key = regf_add_key,
+ .set_value = regf_set_value,
+ .del_key = regf_del_key,
+ .delete_value = regf_del_value,
+ .flush_key = regf_flush_key
+};
diff --git a/source4/lib/registry/regf.idl b/source4/lib/registry/regf.idl
new file mode 100644
index 0000000..064aaf0
--- /dev/null
+++ b/source4/lib/registry/regf.idl
@@ -0,0 +1,167 @@
+/*
+ Definitions for the REGF registry file format as used by
+ Windows NT4 and above.
+
+ Copyright (C) 2005 Jelmer Vernooij, jelmer@samba.org
+ Copyright (C) 2006 Wilco Baan Hofman, wilco@baanhofman.nl
+
+ Based on two files from Samba 3:
+ regedit.c by Richard Sharpe
+ regfio.c by Jerry Carter
+
+*/
+
+interface regf
+{
+ const int REGF_OFFSET_NONE = 0xffffffff;
+
+ /*
+ * Registry version number
+ * 1.2.0.1 for WinNT 3.51
+ * 1.3.0.1 for WinNT 4
+ * 1.5.0.1 for WinXP
+ */
+
+ [noprint] struct regf_version {
+ [value(1)] uint32 major;
+ uint32 minor;
+ [value(0)] uint32 release;
+ [value(1)] uint32 build;
+ };
+
+ /*
+ "regf" is obviously the abbreviation for "Registry file". "regf" is the
+ signature of the header-block which is always 4kb in size, although only
+ the first 64 bytes seem to be used and a checksum is calculated over
+ the first 0x200 bytes only!
+ */
+
+ [public,noprint] struct regf_hdr {
+ [charset(DOS)] uint8 REGF_ID[4]; /* 'regf' */
+ uint32 update_counter1;
+ uint32 update_counter2;
+ NTTIME modtime;
+ regf_version version;
+ uint32 data_offset;
+ uint32 last_block;
+ [value(1)] uint32 uk7; /* 1 */
+ [charset(UTF16)] uint16 description[0x20];
+ uint32 padding[99]; /* Padding */
+ /* Checksum of first 0x200 bytes XOR-ed */
+ uint32 chksum;
+ };
+
+ /*
+ hbin probably means hive-bin (i.e. hive-container)
+ This block is always a multiple
+ of 4kb in size.
+ */
+ [public,noprint] struct hbin_block {
+ [charset(DOS)] uint8 HBIN_ID[4]; /* hbin */
+ uint32 offset_from_first; /* Offset from 1st hbin-Block */
+ uint32 offset_to_next; /* Offset to the next hbin-Block */
+ uint32 unknown[2];
+ NTTIME last_change;
+ uint32 block_size; /* Block size (including the header!) */
+ uint8 data[offset_to_next-0x20];
+ /* data is filled with:
+ uint32 length;
+ Negative if in use, positive otherwise
+ Always a multiple of 8
+ uint8_t data[length];
+ Free space marker if 0xffffffff
+ */
+ };
+
+ [noprint] enum reg_key_type {
+ REG_ROOT_KEY = 0x2C,
+ REG_SUB_KEY = 0x20,
+ REG_SYM_LINK = 0x10
+ };
+
+ /*
+ The nk-record can be treated as a combination of tree-record and
+ key-record of the win 95 registry.
+ */
+ [public,noprint] struct nk_block {
+ [charset(DOS)] uint8 header[2];
+ reg_key_type type;
+ NTTIME last_change;
+ uint32 uk1;
+ uint32 parent_offset;
+ uint32 num_subkeys;
+ uint32 uk2;
+ uint32 subkeys_offset;
+ uint32 unknown_offset;
+ uint32 num_values;
+ uint32 values_offset; /* Points to a list of offsets of vk-records */
+ uint32 sk_offset;
+ uint32 clsname_offset;
+ uint32 unk3[5];
+ [value(strlen(key_name))] uint16 name_length;
+ uint16 clsname_length;
+ [charset(DOS)] uint8 key_name[name_length];
+ };
+
+ /* sk (? Security Key ?) is the ACL of the registry. */
+ [noprint,public] struct sk_block {
+ [charset(DOS)] uint8 header[2];
+ uint16 tag;
+ uint32 prev_offset;
+ uint32 next_offset;
+ uint32 ref_cnt;
+ uint32 rec_size;
+ uint8 sec_desc[rec_size];
+ };
+
+ [noprint] struct lh_hash {
+ uint32 nk_offset;
+ uint32 base37; /* base37 of key name */
+ };
+
+ /* Subkey listing with hash of first 4 characters */
+ [public,noprint] struct lh_block {
+ [charset(DOS)] uint8 header[2];
+ uint16 key_count;
+ lh_hash hr[key_count];
+ };
+
+ [public,noprint] struct li_block {
+ [charset(DOS)] uint8 header[2];
+ uint16 key_count;
+ uint32 nk_offset[key_count];
+ };
+
+ [public,noprint] struct ri_block {
+ [charset(DOS)] uint8 header[2];
+ uint16 key_count;
+ uint32 offset[key_count]; /* li/lh offset */
+ };
+
+ /* The vk-record consists information to a single value (value key). */
+ [public,noprint] struct vk_block {
+ [charset(DOS)] uint8 header[2];
+ [value(strlen(data_name))] uint16 name_length;
+ uint32 data_length; /* If top-bit set, offset contains the data */
+ uint32 data_offset;
+ uint32 data_type;
+ uint16 flag; /* =1, has name, else no name (=Default). */
+ uint16 unk1;
+ [charset(DOS)] uint8 data_name[name_length];
+ };
+
+ [noprint] struct hash_record {
+ uint32 nk_offset;
+ [charset(DOS)] uint8 hash[4];
+ };
+
+ /*
+ The lf-record is the counterpart to the RGKN-record (the
+ hash-function)
+ */
+ [public,noprint] struct lf_block {
+ [charset(DOS)] uint8 header[2];
+ uint16 key_count;
+ hash_record hr[key_count]; /* Array of hash records, depending on key_count */
+ };
+}
diff --git a/source4/lib/registry/registry.h b/source4/lib/registry/registry.h
new file mode 100644
index 0000000..c22038c
--- /dev/null
+++ b/source4/lib/registry/registry.h
@@ -0,0 +1,532 @@
+/*
+ Unix SMB/CIFS implementation.
+ Registry interface
+ Copyright (C) Gerald Carter 2002.
+ Copyright (C) Jelmer Vernooij 2003-2007.
+
+ 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 _REGISTRY_H /* _REGISTRY_H */
+#define _REGISTRY_H
+
+struct registry_context;
+struct loadparm_context;
+
+#include <talloc.h>
+#include "libcli/util/werror.h"
+#include "librpc/gen_ndr/security.h"
+#include "libcli/util/ntstatus.h"
+#include "../lib/util/time.h"
+#include "../lib/util/data_blob.h"
+
+/**
+ * The hive API. This API is generally used for
+ * reading a specific file that contains just one hive.
+ *
+ * Good examples are .DAT (NTUSER.DAT) files.
+ *
+ * This API does not have any notification support (that
+ * should be provided by the registry implementation), nor
+ * does it understand what predefined keys are.
+ */
+
+struct hive_key {
+ const struct hive_operations *ops;
+};
+
+struct hive_operations {
+ const char *name;
+
+ /**
+ * Open a specific subkey
+ */
+ WERROR (*enum_key) (TALLOC_CTX *mem_ctx,
+ const struct hive_key *key, uint32_t idx,
+ const char **name,
+ const char **classname,
+ NTTIME *last_mod_time);
+
+ /**
+ * Open a subkey by name
+ */
+ WERROR (*get_key_by_name) (TALLOC_CTX *mem_ctx,
+ const struct hive_key *key, const char *name,
+ struct hive_key **subkey);
+
+ /**
+ * Add a new key.
+ */
+ WERROR (*add_key) (TALLOC_CTX *ctx,
+ const struct hive_key *parent_key, const char *path,
+ const char *classname,
+ struct security_descriptor *desc,
+ struct hive_key **key);
+ /**
+ * Remove an existing key.
+ */
+ WERROR (*del_key) (TALLOC_CTX *mem_ctx,
+ const struct hive_key *key, const char *name);
+
+ /**
+ * Force write of a key to disk.
+ */
+ WERROR (*flush_key) (struct hive_key *key);
+
+ /**
+ * Retrieve a registry value with a specific index.
+ */
+ WERROR (*enum_value) (TALLOC_CTX *mem_ctx,
+ struct hive_key *key, uint32_t idx,
+ const char **name, uint32_t *type,
+ DATA_BLOB *data);
+
+ /**
+ * Retrieve a registry value with the specified name
+ */
+ WERROR (*get_value_by_name) (TALLOC_CTX *mem_ctx,
+ struct hive_key *key, const char *name,
+ uint32_t *type, DATA_BLOB *data);
+
+ /**
+ * Set a value on the specified registry key.
+ */
+ WERROR (*set_value) (struct hive_key *key, const char *name,
+ uint32_t type, const DATA_BLOB data);
+
+ /**
+ * Remove a value.
+ */
+ WERROR (*delete_value) (TALLOC_CTX *mem_ctx,
+ struct hive_key *key, const char *name);
+
+ /* Security Descriptors */
+
+ /**
+ * Change the security descriptor on a registry key.
+ *
+ * This should return WERR_NOT_SUPPORTED if the underlying
+ * format does not have a mechanism for storing
+ * security descriptors.
+ */
+ WERROR (*set_sec_desc) (struct hive_key *key,
+ const struct security_descriptor *desc);
+
+ /**
+ * Retrieve the security descriptor on a registry key.
+ *
+ * This should return WERR_NOT_SUPPORTED if the underlying
+ * format does not have a mechanism for storing
+ * security descriptors.
+ */
+ WERROR (*get_sec_desc) (TALLOC_CTX *ctx,
+ const struct hive_key *key,
+ struct security_descriptor **desc);
+
+ /**
+ * Retrieve general information about a key.
+ */
+ WERROR (*get_key_info) (TALLOC_CTX *mem_ctx,
+ const struct hive_key *key,
+ const char **classname,
+ uint32_t *num_subkeys,
+ uint32_t *num_values,
+ NTTIME *last_change_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize);
+};
+
+struct cli_credentials;
+struct auth_session_info;
+struct tevent_context;
+
+WERROR reg_open_hive(TALLOC_CTX *parent_ctx, const char *location,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct hive_key **root);
+WERROR hive_key_get_info(TALLOC_CTX *mem_ctx, const struct hive_key *key,
+ const char **classname, uint32_t *num_subkeys,
+ uint32_t *num_values, NTTIME *last_change_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen, uint32_t *max_valbufsize);
+WERROR hive_key_add_name(TALLOC_CTX *ctx, const struct hive_key *parent_key,
+ const char *name, const char *classname,
+ struct security_descriptor *desc,
+ struct hive_key **key);
+WERROR hive_key_del(TALLOC_CTX *mem_ctx,
+ const struct hive_key *key, const char *name);
+WERROR hive_get_key_by_name(TALLOC_CTX *mem_ctx,
+ const struct hive_key *key, const char *name,
+ struct hive_key **subkey);
+WERROR hive_enum_key(TALLOC_CTX *mem_ctx,
+ const struct hive_key *key, uint32_t idx,
+ const char **name,
+ const char **classname,
+ NTTIME *last_mod_time);
+
+WERROR hive_key_set_value(struct hive_key *key, const char *name,
+ uint32_t type, const DATA_BLOB data);
+
+WERROR hive_get_value(TALLOC_CTX *mem_ctx,
+ struct hive_key *key, const char *name,
+ uint32_t *type, DATA_BLOB *data);
+WERROR hive_get_value_by_index(TALLOC_CTX *mem_ctx,
+ struct hive_key *key, uint32_t idx,
+ const char **name,
+ uint32_t *type, DATA_BLOB *data);
+WERROR hive_get_sec_desc(TALLOC_CTX *mem_ctx,
+ struct hive_key *key,
+ struct security_descriptor **security);
+
+WERROR hive_set_sec_desc(struct hive_key *key,
+ const struct security_descriptor *security);
+
+WERROR hive_key_del_value(TALLOC_CTX *mem_ctx,
+ struct hive_key *key, const char *name);
+
+WERROR hive_key_flush(struct hive_key *key);
+
+
+/* Individual backends */
+WERROR reg_open_directory(TALLOC_CTX *parent_ctx,
+ const char *location, struct hive_key **key);
+WERROR reg_open_regf_file(TALLOC_CTX *parent_ctx,
+ const char *location, struct hive_key **key);
+WERROR reg_open_ldb_file(TALLOC_CTX *parent_ctx, const char *location,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct hive_key **k);
+
+
+WERROR reg_create_directory(TALLOC_CTX *parent_ctx,
+ const char *location, struct hive_key **key);
+WERROR reg_create_regf_file(TALLOC_CTX *parent_ctx,
+ const char *location,
+ int major_version,
+ struct hive_key **key);
+
+
+
+/* Handles for the predefined keys */
+#define HKEY_CLASSES_ROOT 0x80000000
+#define HKEY_CURRENT_USER 0x80000001
+#define HKEY_LOCAL_MACHINE 0x80000002
+#define HKEY_USERS 0x80000003
+#define HKEY_PERFORMANCE_DATA 0x80000004
+#define HKEY_CURRENT_CONFIG 0x80000005
+#define HKEY_DYN_DATA 0x80000006
+#define HKEY_PERFORMANCE_TEXT 0x80000050
+#define HKEY_PERFORMANCE_NLSTEXT 0x80000060
+
+#define HKEY_FIRST HKEY_CLASSES_ROOT
+#define HKEY_LAST HKEY_PERFORMANCE_NLSTEXT
+
+struct reg_predefined_key {
+ uint32_t handle;
+ const char *name;
+};
+
+extern const struct reg_predefined_key reg_predefined_keys[];
+
+#define REG_DELETE -1
+
+/*
+ * The general idea here is that every backend provides a 'hive'. Combining
+ * various hives gives you a complete registry like windows has
+ */
+
+#define REGISTRY_INTERFACE_VERSION 1
+
+struct reg_key_operations;
+
+/* structure to store the registry handles */
+struct registry_key
+{
+ struct registry_context *context;
+};
+
+struct registry_value
+{
+ const char *name;
+ unsigned int data_type;
+ DATA_BLOB data;
+};
+
+/* FIXME */
+typedef void (*reg_key_notification_function) (void);
+typedef void (*reg_value_notification_function) (void);
+
+struct cli_credentials;
+
+struct registry_operations {
+ const char *name;
+
+ WERROR (*get_key_info) (TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char **classname,
+ uint32_t *numsubkeys,
+ uint32_t *numvalues,
+ NTTIME *last_change_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize);
+
+ WERROR (*flush_key) (struct registry_key *key);
+
+ WERROR (*get_predefined_key) (struct registry_context *ctx,
+ uint32_t key_id,
+ struct registry_key **key);
+
+ WERROR (*open_key) (TALLOC_CTX *mem_ctx,
+ struct registry_key *parent,
+ const char *path,
+ struct registry_key **key);
+
+ WERROR (*create_key) (TALLOC_CTX *mem_ctx,
+ struct registry_key *parent,
+ const char *name,
+ const char *key_class,
+ struct security_descriptor *security,
+ struct registry_key **key);
+
+ WERROR (*delete_key) (TALLOC_CTX *mem_ctx,
+ struct registry_key *key, const char *name);
+
+ WERROR (*delete_value) (TALLOC_CTX *mem_ctx,
+ struct registry_key *key, const char *name);
+
+ WERROR (*enum_key) (TALLOC_CTX *mem_ctx,
+ const struct registry_key *key, uint32_t idx,
+ const char **name,
+ const char **keyclass,
+ NTTIME *last_changed_time);
+
+ WERROR (*enum_value) (TALLOC_CTX *mem_ctx,
+ const struct registry_key *key, uint32_t idx,
+ const char **name,
+ uint32_t *type,
+ DATA_BLOB *data);
+
+ WERROR (*get_sec_desc) (TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ struct security_descriptor **security);
+
+ WERROR (*set_sec_desc) (struct registry_key *key,
+ const struct security_descriptor *security);
+
+ WERROR (*load_key) (struct registry_key *key,
+ const char *key_name,
+ const char *path);
+
+ WERROR (*unload_key) (struct registry_key *key, const char *name);
+
+ WERROR (*notify_value_change) (struct registry_key *key,
+ reg_value_notification_function fn);
+
+ WERROR (*get_value) (TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char *name,
+ uint32_t *type,
+ DATA_BLOB *data);
+
+ WERROR (*set_value) (struct registry_key *key,
+ const char *name,
+ uint32_t type,
+ const DATA_BLOB data);
+};
+
+/**
+ * Handle to a full registry
+ * contains zero or more hives
+ */
+struct registry_context {
+ const struct registry_operations *ops;
+};
+
+struct auth_session_info;
+struct tevent_context;
+struct loadparm_context;
+
+/**
+ * Open the locally defined registry.
+ */
+WERROR reg_open_local(TALLOC_CTX *mem_ctx,
+ struct registry_context **ctx);
+
+WERROR reg_open_samba(TALLOC_CTX *mem_ctx,
+ struct registry_context **ctx,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials);
+
+/**
+ * Open the registry on a remote machine.
+ */
+WERROR reg_open_remote(TALLOC_CTX *mem_ctx,
+ struct registry_context **ctx,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx,
+ const char *location, struct tevent_context *ev);
+
+WERROR reg_open_wine(struct registry_context **ctx, const char *path);
+
+const char *reg_get_predef_name(uint32_t hkey);
+WERROR reg_get_predefined_key_by_name(struct registry_context *ctx,
+ const char *name,
+ struct registry_key **key);
+WERROR reg_get_predefined_key(struct registry_context *ctx,
+ uint32_t hkey,
+ struct registry_key **key);
+
+WERROR reg_open_key(TALLOC_CTX *mem_ctx, struct registry_key *parent,
+ const char *name, struct registry_key **result);
+
+WERROR reg_key_get_value_by_index(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key, uint32_t idx,
+ const char **name,
+ uint32_t *type,
+ DATA_BLOB *data);
+WERROR reg_key_get_info(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char **class_name,
+ uint32_t *num_subkeys,
+ uint32_t *num_values,
+ NTTIME *last_change_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize);
+WERROR reg_key_get_subkey_by_index(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ uint32_t idx,
+ const char **name,
+ const char **classname,
+ NTTIME *last_mod_time);
+WERROR reg_key_get_subkey_by_name(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char *name,
+ struct registry_key **subkey);
+WERROR reg_key_get_value_by_name(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char *name,
+ uint32_t *type,
+ DATA_BLOB *data);
+WERROR reg_key_del(TALLOC_CTX *mem_ctx,
+ struct registry_key *parent, const char *name);
+WERROR reg_key_add_name(TALLOC_CTX *mem_ctx,
+ struct registry_key *parent, const char *name,
+ const char *classname,
+ struct security_descriptor *desc,
+ struct registry_key **newkey);
+WERROR reg_val_set(struct registry_key *key, const char *value,
+ uint32_t type, DATA_BLOB data);
+WERROR reg_get_sec_desc(TALLOC_CTX *ctx, const struct registry_key *key,
+ struct security_descriptor **secdesc);
+WERROR reg_del_value(TALLOC_CTX *mem_ctx,
+ struct registry_key *key, const char *valname);
+WERROR reg_key_flush(struct registry_key *key);
+WERROR reg_create_key(TALLOC_CTX *mem_ctx,
+ struct registry_key *parent,
+ const char *name,
+ const char *key_class,
+ struct security_descriptor *security,
+ struct registry_key **key);
+
+/* Utility functions */
+const char *str_regtype(int type);
+bool push_reg_sz(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, const char *s);
+bool push_reg_multi_sz(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, const char **a);
+bool pull_reg_sz(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, const char **s);
+bool pull_reg_multi_sz(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, const char ***a);
+int regtype_by_string(const char *str);
+char *reg_val_data_string(TALLOC_CTX *mem_ctx, uint32_t type, const DATA_BLOB data);
+char *reg_val_description(TALLOC_CTX *mem_ctx, const char *name,
+ uint32_t type, const DATA_BLOB data);
+bool reg_string_to_val(TALLOC_CTX *mem_ctx, const char *type_str,
+ const char *data_str, uint32_t *type, DATA_BLOB *data);
+WERROR reg_open_key_abs(TALLOC_CTX *mem_ctx, struct registry_context *handle,
+ const char *name, struct registry_key **result);
+WERROR reg_key_del_abs(struct registry_context *ctx, const char *path);
+WERROR reg_key_add_abs(TALLOC_CTX *mem_ctx, struct registry_context *ctx,
+ const char *path, uint32_t access_mask,
+ struct security_descriptor *sec_desc,
+ struct registry_key **result);
+WERROR reg_load_key(struct registry_context *ctx, struct registry_key *key,
+ const char *name, const char *filename);
+
+WERROR reg_mount_hive(struct registry_context *rctx,
+ struct hive_key *hive_key,
+ uint32_t key_id,
+ const char **elements);
+
+struct registry_key *reg_import_hive_key(struct registry_context *ctx,
+ struct hive_key *hive,
+ uint32_t predef_key,
+ const char **elements);
+WERROR reg_set_sec_desc(struct registry_key *key,
+ const struct security_descriptor *security);
+
+struct reg_diff_callbacks {
+ WERROR (*add_key) (void *callback_data, const char *key_name);
+ WERROR (*set_value) (void *callback_data, const char *key_name,
+ const char *value_name, uint32_t value_type,
+ DATA_BLOB value);
+ WERROR (*del_value) (void *callback_data, const char *key_name,
+ const char *value_name);
+ WERROR (*del_key) (void *callback_data, const char *key_name);
+ WERROR (*del_all_values) (void *callback_data, const char *key_name);
+ WERROR (*done) (void *callback_data);
+};
+
+WERROR reg_diff_apply(struct registry_context *ctx,
+ const char *filename);
+
+WERROR reg_generate_diff(struct registry_context *ctx1,
+ struct registry_context *ctx2,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data);
+WERROR reg_dotreg_diff_save(TALLOC_CTX *ctx, const char *filename,
+ struct reg_diff_callbacks **callbacks,
+ void **callback_data);
+WERROR reg_preg_diff_save(TALLOC_CTX *ctx, const char *filename,
+ struct reg_diff_callbacks **callbacks,
+ void **callback_data);
+WERROR reg_generate_diff_key(struct registry_key *oldkey,
+ struct registry_key *newkey,
+ const char *path,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data);
+WERROR reg_diff_load(const char *filename,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data);
+
+WERROR reg_dotreg_diff_load(int fd,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data);
+
+WERROR reg_preg_diff_load(int fd,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data);
+
+WERROR local_get_predefined_key(struct registry_context *ctx,
+ uint32_t key_id, struct registry_key **key);
+
+
+#endif /* _REGISTRY_H */
diff --git a/source4/lib/registry/rpc.c b/source4/lib/registry/rpc.c
new file mode 100644
index 0000000..2f6edf3
--- /dev/null
+++ b/source4/lib/registry/rpc.c
@@ -0,0 +1,579 @@
+/*
+ Samba Unix/Linux SMB implementation
+ RPC backend for the registry library
+ Copyright (C) 2003-2007 Jelmer Vernooij, jelmer@samba.org
+ Copyright (C) 2008 Matthias Dieter Wallnöfer, mwallnoefer@yahoo.de
+
+ 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 "registry.h"
+#include "librpc/gen_ndr/ndr_winreg_c.h"
+
+#define MAX_NAMESIZE 512
+#define MAX_VALSIZE 32768
+
+struct rpc_key {
+ struct registry_key key;
+ struct policy_handle pol;
+ struct dcerpc_binding_handle *binding_handle;
+ const char* classname;
+ uint32_t num_subkeys;
+ uint32_t max_subkeylen;
+ uint32_t max_classlen;
+ uint32_t num_values;
+ uint32_t max_valnamelen;
+ uint32_t max_valbufsize;
+ uint32_t secdescsize;
+ NTTIME last_changed_time;
+};
+
+struct rpc_registry_context {
+ struct registry_context context;
+ struct dcerpc_pipe *pipe;
+ struct dcerpc_binding_handle *binding_handle;
+};
+
+static struct registry_operations reg_backend_rpc;
+
+/**
+ * This is the RPC backend for the registry library.
+ */
+
+#define openhive(u) static WERROR open_ ## u(struct dcerpc_binding_handle *b, TALLOC_CTX *mem_ctx, struct policy_handle *hnd) \
+{ \
+ struct winreg_Open ## u r; \
+ NTSTATUS status; \
+\
+ ZERO_STRUCT(r); \
+ r.in.system_name = NULL; \
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; \
+ r.out.handle = hnd;\
+\
+ status = dcerpc_winreg_Open ## u ## _r(b, mem_ctx, &r); \
+\
+ if (!NT_STATUS_IS_OK(status)) { \
+ DEBUG(1, ("OpenHive failed - %s\n", nt_errstr(status))); \
+ return ntstatus_to_werror(status); \
+ } \
+\
+ return r.out.result;\
+}
+
+openhive(HKLM)
+openhive(HKCU)
+openhive(HKPD)
+openhive(HKU)
+openhive(HKCR)
+openhive(HKDD)
+openhive(HKCC)
+
+static struct {
+ uint32_t hkey;
+ WERROR (*open) (struct dcerpc_binding_handle *b, TALLOC_CTX *,
+ struct policy_handle *h);
+} known_hives[] = {
+ { HKEY_LOCAL_MACHINE, open_HKLM },
+ { HKEY_CURRENT_USER, open_HKCU },
+ { HKEY_CLASSES_ROOT, open_HKCR },
+ { HKEY_PERFORMANCE_DATA, open_HKPD },
+ { HKEY_USERS, open_HKU },
+ { HKEY_DYN_DATA, open_HKDD },
+ { HKEY_CURRENT_CONFIG, open_HKCC },
+ { 0, NULL }
+};
+
+static WERROR rpc_query_key(TALLOC_CTX *mem_ctx, const struct registry_key *k);
+
+static WERROR rpc_get_predefined_key(struct registry_context *ctx,
+ uint32_t hkey_type,
+ struct registry_key **k)
+{
+ int n;
+ struct rpc_key *mykeydata;
+ struct rpc_registry_context *rctx = talloc_get_type(ctx, struct rpc_registry_context);
+
+ *k = NULL;
+
+ for(n = 0; known_hives[n].hkey; n++) {
+ if(known_hives[n].hkey == hkey_type)
+ break;
+ }
+
+ if (known_hives[n].open == NULL) {
+ DEBUG(1, ("No such hive %d\n", hkey_type));
+ return WERR_NO_MORE_ITEMS;
+ }
+
+ mykeydata = talloc_zero(ctx, struct rpc_key);
+ W_ERROR_HAVE_NO_MEMORY(mykeydata);
+ mykeydata->key.context = ctx;
+ mykeydata->binding_handle = rctx->binding_handle;
+ mykeydata->num_values = -1;
+ mykeydata->num_subkeys = -1;
+ *k = (struct registry_key *)mykeydata;
+ return known_hives[n].open(mykeydata->binding_handle, mykeydata, &mykeydata->pol);
+}
+
+#if 0
+static WERROR rpc_key_put_rpc_data(TALLOC_CTX *mem_ctx, struct registry_key *k)
+{
+ struct winreg_OpenKey r;
+ struct rpc_key_data *mykeydata;
+
+ k->backend_data = mykeydata = talloc_zero(mem_ctx, struct rpc_key_data);
+ mykeydata->num_values = -1;
+ mykeydata->num_subkeys = -1;
+
+ /* Then, open the handle using the hive */
+
+ ZERO_STRUCT(r);
+ r.in.handle = &(((struct rpc_key_data *)k->hive->root->backend_data)->pol);
+ r.in.keyname.name = k->path;
+ r.in.unknown = 0x00000000;
+ r.in.access_mask = 0x02000000;
+ r.out.handle = &mykeydata->pol;
+
+ dcerpc_winreg_OpenKey((struct dcerpc_pipe *)k->hive->backend_data,
+ mem_ctx, &r);
+
+ return r.out.result;
+}
+#endif
+
+static WERROR rpc_open_key(TALLOC_CTX *mem_ctx, struct registry_key *h,
+ const char *name, struct registry_key **key)
+{
+ struct rpc_key *parentkeydata = talloc_get_type(h, struct rpc_key),
+ *mykeydata;
+ struct winreg_OpenKey r;
+ NTSTATUS status;
+
+ mykeydata = talloc_zero(mem_ctx, struct rpc_key);
+ W_ERROR_HAVE_NO_MEMORY(mykeydata);
+ mykeydata->key.context = parentkeydata->key.context;
+ mykeydata->binding_handle = parentkeydata->binding_handle;
+ mykeydata->num_values = -1;
+ mykeydata->num_subkeys = -1;
+ *key = (struct registry_key *)mykeydata;
+
+ /* Then, open the handle using the hive */
+ ZERO_STRUCT(r);
+ r.in.parent_handle = &parentkeydata->pol;
+ r.in.keyname.name = name;
+ r.in.options = 0x00000000;
+ r.in.access_mask = 0x02000000;
+ r.out.handle = &mykeydata->pol;
+
+ status = dcerpc_winreg_OpenKey_r(mykeydata->binding_handle, mem_ctx, &r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("OpenKey failed - %s\n", nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+
+ return r.out.result;
+}
+
+static WERROR rpc_get_value_by_index(TALLOC_CTX *mem_ctx,
+ const struct registry_key *parent,
+ uint32_t n,
+ const char **value_name,
+ uint32_t *type,
+ DATA_BLOB *data)
+{
+ struct rpc_key *mykeydata = talloc_get_type(parent, struct rpc_key);
+ struct winreg_EnumValue r;
+ struct winreg_ValNameBuf name;
+ uint8_t value;
+ uint32_t val_size = MAX_VALSIZE;
+ uint32_t zero = 0;
+ WERROR error;
+ NTSTATUS status;
+
+ if (mykeydata->num_values == -1) {
+ error = rpc_query_key(mem_ctx, parent);
+ if(!W_ERROR_IS_OK(error)) return error;
+ }
+
+ name.name = "";
+ name.size = MAX_NAMESIZE;
+
+ ZERO_STRUCT(r);
+ r.in.handle = &mykeydata->pol;
+ r.in.enum_index = n;
+ r.in.name = &name;
+ r.in.type = (enum winreg_Type *) type;
+ r.in.value = &value;
+ r.in.size = &val_size;
+ r.in.length = &zero;
+ r.out.name = &name;
+ r.out.type = (enum winreg_Type *) type;
+ r.out.value = &value;
+ r.out.size = &val_size;
+ r.out.length = &zero;
+
+ status = dcerpc_winreg_EnumValue_r(mykeydata->binding_handle, mem_ctx, &r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("EnumValue failed - %s\n", nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+
+ *value_name = talloc_steal(mem_ctx, r.out.name->name);
+ *type = *(r.out.type);
+ *data = data_blob_talloc(mem_ctx, r.out.value, *r.out.length);
+
+ return r.out.result;
+}
+
+static WERROR rpc_get_value_by_name(TALLOC_CTX *mem_ctx,
+ const struct registry_key *parent,
+ const char *value_name,
+ uint32_t *type,
+ DATA_BLOB *data)
+{
+ struct rpc_key *mykeydata = talloc_get_type(parent, struct rpc_key);
+ struct winreg_QueryValue r;
+ struct winreg_String name;
+ uint8_t value;
+ uint32_t val_size = MAX_VALSIZE;
+ uint32_t zero = 0;
+ WERROR error;
+ NTSTATUS status;
+
+ if (mykeydata->num_values == -1) {
+ error = rpc_query_key(mem_ctx, parent);
+ if(!W_ERROR_IS_OK(error)) return error;
+ }
+
+ name.name = value_name;
+
+ ZERO_STRUCT(r);
+ r.in.handle = &mykeydata->pol;
+ r.in.value_name = &name;
+ r.in.type = (enum winreg_Type *) type;
+ r.in.data = &value;
+ r.in.data_size = &val_size;
+ r.in.data_length = &zero;
+ r.out.type = (enum winreg_Type *) type;
+ r.out.data = &value;
+ r.out.data_size = &val_size;
+ r.out.data_length = &zero;
+
+ status = dcerpc_winreg_QueryValue_r(mykeydata->binding_handle, mem_ctx, &r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("QueryValue failed - %s\n", nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+
+ *type = *(r.out.type);
+ *data = data_blob_talloc(mem_ctx, r.out.data, *r.out.data_length);
+
+ return r.out.result;
+}
+
+static WERROR rpc_set_value(struct registry_key *key, const char *value_name,
+ uint32_t type, const DATA_BLOB data)
+{
+ struct rpc_key *mykeydata = talloc_get_type(key, struct rpc_key);
+ struct winreg_SetValue r;
+ struct winreg_String name;
+ NTSTATUS status;
+
+ name = (struct winreg_String) { .name = value_name };
+
+ ZERO_STRUCT(r);
+ r.in.handle = &mykeydata->pol;
+ r.in.name = name;
+ r.in.type = (enum winreg_Type)type;
+ r.in.data = data.data;
+ r.in.size = data.length;
+
+ status = dcerpc_winreg_SetValue_r(mykeydata->binding_handle, key, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("SetValue failed - %s\n", nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+
+ return r.out.result;
+}
+
+static WERROR rpc_del_value(TALLOC_CTX *mem_ctx, struct registry_key *key,
+ const char *value_name)
+{
+ struct rpc_key *mykeydata = talloc_get_type(key, struct rpc_key);
+ struct winreg_DeleteValue r;
+ struct winreg_String name;
+ NTSTATUS status;
+
+ name = (struct winreg_String) { .name = value_name };
+
+ ZERO_STRUCT(r);
+ r.in.handle = &mykeydata->pol;
+ r.in.value = name;
+
+ status = dcerpc_winreg_DeleteValue_r(mykeydata->binding_handle,
+ mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("DeleteValue failed - %s\n", nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+
+ return r.out.result;
+}
+
+static WERROR rpc_get_subkey_by_index(TALLOC_CTX *mem_ctx,
+ const struct registry_key *parent,
+ uint32_t n,
+ const char **name,
+ const char **keyclass,
+ NTTIME *last_changed_time)
+{
+ struct winreg_EnumKey r;
+ struct rpc_key *mykeydata = talloc_get_type(parent, struct rpc_key);
+ struct winreg_StringBuf namebuf, classbuf;
+ NTTIME change_time = 0;
+ NTSTATUS status;
+
+ namebuf.name = "";
+ namebuf.size = MAX_NAMESIZE;
+ classbuf.name = NULL;
+ classbuf.size = 0;
+
+ ZERO_STRUCT(r);
+ r.in.handle = &mykeydata->pol;
+ r.in.enum_index = n;
+ r.in.name = &namebuf;
+ r.in.keyclass = &classbuf;
+ r.in.last_changed_time = &change_time;
+ r.out.name = &namebuf;
+ r.out.keyclass = &classbuf;
+ r.out.last_changed_time = &change_time;
+
+ status = dcerpc_winreg_EnumKey_r(mykeydata->binding_handle, mem_ctx, &r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("EnumKey failed - %s\n", nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+
+ if (name != NULL)
+ *name = talloc_steal(mem_ctx, r.out.name->name);
+ if (keyclass != NULL)
+ *keyclass = talloc_steal(mem_ctx, r.out.keyclass->name);
+ if (last_changed_time != NULL)
+ *last_changed_time = *(r.out.last_changed_time);
+
+ return r.out.result;
+}
+
+static WERROR rpc_add_key(TALLOC_CTX *mem_ctx,
+ struct registry_key *parent, const char *path,
+ const char *key_class,
+ struct security_descriptor *sec,
+ struct registry_key **key)
+{
+ struct winreg_CreateKey r;
+ struct rpc_key *parentkd = talloc_get_type(parent, struct rpc_key);
+ struct rpc_key *rpck = talloc_zero(mem_ctx, struct rpc_key);
+
+ NTSTATUS status;
+
+ if (rpck == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ rpck->key.context = parentkd->key.context;
+ rpck->binding_handle = parentkd->binding_handle;
+ rpck->num_values = -1;
+ rpck->num_subkeys = -1;
+
+ ZERO_STRUCT(r);
+ r.in.handle = &parentkd->pol;
+ r.in.name.name = path;
+ r.in.keyclass.name = NULL;
+ r.in.options = 0;
+ r.in.access_mask = 0x02000000;
+ r.in.secdesc = NULL;
+ r.in.action_taken = NULL;
+ r.out.new_handle = &rpck->pol;
+ r.out.action_taken = NULL;
+
+ status = dcerpc_winreg_CreateKey_r(parentkd->binding_handle, mem_ctx, &r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(rpck);
+ DEBUG(1, ("CreateKey failed - %s\n", nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+
+ rpck->binding_handle = parentkd->binding_handle;
+ *key = (struct registry_key *)rpck;
+
+ return r.out.result;
+}
+
+static WERROR rpc_query_key(TALLOC_CTX *mem_ctx, const struct registry_key *k)
+{
+ struct winreg_QueryInfoKey r;
+ struct rpc_key *mykeydata = talloc_get_type(k, struct rpc_key);
+ struct winreg_String classname;
+ NTSTATUS status;
+
+ classname.name = NULL;
+
+ ZERO_STRUCT(r);
+ r.in.handle = &mykeydata->pol;
+ r.in.classname = &classname;
+ r.out.classname = &classname;
+ r.out.num_subkeys = &mykeydata->num_subkeys;
+ r.out.max_subkeylen = &mykeydata->max_subkeylen;
+ r.out.max_classlen = &mykeydata->max_classlen;
+ r.out.num_values = &mykeydata->num_values;
+ r.out.max_valnamelen = &mykeydata->max_valnamelen;
+ r.out.max_valbufsize = &mykeydata->max_valbufsize;
+ r.out.secdescsize = &mykeydata->secdescsize;
+ r.out.last_changed_time = &mykeydata->last_changed_time;
+
+ status = dcerpc_winreg_QueryInfoKey_r(mykeydata->binding_handle, mem_ctx, &r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("QueryInfoKey failed - %s\n", nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+
+ mykeydata->classname = talloc_steal(mem_ctx, r.out.classname->name);
+
+ return r.out.result;
+}
+
+static WERROR rpc_del_key(TALLOC_CTX *mem_ctx, struct registry_key *parent,
+ const char *name)
+{
+ NTSTATUS status;
+ struct rpc_key *mykeydata = talloc_get_type(parent, struct rpc_key);
+ struct winreg_DeleteKey r;
+
+ ZERO_STRUCT(r);
+ r.in.handle = &mykeydata->pol;
+ r.in.key.name = name;
+
+ status = dcerpc_winreg_DeleteKey_r(mykeydata->binding_handle, mem_ctx, &r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("DeleteKey failed - %s\n", nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+
+ return r.out.result;
+}
+
+static WERROR rpc_get_info(TALLOC_CTX *mem_ctx, const struct registry_key *key,
+ const char **classname,
+ uint32_t *num_subkeys,
+ uint32_t *num_values,
+ NTTIME *last_changed_time,
+ uint32_t *max_subkeylen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize)
+{
+ struct rpc_key *mykeydata = talloc_get_type(key, struct rpc_key);
+ WERROR error;
+
+ if (mykeydata->num_values == -1) {
+ error = rpc_query_key(mem_ctx, key);
+ if(!W_ERROR_IS_OK(error)) return error;
+ }
+
+ if (classname != NULL)
+ *classname = mykeydata->classname;
+
+ if (num_subkeys != NULL)
+ *num_subkeys = mykeydata->num_subkeys;
+
+ if (num_values != NULL)
+ *num_values = mykeydata->num_values;
+
+ if (last_changed_time != NULL)
+ *last_changed_time = mykeydata->last_changed_time;
+
+ if (max_subkeylen != NULL)
+ *max_subkeylen = mykeydata->max_subkeylen;
+
+ if (max_valnamelen != NULL)
+ *max_valnamelen = mykeydata->max_valnamelen;
+
+ if (max_valbufsize != NULL)
+ *max_valbufsize = mykeydata->max_valbufsize;
+
+ return WERR_OK;
+}
+
+static struct registry_operations reg_backend_rpc = {
+ .name = "rpc",
+ .open_key = rpc_open_key,
+ .get_predefined_key = rpc_get_predefined_key,
+ .enum_key = rpc_get_subkey_by_index,
+ .enum_value = rpc_get_value_by_index,
+ .get_value = rpc_get_value_by_name,
+ .set_value = rpc_set_value,
+ .delete_value = rpc_del_value,
+ .create_key = rpc_add_key,
+ .delete_key = rpc_del_key,
+ .get_key_info = rpc_get_info,
+};
+
+_PUBLIC_ WERROR reg_open_remote(TALLOC_CTX *mem_ctx,
+ struct registry_context **ctx,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx,
+ const char *location, struct tevent_context *ev)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ struct rpc_registry_context *rctx;
+
+ dcerpc_init();
+
+ rctx = talloc(mem_ctx, struct rpc_registry_context);
+ W_ERROR_HAVE_NO_MEMORY(rctx);
+
+ /* Default to local smbd if no connection is specified */
+ if (!location) {
+ location = talloc_strdup(rctx, "ncalrpc:");
+ }
+
+ status = dcerpc_pipe_connect(rctx /* TALLOC_CTX */,
+ &p, location,
+ &ndr_table_winreg,
+ credentials, ev, lp_ctx);
+ if(NT_STATUS_IS_ERR(status)) {
+ DEBUG(1, ("Unable to open '%s': %s\n", location,
+ nt_errstr(status)));
+ talloc_free(rctx);
+ *ctx = NULL;
+ return ntstatus_to_werror(status);
+ }
+
+ rctx->pipe = p;
+ rctx->binding_handle = p->binding_handle;
+
+ *ctx = (struct registry_context *)rctx;
+ (*ctx)->ops = &reg_backend_rpc;
+
+ return WERR_OK;
+}
diff --git a/source4/lib/registry/samba.c b/source4/lib/registry/samba.c
new file mode 100644
index 0000000..ff52297
--- /dev/null
+++ b/source4/lib/registry/samba.c
@@ -0,0 +1,100 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) Jelmer Vernooij 2004-2007.
+
+ 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 "registry.h"
+#include "param/param.h"
+
+/**
+ * @file
+ * @brief Samba-specific registry functions
+ */
+
+static WERROR mount_samba_hive(struct registry_context *ctx,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ struct auth_session_info *auth_info,
+ struct cli_credentials *creds,
+ const char *name,
+ uint32_t hive_id)
+{
+ WERROR error;
+ struct hive_key *hive;
+ const char *location;
+
+ location = talloc_asprintf(ctx, "%s/%s.ldb",
+ lpcfg_private_dir(lp_ctx),
+ name);
+ W_ERROR_HAVE_NO_MEMORY(location);
+
+ error = reg_open_hive(ctx, location, auth_info, creds, event_ctx, lp_ctx, &hive);
+
+ if (W_ERROR_EQUAL(error, WERR_FILE_NOT_FOUND))
+ error = reg_open_ldb_file(ctx, location, auth_info,
+ creds, event_ctx, lp_ctx, &hive);
+
+ talloc_free(discard_const_p(char, location));
+
+ if (!W_ERROR_IS_OK(error))
+ return error;
+
+ return reg_mount_hive(ctx, hive, hive_id, NULL);
+}
+
+
+_PUBLIC_ WERROR reg_open_samba(TALLOC_CTX *mem_ctx,
+ struct registry_context **ctx,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials)
+{
+ WERROR result;
+
+ result = reg_open_local(mem_ctx, ctx);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ mount_samba_hive(*ctx, ev_ctx, lp_ctx, session_info, credentials,
+ "hklm", HKEY_LOCAL_MACHINE);
+
+ mount_samba_hive(*ctx, ev_ctx, lp_ctx, session_info, credentials,
+ "hkcr", HKEY_CLASSES_ROOT);
+
+ /* FIXME: Should be mounted from NTUSER.DAT in the home directory of the
+ * current user */
+ mount_samba_hive(*ctx, ev_ctx, lp_ctx, session_info, credentials,
+ "hkcu", HKEY_CURRENT_USER);
+
+ mount_samba_hive(*ctx, ev_ctx, lp_ctx, session_info, credentials,
+ "hku", HKEY_USERS);
+
+ /* FIXME: Different hive backend for HKEY_CLASSES_ROOT: merged view of HKEY_LOCAL_MACHINE\Software\Classes
+ * and HKEY_CURRENT_USER\Software\Classes */
+
+ /* FIXME: HKEY_CURRENT_CONFIG is an alias for HKEY_LOCAL_MACHINE\System\CurrentControlSet\Hardware Profiles\Current */
+
+ /* FIXME: HKEY_PERFORMANCE_DATA is dynamically generated */
+
+ /* FIXME: HKEY_LOCAL_MACHINE\Hardware is autogenerated */
+
+ /* FIXME: HKEY_LOCAL_MACHINE\Security\SAM is an alias for HKEY_LOCAL_MACHINE\SAM */
+
+ return WERR_OK;
+}
diff --git a/source4/lib/registry/tests/diff.c b/source4/lib/registry/tests/diff.c
new file mode 100644
index 0000000..5d2bfd7
--- /dev/null
+++ b/source4/lib/registry/tests/diff.c
@@ -0,0 +1,291 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local testing of registry diff functionality
+
+ Copyright (C) Jelmer Vernooij 2007
+ Copyright (C) Wilco Baan Hofman 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 "lib/registry/registry.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/winreg.h"
+#include "param/param.h"
+#include "lib/registry/tests/proto.h"
+
+struct diff_tcase_data {
+ struct registry_context *r1_ctx;
+ struct registry_context *r2_ctx;
+ struct reg_diff_callbacks *callbacks;
+ void *callback_data;
+ char *tempdir;
+ char *filename;
+};
+
+static bool test_generate_diff(struct torture_context *tctx, void *tcase_data)
+{
+ WERROR error;
+ struct diff_tcase_data *td = tcase_data;
+
+ error = reg_generate_diff(td->r1_ctx, td->r2_ctx,
+ td->callbacks,
+ td->callback_data);
+ torture_assert_werr_ok(tctx, error, "reg_generate_diff");
+
+ return true;
+}
+
+#if 0
+static bool test_diff_load(struct torture_context *tctx, void *tcase_data)
+{
+ struct diff_tcase_data *td = tcase_data;
+ struct reg_diff_callbacks *callbacks;
+ void *data;
+ WERROR error;
+
+ error = reg_diff_load(td->filename, callbacks, data);
+ torture_assert_werr_ok(tctx, error, "reg_diff_load");
+
+ return true;
+}
+#endif
+static bool test_diff_apply(struct torture_context *tctx, void *tcase_data)
+{
+ struct diff_tcase_data *td = tcase_data;
+ struct registry_key *key;
+ WERROR error;
+
+ error = reg_diff_apply(td->r1_ctx, td->filename);
+ torture_assert_werr_ok(tctx, error, "reg_diff_apply");
+
+ error = td->r1_ctx->ops->get_predefined_key(td->r1_ctx, HKEY_LOCAL_MACHINE, &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKEY_LOCAL_MACHINE failed");
+
+ /* If this generates an error it could be that the apply doesn't work,
+ * but also that the reg_generate_diff didn't work. */
+ error = td->r1_ctx->ops->open_key(td->r1_ctx, key, "Software", &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKLM\\Software failed");
+ error = td->r1_ctx->ops->open_key(td->r1_ctx, key, "Microsoft", &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKLM\\Software\\Microsoft failed");
+ error = td->r1_ctx->ops->open_key(td->r1_ctx, key, "Windows", &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKLM\\..\\Microsoft\\Windows failed");
+ error = td->r1_ctx->ops->open_key(td->r1_ctx, key, "CurrentVersion", &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKLM\\..\\Windows\\CurrentVersion failed");
+ error = td->r1_ctx->ops->open_key(td->r1_ctx, key, "Policies", &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKLM\\..\\CurrentVersion\\Policies failed");
+ error = td->r1_ctx->ops->open_key(td->r1_ctx, key, "Explorer", &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKLM\\..\\Policies\\Explorer failed");
+
+ return true;
+}
+
+static const char *added_key = NULL;
+
+static WERROR test_add_key(void *callback_data, const char *key_name)
+{
+ added_key = talloc_strdup(callback_data, key_name);
+
+ return WERR_OK;
+}
+
+static bool test_generate_diff_key_add(struct torture_context *tctx, void *tcase_data)
+{
+ struct reg_diff_callbacks cb;
+ struct registry_key rk;
+
+ return true;
+
+ ZERO_STRUCT(cb);
+
+ cb.add_key = test_add_key;
+
+ if (W_ERROR_IS_OK(reg_generate_diff_key(&rk, NULL, "bla", &cb, tctx)))
+ return false;
+
+ torture_assert_str_equal(tctx, added_key, "bla", "key added");
+
+ return true;
+}
+
+static bool test_generate_diff_key_null(struct torture_context *tctx, void *tcase_data)
+{
+ struct reg_diff_callbacks cb;
+
+ ZERO_STRUCT(cb);
+
+ if (!W_ERROR_IS_OK(reg_generate_diff_key(NULL, NULL, "", &cb, NULL)))
+ return false;
+
+ return true;
+}
+
+static void tcase_add_tests (struct torture_tcase *tcase)
+{
+ torture_tcase_add_simple_test(tcase, "test_generate_diff_key_add",
+ test_generate_diff_key_add);
+ torture_tcase_add_simple_test(tcase, "test_generate_diff_key_null",
+ test_generate_diff_key_null);
+ torture_tcase_add_simple_test(tcase, "test_generate_diff",
+ test_generate_diff);
+ torture_tcase_add_simple_test(tcase, "test_diff_apply",
+ test_diff_apply);
+/* torture_tcase_add_simple_test(tcase, "test_diff_load",
+ test_diff_load);
+*/
+}
+
+static bool diff_setup_tcase(struct torture_context *tctx, void **data)
+{
+ struct registry_context *r1_ctx, *r2_ctx;
+ WERROR error;
+ NTSTATUS status;
+ struct hive_key *r1_hklm, *r1_hkcu;
+ struct hive_key *r2_hklm, *r2_hkcu;
+ const char *filename;
+ struct diff_tcase_data *td;
+ struct registry_key *key, *newkey;
+ DATA_BLOB blob;
+
+ td = talloc(tctx, struct diff_tcase_data);
+
+ /* Create two registry contexts */
+ error = reg_open_local(tctx, &r1_ctx);
+ torture_assert_werr_ok(tctx, error, "Opening registry 1 for patch tests failed");
+
+ error = reg_open_local(tctx, &r2_ctx);
+ torture_assert_werr_ok(tctx, error, "Opening registry 2 for patch tests failed");
+
+ /* Create temp directory */
+ status = torture_temp_dir(tctx, "patchfile", &td->tempdir);
+ torture_assert_ntstatus_ok(tctx, status, "Creating temp dir failed");
+
+ /* Create and mount HKLM and HKCU hives for registry 1 */
+ filename = talloc_asprintf(tctx, "%s/r1_local_machine.ldb", td->tempdir);
+ error = reg_open_ldb_file(tctx, filename, NULL, NULL, tctx->ev, tctx->lp_ctx, &r1_hklm);
+ torture_assert_werr_ok(tctx, error, "Opening local machine file failed");
+
+ error = reg_mount_hive(r1_ctx, r1_hklm, HKEY_LOCAL_MACHINE, NULL);
+ torture_assert_werr_ok(tctx, error, "Mounting hive failed");
+
+ filename = talloc_asprintf(tctx, "%s/r1_current_user.ldb", td->tempdir);
+ error = reg_open_ldb_file(tctx, filename, NULL, NULL, tctx->ev, tctx->lp_ctx, &r1_hkcu);
+ torture_assert_werr_ok(tctx, error, "Opening current user file failed");
+
+ error = reg_mount_hive(r1_ctx, r1_hkcu, HKEY_CURRENT_USER, NULL);
+ torture_assert_werr_ok(tctx, error, "Mounting hive failed");
+
+ /* Create and mount HKLM and HKCU hives for registry 2 */
+ filename = talloc_asprintf(tctx, "%s/r2_local_machine.ldb", td->tempdir);
+ error = reg_open_ldb_file(tctx, filename, NULL, NULL, tctx->ev, tctx->lp_ctx, &r2_hklm);
+ torture_assert_werr_ok(tctx, error, "Opening local machine file failed");
+
+ error = reg_mount_hive(r2_ctx, r2_hklm, HKEY_LOCAL_MACHINE, NULL);
+ torture_assert_werr_ok(tctx, error, "Mounting hive failed");
+
+ filename = talloc_asprintf(tctx, "%s/r2_current_user.ldb", td->tempdir);
+ error = reg_open_ldb_file(tctx, filename, NULL, NULL, tctx->ev, tctx->lp_ctx, &r2_hkcu);
+ torture_assert_werr_ok(tctx, error, "Opening current user file failed");
+
+ error = reg_mount_hive(r2_ctx, r2_hkcu, HKEY_CURRENT_USER, NULL);
+ torture_assert_werr_ok(tctx, error, "Mounting hive failed");
+
+ error = r1_ctx->ops->get_predefined_key(r1_ctx, HKEY_CURRENT_USER, &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKEY_CURRENT_USER failed");
+ error = r1_ctx->ops->create_key(r1_ctx, key, "Network", NULL, NULL, &newkey);
+ torture_assert_werr_ok(tctx, error, "Opening HKCU\\Network failed");
+ error = r1_ctx->ops->create_key(r1_ctx, newkey, "L", NULL, NULL, &newkey);
+ torture_assert_werr_ok(tctx, error, "Opening HKCU\\Network\\L failed");
+
+ error = r2_ctx->ops->get_predefined_key(r2_ctx, HKEY_LOCAL_MACHINE, &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKEY_LOCAL_MACHINE failed");
+ error = r2_ctx->ops->create_key(r2_ctx, key, "Software", NULL, NULL, &newkey);
+ torture_assert_werr_ok(tctx, error, "Creating HKLM\\Sofware failed");
+ error = r2_ctx->ops->create_key(r2_ctx, newkey, "Microsoft", NULL, NULL, &newkey);
+ torture_assert_werr_ok(tctx, error, "Creating HKLM\\Software\\Microsoft failed");
+ error = r2_ctx->ops->create_key(r2_ctx, newkey, "Windows", NULL, NULL, &newkey);
+ torture_assert_werr_ok(tctx, error, "Creating HKLM\\Software\\Microsoft\\Windows failed");
+ error = r2_ctx->ops->create_key(r2_ctx, newkey, "CurrentVersion", NULL, NULL, &newkey);
+ torture_assert_werr_ok(tctx, error, "Creating HKLM\\..\\Windows\\CurrentVersion failed");
+ error = r2_ctx->ops->create_key(r2_ctx, newkey, "Policies", NULL, NULL, &newkey);
+ torture_assert_werr_ok(tctx, error, "Creating HKLM\\..\\CurrentVersion\\Policies failed");
+ error = r2_ctx->ops->create_key(r2_ctx, newkey, "Explorer", NULL, NULL, &newkey);
+ torture_assert_werr_ok(tctx, error, "Creating HKLM\\..\\Policies\\Explorer failed");
+
+ blob.data = talloc_array(r2_ctx, uint8_t, 4);
+ /* set "0x03FFFFFF" in little endian format */
+ blob.data[0] = 0xFF; blob.data[1] = 0xFF;
+ blob.data[2] = 0xFF; blob.data[3] = 0x03;
+ blob.length = 4;
+
+ r1_ctx->ops->set_value(newkey, "NoDrives", REG_DWORD, blob);
+
+ /* Set test case data */
+ td->r1_ctx = r1_ctx;
+ td->r2_ctx = r2_ctx;
+
+ *data = td;
+
+ return true;
+}
+
+static bool diff_setup_preg_tcase (struct torture_context *tctx, void **data)
+{
+ struct diff_tcase_data *td;
+ WERROR error;
+
+ diff_setup_tcase(tctx, data);
+ td = *data;
+
+ td->filename = talloc_asprintf(tctx, "%s/test.pol", td->tempdir);
+ error = reg_preg_diff_save(tctx, td->filename, &td->callbacks,
+ &td->callback_data);
+ torture_assert_werr_ok(tctx, error, "reg_preg_diff_save");
+
+ return true;
+}
+
+static bool diff_setup_dotreg_tcase (struct torture_context *tctx, void **data)
+{
+ struct diff_tcase_data *td;
+ WERROR error;
+
+ diff_setup_tcase(tctx, data);
+ td = *data;
+
+ td->filename = talloc_asprintf(tctx, "%s/test.reg", td->tempdir);
+ error = reg_dotreg_diff_save(tctx, td->filename, &td->callbacks,
+ &td->callback_data);
+ torture_assert_werr_ok(tctx, error, "reg_dotreg_diff_save");
+
+ return true;
+}
+
+struct torture_suite *torture_registry_diff(TALLOC_CTX *mem_ctx)
+{
+ struct torture_tcase *tcase;
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "diff");
+
+ tcase = torture_suite_add_tcase(suite, "PReg");
+ torture_tcase_set_fixture(tcase, diff_setup_preg_tcase, NULL);
+ tcase_add_tests(tcase);
+
+ tcase = torture_suite_add_tcase(suite, "dotreg");
+ torture_tcase_set_fixture(tcase, diff_setup_dotreg_tcase, NULL);
+ tcase_add_tests(tcase);
+
+ return suite;
+}
diff --git a/source4/lib/registry/tests/generic.c b/source4/lib/registry/tests/generic.c
new file mode 100644
index 0000000..2ef6f84
--- /dev/null
+++ b/source4/lib/registry/tests/generic.c
@@ -0,0 +1,179 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local testing of registry library
+
+ Copyright (C) Jelmer Vernooij 2005-2007
+
+ 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 "lib/registry/registry.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/winreg.h"
+#include "param/param.h"
+#include "lib/registry/tests/proto.h"
+
+static bool test_str_regtype(struct torture_context *ctx)
+{
+ torture_assert_str_equal(ctx, str_regtype(0),
+ "REG_NONE", "REG_NONE failed");
+ torture_assert_str_equal(ctx, str_regtype(1),
+ "REG_SZ", "REG_SZ failed");
+ torture_assert_str_equal(ctx, str_regtype(2),
+ "REG_EXPAND_SZ", "REG_EXPAND_SZ failed");
+ torture_assert_str_equal(ctx, str_regtype(3),
+ "REG_BINARY", "REG_BINARY failed");
+ torture_assert_str_equal(ctx, str_regtype(4),
+ "REG_DWORD", "REG_DWORD failed");
+ torture_assert_str_equal(ctx, str_regtype(5),
+ "REG_DWORD_BIG_ENDIAN", "REG_DWORD_BIG_ENDIAN failed");
+ torture_assert_str_equal(ctx, str_regtype(6),
+ "REG_LINK", "REG_LINK failed");
+ torture_assert_str_equal(ctx, str_regtype(7),
+ "REG_MULTI_SZ", "REG_MULTI_SZ failed");
+ torture_assert_str_equal(ctx, str_regtype(8),
+ "REG_RESOURCE_LIST", "REG_RESOURCE_LIST failed");
+ torture_assert_str_equal(ctx, str_regtype(9),
+ "REG_FULL_RESOURCE_DESCRIPTOR", "REG_FULL_RESOURCE_DESCRIPTOR failed");
+ torture_assert_str_equal(ctx, str_regtype(10),
+ "REG_RESOURCE_REQUIREMENTS_LIST", "REG_RESOURCE_REQUIREMENTS_LIST failed");
+ torture_assert_str_equal(ctx, str_regtype(11),
+ "REG_QWORD", "REG_QWORD failed");
+
+ return true;
+}
+
+
+static bool test_reg_val_data_string_dword(struct torture_context *ctx)
+{
+ uint8_t d[] = { 0x20, 0x00, 0x00, 0x00 };
+ DATA_BLOB db = { d, 4 };
+ torture_assert_str_equal(ctx, "0x00000020",
+ reg_val_data_string(ctx, REG_DWORD, db),
+ "dword failed");
+ return true;
+}
+
+static bool test_reg_val_data_string_dword_big_endian(struct torture_context *ctx)
+{
+ uint8_t d[] = { 0x20, 0x00, 0x00, 0x00 };
+ DATA_BLOB db = { d, 4 };
+ torture_assert_str_equal(ctx, "0x00000020",
+ reg_val_data_string(ctx, REG_DWORD_BIG_ENDIAN, db),
+ "dword failed");
+ return true;
+}
+
+static bool test_reg_val_data_string_qword(struct torture_context *ctx)
+{
+ uint8_t d[] = { 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ DATA_BLOB db = { d, 8 };
+ torture_assert_str_equal(ctx, "0x0000000000000020",
+ reg_val_data_string(ctx, REG_QWORD, db),
+ "qword failed");
+ return true;
+}
+
+static bool test_reg_val_data_string_sz(struct torture_context *ctx)
+{
+ DATA_BLOB db;
+ convert_string_talloc(ctx, CH_UTF8, CH_UTF16,
+ "bla", 3, (void **)&db.data, &db.length);
+ torture_assert_str_equal(ctx, "bla",
+ reg_val_data_string(ctx, REG_SZ, db),
+ "sz failed");
+ db.length = 4;
+ torture_assert_str_equal(ctx, "bl",
+ reg_val_data_string(ctx, REG_SZ, db),
+ "sz failed");
+ return true;
+}
+
+static bool test_reg_val_data_string_binary(struct torture_context *ctx)
+{
+ uint8_t x[] = { 0x1, 0x2, 0x3, 0x4 };
+ DATA_BLOB db = { x, 4 };
+ torture_assert_str_equal(ctx, "01020304",
+ reg_val_data_string(ctx, REG_BINARY, db),
+ "binary failed");
+ return true;
+}
+
+
+static bool test_reg_val_data_string_empty(struct torture_context *ctx)
+{
+ DATA_BLOB db = { NULL, 0 };
+ torture_assert_str_equal(ctx, "",
+ reg_val_data_string(ctx, REG_BINARY, db),
+ "empty failed");
+ return true;
+}
+
+static bool test_reg_val_description(struct torture_context *ctx)
+{
+ DATA_BLOB data;
+ convert_string_talloc(ctx, CH_UTF8, CH_UTF16,
+ "stationary traveller",
+ strlen("stationary traveller"),
+ (void **)&data.data, &data.length);
+ torture_assert_str_equal(ctx, "camel = REG_SZ : stationary traveller",
+ reg_val_description(ctx, "camel", REG_SZ, data),
+ "reg_val_description failed");
+ return true;
+}
+
+
+static bool test_reg_val_description_nullname(struct torture_context *ctx)
+{
+ DATA_BLOB data;
+ convert_string_talloc(ctx, CH_UTF8, CH_UTF16,
+ "west berlin",
+ strlen("west berlin"),
+ (void **)&data.data, &data.length);
+ torture_assert_str_equal(ctx, "<No Name> = REG_SZ : west berlin",
+ reg_val_description(ctx, NULL, REG_SZ, data),
+ "description with null name failed");
+ return true;
+}
+
+struct torture_suite *torture_registry(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "registry");
+ torture_suite_add_simple_test(suite, "str_regtype",
+ test_str_regtype);
+ torture_suite_add_simple_test(suite, "reg_val_data_string dword",
+ test_reg_val_data_string_dword);
+ torture_suite_add_simple_test(suite, "reg_val_data_string dword_big_endian",
+ test_reg_val_data_string_dword_big_endian);
+ torture_suite_add_simple_test(suite, "reg_val_data_string qword",
+ test_reg_val_data_string_qword);
+ torture_suite_add_simple_test(suite, "reg_val_data_string sz",
+ test_reg_val_data_string_sz);
+ torture_suite_add_simple_test(suite, "reg_val_data_string binary",
+ test_reg_val_data_string_binary);
+ torture_suite_add_simple_test(suite, "reg_val_data_string empty",
+ test_reg_val_data_string_empty);
+ torture_suite_add_simple_test(suite, "reg_val_description",
+ test_reg_val_description);
+ torture_suite_add_simple_test(suite, "reg_val_description null",
+ test_reg_val_description_nullname);
+
+ torture_suite_add_suite(suite, torture_registry_hive(suite));
+ torture_suite_add_suite(suite, torture_registry_registry(suite));
+ torture_suite_add_suite(suite, torture_registry_diff(suite));
+
+ return suite;
+}
diff --git a/source4/lib/registry/tests/hive.c b/source4/lib/registry/tests/hive.c
new file mode 100644
index 0000000..aca5cff
--- /dev/null
+++ b/source4/lib/registry/tests/hive.c
@@ -0,0 +1,440 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local testing of registry library - hives
+
+ Copyright (C) Jelmer Vernooij 2005-2007
+
+ 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "lib/registry/registry.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/winreg.h"
+#include "system/filesys.h"
+#include "param/param.h"
+#include "libcli/security/security.h"
+#include "lib/registry/tests/proto.h"
+
+static bool test_del_nonexistent_key(struct torture_context *tctx,
+ const void *test_data)
+{
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ WERROR error = hive_key_del(tctx, root, "bla");
+ torture_assert_werr_equal(tctx, error, WERR_FILE_NOT_FOUND,
+ "invalid return code");
+
+ return true;
+}
+
+static bool test_keyinfo_root(struct torture_context *tctx,
+ const void *test_data)
+{
+ uint32_t num_subkeys, num_values;
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ WERROR error;
+
+ /* This is a new backend. There should be no subkeys and no
+ * values */
+ error = hive_key_get_info(tctx, root, NULL, &num_subkeys, &num_values,
+ NULL, NULL, NULL, NULL);
+ torture_assert_werr_ok(tctx, error, "reg_key_num_subkeys()");
+
+ torture_assert_int_equal(tctx, num_subkeys, 0,
+ "New key has non-zero subkey count");
+
+ torture_assert_werr_ok(tctx, error, "reg_key_num_values");
+
+ torture_assert_int_equal(tctx, num_values, 0,
+ "New key has non-zero value count");
+
+ return true;
+}
+
+static bool test_keyinfo_nums(struct torture_context *tctx, void *test_data)
+{
+ uint32_t num_subkeys, num_values;
+ struct hive_key *root = (struct hive_key *)test_data;
+ WERROR error;
+ struct hive_key *subkey;
+ uint8_t d[] = { 0x42, 0x00, 0x00, 0x00 };
+ DATA_BLOB db = { d, 4 };
+
+ error = hive_key_add_name(tctx, root, "Nested Keyll", NULL,
+ NULL, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ error = hive_key_set_value(root, "Answer", REG_DWORD, db);
+ torture_assert_werr_ok(tctx, error, "hive_key_set_value");
+
+ /* This is a new backend. There should be no subkeys and no
+ * values */
+ error = hive_key_get_info(tctx, root, NULL, &num_subkeys, &num_values,
+ NULL, NULL, NULL, NULL);
+ torture_assert_werr_ok(tctx, error, "reg_key_num_subkeys()");
+
+ torture_assert_int_equal(tctx, num_subkeys, 1, "subkey count");
+
+ torture_assert_werr_ok(tctx, error, "reg_key_num_values");
+
+ torture_assert_int_equal(tctx, num_values, 1, "value count");
+
+ return true;
+}
+
+static bool test_add_subkey(struct torture_context *tctx,
+ const void *test_data)
+{
+ WERROR error;
+ struct hive_key *subkey;
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ TALLOC_CTX *mem_ctx = tctx;
+
+ error = hive_key_add_name(mem_ctx, root, "Nested Key", NULL,
+ NULL, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ error = hive_key_del(mem_ctx, root, "Nested Key");
+ torture_assert_werr_ok(tctx, error, "reg_key_del");
+
+ return true;
+}
+
+static bool test_del_recursive(struct torture_context *tctx,
+ const void *test_data)
+{
+ WERROR error;
+ struct hive_key *subkey;
+ struct hive_key *subkey2;
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ TALLOC_CTX *mem_ctx = tctx;
+ uint8_t d[] = { 0x42, 0x00, 0x00, 0x00 };
+ DATA_BLOB db = { d, 4 };
+
+ /* Create a new key under the root */
+ error = hive_key_add_name(mem_ctx, root, "Parent Key", NULL,
+ NULL, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ /* Create a new key under "Parent Key" */
+ error = hive_key_add_name(mem_ctx, subkey, "Child Key", NULL,
+ NULL, &subkey2);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ /* Create a new value under "Child Key" */
+ error = hive_key_set_value(subkey2, "Answer Recursive", REG_DWORD, db);
+ torture_assert_werr_ok(tctx, error, "hive_key_set_value");
+
+ /* Deleting "Parent Key" will also delete "Child Key" and the value. */
+ error = hive_key_del(mem_ctx, root, "Parent Key");
+ torture_assert_werr_ok(tctx, error, "hive_key_del");
+
+ return true;
+}
+
+static bool test_flush_key(struct torture_context *tctx, void *test_data)
+{
+ struct hive_key *root = (struct hive_key *)test_data;
+
+ torture_assert_werr_ok(tctx, hive_key_flush(root), "flush key");
+
+ return true;
+}
+
+static bool test_del_key(struct torture_context *tctx, const void *test_data)
+{
+ WERROR error;
+ struct hive_key *subkey;
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ TALLOC_CTX *mem_ctx = tctx;
+
+ error = hive_key_add_name(mem_ctx, root, "Nested Key", NULL,
+ NULL, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ error = hive_key_del(mem_ctx, root, "Nested Key");
+ torture_assert_werr_ok(tctx, error, "reg_key_del");
+
+ error = hive_key_del(mem_ctx, root, "Nested Key");
+ torture_assert_werr_equal(tctx, error, WERR_FILE_NOT_FOUND, "reg_key_del");
+
+ return true;
+}
+
+static bool test_set_value(struct torture_context *tctx,
+ const void *test_data)
+{
+ WERROR error;
+ struct hive_key *subkey;
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ TALLOC_CTX *mem_ctx = tctx;
+ uint8_t d[] = { 0x42, 0x00, 0x00, 0x00 };
+ DATA_BLOB db = { d, 4 };
+
+ error = hive_key_add_name(mem_ctx, root, "YA Nested Key", NULL,
+ NULL, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ error = hive_key_set_value(subkey, "Answer", REG_DWORD, db);
+ torture_assert_werr_ok(tctx, error, "hive_key_set_value");
+
+ return true;
+}
+
+static bool test_get_value(struct torture_context *tctx, const void *test_data)
+{
+ WERROR error;
+ struct hive_key *subkey;
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ TALLOC_CTX *mem_ctx = tctx;
+ uint32_t type;
+ uint8_t d[] = { 0x42, 0x00, 0x00, 0x00 };
+ DATA_BLOB db = { d, 4 }, data;
+
+ error = hive_key_add_name(mem_ctx, root, "EYA Nested Key", NULL,
+ NULL, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ error = hive_get_value(mem_ctx, subkey, "Answer", &type, &data);
+ torture_assert_werr_equal(tctx, error, WERR_FILE_NOT_FOUND,
+ "getting missing value");
+
+ error = hive_key_set_value(subkey, "Answer", REG_DWORD, db);
+ torture_assert_werr_ok(tctx, error, "hive_key_set_value");
+
+ error = hive_get_value(mem_ctx, subkey, "Answer", &type, &data);
+ torture_assert_werr_ok(tctx, error, "getting value");
+
+ torture_assert_int_equal(tctx, data.length, 4, "value length");
+ torture_assert_int_equal(tctx, type, REG_DWORD, "value type");
+
+ torture_assert_mem_equal(tctx, data.data, db.data, 4, "value data");
+
+ return true;
+}
+
+static bool test_del_value(struct torture_context *tctx, const void *test_data)
+{
+ WERROR error;
+ struct hive_key *subkey;
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ TALLOC_CTX *mem_ctx = tctx;
+ uint32_t type;
+ uint8_t d[] = { 0x42, 0x00, 0x00, 0x00 };
+ DATA_BLOB db = { d, 4 };
+
+ error = hive_key_add_name(mem_ctx, root, "EEYA Nested Key", NULL,
+ NULL, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ error = hive_key_set_value(subkey, "Answer", REG_DWORD, db);
+ torture_assert_werr_ok(tctx, error, "hive_key_set_value");
+
+ error = hive_key_del_value(mem_ctx, subkey, "Answer");
+ torture_assert_werr_ok(tctx, error, "deleting value");
+
+ error = hive_get_value(mem_ctx, subkey, "Answer", &type, &db);
+ torture_assert_werr_equal(tctx, error, WERR_FILE_NOT_FOUND, "getting value");
+
+ error = hive_key_del_value(mem_ctx, subkey, "Answer");
+ torture_assert_werr_equal(tctx, error, WERR_FILE_NOT_FOUND,
+ "deleting value");
+
+ return true;
+}
+
+static bool test_list_values(struct torture_context *tctx,
+ const void *test_data)
+{
+ WERROR error;
+ struct hive_key *subkey;
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ TALLOC_CTX *mem_ctx = tctx;
+ uint32_t type;
+ uint8_t d[] = { 0x42, 0x00, 0x00, 0x00 };
+ DATA_BLOB db = { d, 4 }, data;
+ const char *name;
+
+ error = hive_key_add_name(mem_ctx, root, "AYAYA Nested Key", NULL,
+ NULL, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ error = hive_key_set_value(subkey, "Answer", REG_DWORD, db);
+ torture_assert_werr_ok(tctx, error, "hive_key_set_value");
+
+ error = hive_get_value_by_index(mem_ctx, subkey, 0, &name,
+ &type, &data);
+ torture_assert_werr_ok(tctx, error, "getting value");
+
+ torture_assert_str_equal(tctx, name, "Answer", "value name");
+
+ torture_assert_int_equal(tctx, data.length, 4, "value length");
+ torture_assert_int_equal(tctx, type, REG_DWORD, "value type");
+
+ torture_assert_mem_equal(tctx, data.data, db.data, 4, "value data");
+
+ error = hive_get_value_by_index(mem_ctx, subkey, 1, &name,
+ &type, &data);
+ torture_assert_werr_equal(tctx, error, WERR_NO_MORE_ITEMS,
+ "getting missing value");
+
+ return true;
+}
+
+static bool test_hive_security(struct torture_context *tctx, const void *_data)
+{
+ struct hive_key *subkey = NULL;
+ const struct hive_key *root = _data;
+ WERROR error;
+ struct security_descriptor *osd, *nsd;
+
+ osd = security_descriptor_dacl_create(tctx,
+ 0,
+ NULL, NULL,
+ SID_NT_AUTHENTICATED_USERS,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_GENERIC_ALL,
+ SEC_ACE_FLAG_OBJECT_INHERIT,
+ NULL);
+
+
+ error = hive_key_add_name(tctx, root, "SecurityKey", NULL,
+ osd, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ error = hive_get_sec_desc(tctx, subkey, &nsd);
+ torture_assert_werr_ok (tctx, error, "getting security descriptor");
+
+ torture_assert(tctx, security_descriptor_equal(osd, nsd),
+ "security descriptor changed!");
+
+ /* Create a fresh security descriptor */
+ talloc_free(osd);
+ osd = security_descriptor_dacl_create(tctx,
+ 0,
+ NULL, NULL,
+ SID_NT_AUTHENTICATED_USERS,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_GENERIC_ALL,
+ SEC_ACE_FLAG_OBJECT_INHERIT,
+ NULL);
+
+ error = hive_set_sec_desc(subkey, osd);
+ torture_assert_werr_ok(tctx, error, "setting security descriptor");
+
+ error = hive_get_sec_desc(tctx, subkey, &nsd);
+ torture_assert_werr_ok (tctx, error, "getting security descriptor");
+
+ torture_assert(tctx, security_descriptor_equal(osd, nsd),
+ "security descriptor changed!");
+
+ return true;
+}
+
+static void tcase_add_tests(struct torture_tcase *tcase)
+{
+ torture_tcase_add_simple_test_const(tcase, "del_nonexistent_key",
+ test_del_nonexistent_key);
+ torture_tcase_add_simple_test_const(tcase, "add_subkey",
+ test_add_subkey);
+ torture_tcase_add_simple_test(tcase, "flush_key",
+ test_flush_key);
+ /* test_del_recursive() test must run before test_keyinfo_root().
+ test_keyinfo_root() checks the number of subkeys, which verifies
+ the recursive delete worked properly. */
+ torture_tcase_add_simple_test_const(tcase, "del_recursive",
+ test_del_recursive);
+ torture_tcase_add_simple_test_const(tcase, "get_info",
+ test_keyinfo_root);
+ torture_tcase_add_simple_test(tcase, "get_info_nums",
+ test_keyinfo_nums);
+ torture_tcase_add_simple_test_const(tcase, "set_value",
+ test_set_value);
+ torture_tcase_add_simple_test_const(tcase, "get_value",
+ test_get_value);
+ torture_tcase_add_simple_test_const(tcase, "list_values",
+ test_list_values);
+ torture_tcase_add_simple_test_const(tcase, "del_key",
+ test_del_key);
+ torture_tcase_add_simple_test_const(tcase, "del_value",
+ test_del_value);
+ torture_tcase_add_simple_test_const(tcase, "check hive security",
+ test_hive_security);
+}
+
+static bool hive_setup_ldb(struct torture_context *tctx, void **data)
+{
+ struct hive_key *key;
+ WERROR error;
+ char *dirname;
+ NTSTATUS status;
+
+ status = torture_temp_dir(tctx, "hive-ldb", &dirname);
+ if (!NT_STATUS_IS_OK(status))
+ return false;
+
+ rmdir(dirname);
+
+ error = reg_open_ldb_file(tctx, dirname, NULL, NULL, tctx->ev, tctx->lp_ctx, &key);
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Unable to initialize ldb hive\n");
+ return false;
+ }
+
+ *data = key;
+
+ return true;
+}
+
+static bool hive_setup_regf(struct torture_context *tctx, void **data)
+{
+ struct hive_key *key;
+ WERROR error;
+ char *dirname;
+ NTSTATUS status;
+
+ status = torture_temp_dir(tctx, "hive-regf", &dirname);
+ if (!NT_STATUS_IS_OK(status))
+ return false;
+
+ rmdir(dirname);
+
+ error = reg_create_regf_file(tctx, dirname, 5, &key);
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Unable to create new regf file\n");
+ return false;
+ }
+
+ *data = key;
+
+ return true;
+}
+
+struct torture_suite *torture_registry_hive(TALLOC_CTX *mem_ctx)
+{
+ struct torture_tcase *tcase;
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "hive");
+
+ tcase = torture_suite_add_tcase(suite, "ldb");
+ torture_tcase_set_fixture(tcase, hive_setup_ldb, NULL);
+ tcase_add_tests(tcase);
+
+ tcase = torture_suite_add_tcase(suite, "regf");
+ torture_tcase_set_fixture(tcase, hive_setup_regf, NULL);
+ tcase_add_tests(tcase);
+
+ return suite;
+}
diff --git a/source4/lib/registry/tests/registry.c b/source4/lib/registry/tests/registry.c
new file mode 100644
index 0000000..4d94b5a
--- /dev/null
+++ b/source4/lib/registry/tests/registry.c
@@ -0,0 +1,645 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local testing of registry library - registry backend
+
+ Copyright (C) Jelmer Vernooij 2005-2007
+
+ 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "lib/registry/registry.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/winreg.h"
+#include "libcli/security/security.h"
+#include "system/filesys.h"
+#include "lib/registry/tests/proto.h"
+
+/**
+ * Test obtaining a predefined key.
+ */
+static bool test_get_predefined(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root;
+ WERROR error;
+
+ error = reg_get_predefined_key(rctx, HKEY_CLASSES_ROOT, &root);
+ torture_assert_werr_ok(tctx, error,
+ "getting predefined key failed");
+ return true;
+}
+
+/**
+ * Test obtaining a predefined key.
+ */
+static bool test_get_predefined_unknown(struct torture_context *tctx,
+ void *_data)
+{
+ struct registry_context *rctx = _data;
+ struct registry_key *root;
+ WERROR error;
+
+ error = reg_get_predefined_key(rctx, 1337, &root);
+ torture_assert_werr_equal(tctx, error, WERR_FILE_NOT_FOUND,
+ "getting predefined key failed");
+ return true;
+}
+
+static bool test_predef_key_by_name(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root;
+ WERROR error;
+
+ error = reg_get_predefined_key_by_name(rctx, "HKEY_CLASSES_ROOT",
+ &root);
+ torture_assert_werr_ok(tctx, error,
+ "getting predefined key failed");
+
+ error = reg_get_predefined_key_by_name(rctx, "HKEY_classes_ROOT",
+ &root);
+ torture_assert_werr_ok(tctx, error,
+ "getting predefined key case insensitively failed");
+
+ return true;
+}
+
+static bool test_predef_key_by_name_invalid(struct torture_context *tctx,
+ void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root;
+ WERROR error;
+
+ error = reg_get_predefined_key_by_name(rctx, "BLA", &root);
+ torture_assert_werr_equal(tctx, error, WERR_FILE_NOT_FOUND,
+ "getting predefined key failed");
+ return true;
+}
+
+/**
+ * Test creating a new subkey
+ */
+static bool test_create_subkey(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root, *newkey;
+ WERROR error;
+
+ error = reg_get_predefined_key(rctx, HKEY_CLASSES_ROOT, &root);
+ torture_assert_werr_ok(tctx, error,
+ "getting predefined key failed");
+
+ error = reg_key_add_name(rctx, root, "Bad Bentheim", NULL, NULL,
+ &newkey);
+ torture_assert_werr_ok(tctx, error, "Creating key return code");
+ torture_assert(tctx, newkey != NULL, "Creating new key");
+
+ return true;
+}
+
+/**
+ * Test creating a new nested subkey
+ */
+static bool test_create_nested_subkey(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root, *newkey;
+ WERROR error;
+
+ error = reg_get_predefined_key(rctx, HKEY_CLASSES_ROOT, &root);
+ torture_assert_werr_ok(tctx, error,
+ "getting predefined key failed");
+
+ error = reg_key_add_name(rctx, root, "Hamburg\\Hamburg", NULL, NULL,
+ &newkey);
+ torture_assert_werr_ok(tctx, error, "Creating key return code");
+ torture_assert(tctx, newkey != NULL, "Creating new key");
+
+ return true;
+}
+
+/**
+ * Test creating a new subkey
+ */
+static bool test_key_add_abs_top(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root;
+ WERROR error;
+
+ error = reg_key_add_abs(tctx, rctx, "HKEY_CLASSES_ROOT", 0, NULL,
+ &root);
+ torture_assert_werr_equal(tctx, error, WERR_ALREADY_EXISTS,
+ "create top level");
+
+ return true;
+}
+
+/**
+ * Test creating a new subkey
+ */
+static bool test_key_add_abs(struct torture_context *tctx, void *_data)
+{
+ WERROR error;
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root, *result1, *result2;
+
+ error = reg_key_add_abs(tctx, rctx, "HKEY_CLASSES_ROOT\\bloe", 0, NULL,
+ &result1);
+ torture_assert_werr_ok(tctx, error, "create lowest");
+
+ error = reg_key_add_abs(tctx, rctx, "HKEY_CLASSES_ROOT\\bloe\\bla", 0,
+ NULL, &result1);
+ torture_assert_werr_ok(tctx, error, "create nested");
+
+ error = reg_get_predefined_key(rctx, HKEY_CLASSES_ROOT, &root);
+ torture_assert_werr_ok(tctx, error,
+ "getting predefined key failed");
+
+ error = reg_open_key(tctx, root, "bloe", &result2);
+ torture_assert_werr_ok(tctx, error, "opening key");
+
+ error = reg_open_key(tctx, root, "bloe\\bla", &result2);
+ torture_assert_werr_ok(tctx, error, "opening key");
+
+ return true;
+}
+
+
+static bool test_del_key(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root, *newkey;
+ WERROR error;
+
+ error = reg_get_predefined_key(rctx, HKEY_CLASSES_ROOT, &root);
+ torture_assert_werr_ok(tctx, error,
+ "getting predefined key failed");
+
+ error = reg_key_add_name(rctx, root, "Polen", NULL, NULL, &newkey);
+
+ torture_assert_werr_ok(tctx, error, "Creating key return code");
+ torture_assert(tctx, newkey != NULL, "Creating new key");
+
+ error = reg_key_del(tctx, root, "Polen");
+ torture_assert_werr_ok(tctx, error, "Delete key");
+
+ error = reg_key_del(tctx, root, "Polen");
+ torture_assert_werr_equal(tctx, error, WERR_FILE_NOT_FOUND,
+ "Delete missing key");
+
+ return true;
+}
+
+/**
+ * Convenience function for opening the HKEY_CLASSES_ROOT hive and
+ * creating a single key for testing purposes.
+ */
+static bool create_test_key(struct torture_context *tctx,
+ struct registry_context *rctx,
+ const char *name,
+ struct registry_key **root,
+ struct registry_key **subkey)
+{
+ WERROR error;
+
+ error = reg_get_predefined_key(rctx, HKEY_CLASSES_ROOT, root);
+ torture_assert_werr_ok(tctx, error,
+ "getting predefined key failed");
+
+ error = reg_key_add_name(rctx, *root, name, NULL, NULL, subkey);
+ torture_assert_werr_ok(tctx, error, "Creating key return code");
+
+ return true;
+}
+
+
+static bool test_flush_key(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root, *subkey;
+ WERROR error;
+
+ if (!create_test_key(tctx, rctx, "Bremen", &root, &subkey))
+ return false;
+
+ error = reg_key_flush(subkey);
+ torture_assert_werr_ok(tctx, error, "flush key");
+
+ torture_assert_werr_equal(tctx, reg_key_flush(NULL),
+ WERR_INVALID_PARAMETER, "flush key");
+
+ return true;
+}
+
+static bool test_query_key(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root, *subkey;
+ WERROR error;
+ NTTIME last_changed_time;
+ uint32_t num_subkeys, num_values;
+ const char *classname;
+ const char *data = "temp";
+
+ if (!create_test_key(tctx, rctx, "Muenchen", &root, &subkey))
+ return false;
+
+ error = reg_key_get_info(tctx, subkey, &classname,
+ &num_subkeys, &num_values,
+ &last_changed_time, NULL, NULL, NULL);
+
+ torture_assert_werr_ok(tctx, error, "get info key");
+ torture_assert(tctx, classname == NULL, "classname");
+ torture_assert_int_equal(tctx, num_subkeys, 0, "num subkeys");
+ torture_assert_int_equal(tctx, num_values, 0, "num values");
+
+ error = reg_val_set(subkey, "", REG_SZ,
+ data_blob_string_const(data));
+ torture_assert_werr_ok(tctx, error, "set default value");
+
+ error = reg_key_get_info(tctx, subkey, &classname,
+ &num_subkeys, &num_values,
+ &last_changed_time, NULL, NULL, NULL);
+
+ torture_assert_werr_ok(tctx, error, "get info key");
+ torture_assert(tctx, classname == NULL, "classname");
+ torture_assert_int_equal(tctx, num_subkeys, 0, "num subkeys");
+ torture_assert_int_equal(tctx, num_values, 1, "num values");
+
+ return true;
+}
+
+static bool test_query_key_nums(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root, *subkey1, *subkey2;
+ WERROR error;
+ uint32_t num_subkeys, num_values;
+ char data[4];
+ SIVAL(data, 0, 42);
+
+ if (!create_test_key(tctx, rctx, "Berlin", &root, &subkey1))
+ return false;
+
+ error = reg_key_add_name(rctx, subkey1, "Bentheim", NULL, NULL,
+ &subkey2);
+ torture_assert_werr_ok(tctx, error, "Creating key return code");
+
+ error = reg_val_set(subkey1, "Answer", REG_DWORD,
+ data_blob_talloc(tctx, &data, sizeof(data)));
+ torture_assert_werr_ok(tctx, error, "set value");
+
+ error = reg_key_get_info(tctx, subkey1, NULL, &num_subkeys,
+ &num_values, NULL, NULL, NULL, NULL);
+
+ torture_assert_werr_ok(tctx, error, "get info key");
+ torture_assert_int_equal(tctx, num_subkeys, 1, "num subkeys");
+ torture_assert_int_equal(tctx, num_values, 1, "num values");
+
+ return true;
+}
+
+/**
+ * Test that the subkeys of a key can be enumerated, that
+ * the returned parameters for get_subkey_by_index are optional and
+ * that enumerating the parents of a non-top-level node works.
+ */
+static bool test_list_subkeys(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *subkey = NULL, *root;
+ WERROR error;
+ NTTIME last_mod_time;
+ const char *classname, *name;
+
+ if (!create_test_key(tctx, rctx, "Goettingen", &root, &subkey))
+ return false;
+
+ error = reg_key_get_subkey_by_index(tctx, root, 0, &name, &classname,
+ &last_mod_time);
+
+ torture_assert_werr_ok(tctx, error, "Enum keys return code");
+ torture_assert_str_equal(tctx, name, "Goettingen", "Enum keys data");
+
+
+ error = reg_key_get_subkey_by_index(tctx, root, 0, NULL, NULL, NULL);
+
+ torture_assert_werr_ok(tctx, error,
+ "Enum keys with NULL arguments return code");
+
+ error = reg_key_get_subkey_by_index(tctx, root, 1, NULL, NULL, NULL);
+
+ torture_assert_werr_equal(tctx, error, WERR_NO_MORE_ITEMS,
+ "Invalid error for no more items");
+
+ error = reg_key_get_subkey_by_index(tctx, subkey, 0, NULL, NULL, NULL);
+
+ torture_assert_werr_equal(tctx, error, WERR_NO_MORE_ITEMS,
+ "Invalid error for no more items");
+
+ return true;
+}
+
+/**
+ * Test setting a value
+ */
+static bool test_set_value(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *subkey = NULL, *root;
+ WERROR error;
+ char data[4];
+
+ SIVAL(data, 0, 42);
+
+ if (!create_test_key(tctx, rctx, "Dusseldorf", &root, &subkey))
+ return false;
+
+ error = reg_val_set(subkey, "Answer", REG_DWORD,
+ data_blob_talloc(tctx, data, sizeof(data)));
+ torture_assert_werr_ok (tctx, error, "setting value");
+
+ return true;
+}
+
+/**
+ * Test getting/setting security descriptors
+ */
+static bool test_security(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *subkey = NULL, *root;
+ WERROR error;
+ struct security_descriptor *osd, *nsd;
+
+ if (!create_test_key(tctx, rctx, "Düsseldorf", &root, &subkey))
+ return false;
+
+ osd = security_descriptor_dacl_create(tctx,
+ 0,
+ NULL, NULL,
+ SID_NT_AUTHENTICATED_USERS,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_GENERIC_ALL,
+ SEC_ACE_FLAG_OBJECT_INHERIT,
+ NULL);
+
+ error = reg_set_sec_desc(subkey, osd);
+ torture_assert_werr_ok(tctx, error, "setting security descriptor");
+
+ error = reg_get_sec_desc(tctx, subkey, &nsd);
+ torture_assert_werr_ok (tctx, error, "getting security descriptor");
+
+ torture_assert(tctx, security_descriptor_equal(osd, nsd),
+ "security descriptor changed!");
+
+ return true;
+}
+
+/**
+ * Test getting a value
+ */
+static bool test_get_value(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *subkey = NULL, *root;
+ WERROR error;
+ DATA_BLOB data;
+ char value[4];
+ uint32_t type;
+ const char *data_val = "temp";
+
+ SIVAL(value, 0, 42);
+
+ if (!create_test_key(tctx, rctx, "Duisburg", &root, &subkey))
+ return false;
+
+ error = reg_key_get_value_by_name(tctx, subkey, __FUNCTION__, &type,
+ &data);
+ torture_assert_werr_equal(tctx, error, WERR_FILE_NOT_FOUND,
+ "getting missing value");
+
+ error = reg_val_set(subkey, __FUNCTION__, REG_DWORD,
+ data_blob_talloc(tctx, value, sizeof(value)));
+ torture_assert_werr_ok(tctx, error, "setting value");
+
+ error = reg_key_get_value_by_name(tctx, subkey, __FUNCTION__, &type,
+ &data);
+ torture_assert_werr_ok(tctx, error, "getting value");
+
+ torture_assert_int_equal(tctx, sizeof(value), data.length, "value length ok");
+ torture_assert_mem_equal(tctx, data.data, value, sizeof(value),
+ "value content ok");
+ torture_assert_int_equal(tctx, REG_DWORD, type, "value type");
+
+ error = reg_val_set(subkey, "", REG_SZ,
+ data_blob_talloc(tctx, data_val,
+ strlen(data_val)));
+ torture_assert_werr_ok(tctx, error, "set default value");
+
+ error = reg_key_get_value_by_name(tctx, subkey, "", &type,
+ &data);
+ torture_assert_werr_ok(tctx, error, "getting default value");
+ torture_assert_int_equal(tctx, REG_SZ, type, "value type ok");
+ torture_assert_int_equal(tctx, strlen(data_val), data.length, "value length ok");
+ torture_assert_str_equal(tctx, data_val, (char *)data.data, "value ok");
+
+ return true;
+}
+
+/**
+ * Test unsetting a value
+ */
+static bool test_del_value(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx =(struct registry_context *)_data;
+ struct registry_key *subkey = NULL, *root;
+ WERROR error;
+ DATA_BLOB data;
+ uint32_t type;
+ char value[4];
+ const char *data_val = "temp";
+
+ SIVAL(value, 0, 42);
+
+ if (!create_test_key(tctx, rctx, "Warschau", &root, &subkey))
+ return false;
+
+ error = reg_key_get_value_by_name(tctx, subkey, __FUNCTION__, &type,
+ &data);
+ torture_assert_werr_equal(tctx, error, WERR_FILE_NOT_FOUND,
+ "getting missing value");
+
+ error = reg_val_set(subkey, __FUNCTION__, REG_DWORD,
+ data_blob_talloc(tctx, value, sizeof(value)));
+ torture_assert_werr_ok (tctx, error, "setting value");
+
+ error = reg_del_value(tctx, subkey, __FUNCTION__);
+ torture_assert_werr_ok (tctx, error, "unsetting value");
+
+ error = reg_key_get_value_by_name(tctx, subkey, __FUNCTION__,
+ &type, &data);
+ torture_assert_werr_equal(tctx, error, WERR_FILE_NOT_FOUND,
+ "getting missing value");
+
+ error = reg_del_value(tctx, subkey, "");
+ torture_assert_werr_equal(tctx, error, WERR_FILE_NOT_FOUND,
+ "unsetting missing default value");
+
+ error = reg_val_set(subkey, "", REG_SZ,
+ data_blob_talloc(tctx, data_val,
+ strlen(data_val)));
+ torture_assert_werr_ok(tctx, error, "set default value");
+
+ error = reg_del_value(tctx, subkey, "");
+ torture_assert_werr_ok (tctx, error, "unsetting default value");
+
+ return true;
+}
+
+/**
+ * Test listing values
+ */
+static bool test_list_values(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *subkey = NULL, *root;
+ WERROR error;
+ DATA_BLOB data;
+ uint32_t type;
+ const char *name;
+ char value[4];
+ const char *data_val = "temp";
+
+ SIVAL(value, 0, 42);
+
+ if (!create_test_key(tctx, rctx, "Bonn", &root, &subkey))
+ return false;
+
+ error = reg_val_set(subkey, "bar", REG_DWORD,
+ data_blob_talloc(tctx, value, sizeof(value)));
+ torture_assert_werr_ok (tctx, error, "setting value");
+
+ error = reg_key_get_value_by_index(tctx, subkey, 0, &name,
+ &type, &data);
+ torture_assert_werr_ok(tctx, error, "getting value");
+
+ torture_assert_str_equal(tctx, name, "bar", "value name");
+ torture_assert_int_equal(tctx, sizeof(value), data.length, "value length");
+ torture_assert_mem_equal(tctx, data.data, value, sizeof(value),
+ "value content");
+ torture_assert_int_equal(tctx, REG_DWORD, type, "value type");
+
+ error = reg_key_get_value_by_index(tctx, subkey, 1, &name,
+ &type, &data);
+ torture_assert_werr_equal(tctx, error, WERR_NO_MORE_ITEMS,
+ "getting missing value");
+
+ error = reg_val_set(subkey, "", REG_SZ,
+ data_blob_talloc(tctx, data_val, strlen(data_val)));
+ torture_assert_werr_ok(tctx, error, "set default value");
+
+ error = reg_key_get_value_by_index(tctx, subkey, 0, &name,
+ &type, &data);
+ torture_assert_werr_ok(tctx, error, "getting default value");
+ torture_assert_int_equal(tctx, REG_SZ, type, "value type ok");
+ torture_assert_int_equal(tctx, strlen(data_val), data.length, "value length ok");
+ torture_assert_str_equal(tctx, data_val, (char *)data.data, "value ok");
+
+ return true;
+}
+
+static bool setup_local_registry(struct torture_context *tctx, void **data)
+{
+ struct registry_context *rctx;
+ WERROR error;
+ char *tempdir;
+ NTSTATUS status;
+ struct hive_key *hive_key;
+ const char *filename;
+
+ error = reg_open_local(tctx, &rctx);
+ torture_assert_werr_ok(tctx, error, "Opening local registry failed");
+
+ status = torture_temp_dir(tctx, "registry-local", &tempdir);
+ torture_assert_ntstatus_ok(tctx, status, "Creating temp dir failed");
+
+ filename = talloc_asprintf(tctx, "%s/classes_root.ldb", tempdir);
+ error = reg_open_ldb_file(tctx, filename, NULL, NULL, tctx->ev, tctx->lp_ctx, &hive_key);
+ torture_assert_werr_ok(tctx, error, "Opening classes_root file failed");
+
+ error = reg_mount_hive(rctx, hive_key, HKEY_CLASSES_ROOT, NULL);
+ torture_assert_werr_ok(tctx, error, "Mounting hive failed");
+
+ *data = rctx;
+
+ return true;
+}
+
+static void tcase_add_tests(struct torture_tcase *tcase)
+{
+ torture_tcase_add_simple_test(tcase, "list_subkeys",
+ test_list_subkeys);
+ torture_tcase_add_simple_test(tcase, "get_predefined_key",
+ test_get_predefined);
+ torture_tcase_add_simple_test(tcase, "get_predefined_key",
+ test_get_predefined_unknown);
+ torture_tcase_add_simple_test(tcase, "create_key",
+ test_create_subkey);
+ torture_tcase_add_simple_test(tcase, "create_key",
+ test_create_nested_subkey);
+ torture_tcase_add_simple_test(tcase, "key_add_abs",
+ test_key_add_abs);
+ torture_tcase_add_simple_test(tcase, "key_add_abs_top",
+ test_key_add_abs_top);
+ torture_tcase_add_simple_test(tcase, "set_value",
+ test_set_value);
+ torture_tcase_add_simple_test(tcase, "get_value",
+ test_get_value);
+ torture_tcase_add_simple_test(tcase, "list_values",
+ test_list_values);
+ torture_tcase_add_simple_test(tcase, "del_key",
+ test_del_key);
+ torture_tcase_add_simple_test(tcase, "del_value",
+ test_del_value);
+ torture_tcase_add_simple_test(tcase, "flush_key",
+ test_flush_key);
+ torture_tcase_add_simple_test(tcase, "query_key",
+ test_query_key);
+ torture_tcase_add_simple_test(tcase, "query_key_nums",
+ test_query_key_nums);
+ torture_tcase_add_simple_test(tcase, "test_predef_key_by_name",
+ test_predef_key_by_name);
+ torture_tcase_add_simple_test(tcase, "security",
+ test_security);
+ torture_tcase_add_simple_test(tcase,"test_predef_key_by_name_invalid",
+ test_predef_key_by_name_invalid);
+}
+
+struct torture_suite *torture_registry_registry(TALLOC_CTX *mem_ctx)
+{
+ struct torture_tcase *tcase;
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "registry");
+
+ tcase = torture_suite_add_tcase(suite, "local");
+ torture_tcase_set_fixture(tcase, setup_local_registry, NULL);
+ tcase_add_tests(tcase);
+
+ return suite;
+}
diff --git a/source4/lib/registry/tools/common.c b/source4/lib/registry/tools/common.c
new file mode 100644
index 0000000..a2fda8d
--- /dev/null
+++ b/source4/lib/registry/tools/common.c
@@ -0,0 +1,88 @@
+/*
+ Unix SMB/CIFS implementation.
+ Popt routines specifically for registry
+
+ Copyright (C) Jelmer Vernooij 2007
+
+ 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 "auth/credentials/credentials.h"
+#include "lib/registry/registry.h"
+#include "lib/registry/tools/common.h"
+
+struct registry_context *reg_common_open_remote(const char *remote,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct cli_credentials *creds)
+{
+ struct registry_context *h = NULL;
+ WERROR error;
+
+ error = reg_open_remote(NULL, &h, NULL, creds, lp_ctx, remote, ev_ctx);
+
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Unable to open remote registry at %s:%s \n",
+ remote, win_errstr(error));
+ return NULL;
+ }
+
+ return h;
+}
+
+struct registry_key *reg_common_open_file(const char *path,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct cli_credentials *creds)
+{
+ struct hive_key *hive_root;
+ struct registry_context *h = NULL;
+ WERROR error;
+
+ error = reg_open_hive(ev_ctx, path, NULL, creds, ev_ctx, lp_ctx, &hive_root);
+
+ if(!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Unable to open '%s': %s \n",
+ path, win_errstr(error));
+ return NULL;
+ }
+
+ error = reg_open_local(NULL, &h);
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Unable to initialize local registry: %s\n",
+ win_errstr(error));
+ return NULL;
+ }
+
+ return reg_import_hive_key(h, hive_root, -1, NULL);
+}
+
+struct registry_context *reg_common_open_local(struct cli_credentials *creds,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx)
+{
+ WERROR error;
+ struct registry_context *h = NULL;
+
+ error = reg_open_samba(NULL, &h, ev_ctx, lp_ctx, NULL, creds);
+
+ if(!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Unable to open local registry:%s \n",
+ win_errstr(error));
+ return NULL;
+ }
+
+ return h;
+}
diff --git a/source4/lib/registry/tools/regdiff.c b/source4/lib/registry/tools/regdiff.c
new file mode 100644
index 0000000..aab7b68
--- /dev/null
+++ b/source4/lib/registry/tools/regdiff.c
@@ -0,0 +1,183 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple registry frontend
+
+ Copyright (C) Jelmer Vernooij 2004-2007
+ Copyright (C) Wilco Baan Hofman 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 "lib/registry/registry.h"
+#include "lib/events/events.h"
+#include "lib/cmdline/cmdline.h"
+#include "lib/registry/tools/common.h"
+#include "param/param.h"
+
+enum reg_backend { REG_UNKNOWN, REG_LOCAL, REG_REMOTE, REG_NULL };
+
+static struct registry_context *open_backend(TALLOC_CTX *mem_ctx,
+ poptContext pc,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ enum reg_backend backend,
+ const char *remote_host)
+{
+ WERROR error;
+ struct registry_context *ctx;
+ struct cli_credentials *creds = samba_cmdline_get_creds();
+
+ switch (backend) {
+ case REG_UNKNOWN:
+ poptPrintUsage(pc, stderr, 0);
+ return NULL;
+ case REG_LOCAL:
+ error = reg_open_samba(mem_ctx, &ctx, ev_ctx, lp_ctx, NULL,
+ creds);
+ break;
+ case REG_REMOTE:
+ error = reg_open_remote(mem_ctx, &ctx, NULL,
+ creds, lp_ctx,
+ remote_host, ev_ctx);
+ break;
+ case REG_NULL:
+ error = reg_open_local(mem_ctx, &ctx);
+ break;
+ }
+
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Error: %s\n", win_errstr(error));
+ return NULL;
+ }
+
+ return ctx;
+}
+
+int main(int argc, char **argv)
+{
+ const char **argv_const = discard_const_p(const char *, argv);
+ int opt;
+ poptContext pc;
+ char *outputfile = NULL;
+ enum reg_backend backend1 = REG_UNKNOWN, backend2 = REG_UNKNOWN;
+ const char *remote1 = NULL, *remote2 = NULL;
+ struct registry_context *h1 = NULL, *h2 = NULL;
+ WERROR error;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"output", 'o', POPT_ARG_STRING, &outputfile, 0, "output file to use", NULL },
+ {"null", 'n', POPT_ARG_NONE, NULL, 'n', "Diff from NULL", NULL },
+ {"remote", 'R', POPT_ARG_STRING, NULL, 'R', "Connect to remote server" , NULL },
+ {"local", 'L', POPT_ARG_NONE, NULL, 'L', "Open local registry", NULL },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CREDENTIALS
+ POPT_COMMON_VERSION
+ {0}
+ };
+ TALLOC_CTX *ctx;
+ void *callback_data;
+ struct tevent_context *ev_ctx;
+ struct reg_diff_callbacks *callbacks;
+ struct loadparm_context *lp_ctx = NULL;
+ bool ok;
+
+ ctx = talloc_init("regdiff");
+ if (ctx == NULL) {
+ exit(ENOMEM);
+ }
+
+ ok = samba_cmdline_init(ctx,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(ctx);
+ exit(1);
+ }
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv_const,
+ long_options,
+ 0);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ TALLOC_FREE(ctx);
+ exit(1);
+ }
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ error = WERR_OK;
+ switch(opt) {
+ case 'L':
+ if (backend1 == REG_UNKNOWN)
+ backend1 = REG_LOCAL;
+ else if (backend2 == REG_UNKNOWN)
+ backend2 = REG_LOCAL;
+ break;
+ case 'n':
+ if (backend1 == REG_UNKNOWN)
+ backend1 = REG_NULL;
+ else if (backend2 == REG_UNKNOWN)
+ backend2 = REG_NULL;
+ break;
+ case 'R':
+ if (backend1 == REG_UNKNOWN) {
+ backend1 = REG_REMOTE;
+ remote1 = poptGetOptArg(pc);
+ } else if (backend2 == REG_UNKNOWN) {
+ backend2 = REG_REMOTE;
+ remote2 = poptGetOptArg(pc);
+ }
+ break;
+ case POPT_ERROR_BADOPT:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+
+ }
+
+ ev_ctx = s4_event_context_init(ctx);
+ lp_ctx = samba_cmdline_get_lp_ctx();
+
+ h1 = open_backend(ctx, pc, ev_ctx, lp_ctx, backend1, remote1);
+ if (h1 == NULL)
+ return 1;
+
+ h2 = open_backend(ctx, pc, ev_ctx, lp_ctx, backend2, remote2);
+ if (h2 == NULL)
+ return 1;
+
+ poptFreeContext(pc);
+ samba_cmdline_burn(argc, argv);
+
+ error = reg_dotreg_diff_save(ctx, outputfile, &callbacks, &callback_data);
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Problem saving registry diff to '%s': %s\n",
+ outputfile, win_errstr(error));
+ return -1;
+ }
+
+ error = reg_generate_diff(h1, h2, callbacks, callback_data);
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Unable to generate diff between keys: %s\n",
+ win_errstr(error));
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/source4/lib/registry/tools/regpatch.c b/source4/lib/registry/tools/regpatch.c
new file mode 100644
index 0000000..eafaff6
--- /dev/null
+++ b/source4/lib/registry/tools/regpatch.c
@@ -0,0 +1,119 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple registry frontend
+
+ Copyright (C) 2004-2007 Jelmer Vernooij, jelmer@samba.org
+
+ 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 "lib/events/events.h"
+#include "lib/registry/registry.h"
+#include "lib/cmdline/cmdline.h"
+#include "lib/registry/tools/common.h"
+#include "param/param.h"
+#include "events/events.h"
+
+int main(int argc, char **argv)
+{
+ const char **argv_const = discard_const_p(const char *, argv);
+ bool ok;
+ TALLOC_CTX *mem_ctx = NULL;
+ int opt;
+ poptContext pc;
+ const char *patch;
+ struct registry_context *h;
+ const char *file = NULL;
+ const char *remote = NULL;
+ struct tevent_context *ev_ctx;
+ struct loadparm_context *lp_ctx = NULL;
+ struct cli_credentials *creds = NULL;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"remote", 'R', POPT_ARG_STRING, &remote, 0, "connect to specified remote server", NULL},
+ {"file", 'F', POPT_ARG_STRING, &file, 0, "file path", NULL },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CREDENTIALS
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ mem_ctx = talloc_init("regtree.c/main");
+ if (mem_ctx == NULL) {
+ exit(ENOMEM);
+ }
+
+ ok = samba_cmdline_init(mem_ctx,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(mem_ctx);
+ exit(1);
+ }
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv_const,
+ long_options,
+ 0);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ TALLOC_FREE(mem_ctx);
+ exit(1);
+ }
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case POPT_ERROR_BADOPT:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ ev_ctx = s4_event_context_init(NULL);
+ lp_ctx = samba_cmdline_get_lp_ctx();
+ creds = samba_cmdline_get_creds();
+
+ if (remote) {
+ h = reg_common_open_remote (remote, ev_ctx, lp_ctx, creds);
+ } else {
+ h = reg_common_open_local (creds, ev_ctx, lp_ctx);
+ }
+
+ if (h == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return 1;
+ }
+
+ patch = talloc_strdup(mem_ctx, poptGetArg(pc));
+ if (patch == NULL) {
+ poptPrintUsage(pc, stderr, 0);
+ TALLOC_FREE(mem_ctx);
+ return 1;
+ }
+
+ poptFreeContext(pc);
+ samba_cmdline_burn(argc, argv);
+
+ reg_diff_apply(h, patch);
+
+ TALLOC_FREE(mem_ctx);
+
+ return 0;
+}
diff --git a/source4/lib/registry/tools/regshell.c b/source4/lib/registry/tools/regshell.c
new file mode 100644
index 0000000..dc7bf54
--- /dev/null
+++ b/source4/lib/registry/tools/regshell.c
@@ -0,0 +1,708 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple registry frontend
+
+ Copyright (C) Jelmer Vernooij 2004-2007
+ Copyright (C) Wilco Baan Hofman 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 "lib/registry/registry.h"
+#include "lib/cmdline/cmdline.h"
+#include "lib/events/events.h"
+#include "system/time.h"
+#include "../libcli/smbreadline/smbreadline.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "lib/registry/tools/common.h"
+#include "param/param.h"
+
+struct regshell_context {
+ struct registry_context *registry;
+ char *path;
+ char *predef;
+ struct registry_key *current;
+ struct registry_key *root;
+};
+
+static WERROR get_full_path(struct regshell_context *ctx, const char *path, char **ret_path)
+{
+ const char *dir;
+ char *tmp;
+ char *new_path;
+
+ if (path[0] == '\\') {
+ new_path = talloc_strdup(ctx, "");
+ } else {
+ new_path = talloc_strdup(ctx, ctx->path);
+ }
+
+ dir = strtok(discard_const_p(char, path), "\\");
+ if (dir == NULL) {
+ *ret_path = new_path;
+ return WERR_OK;
+ }
+ do {
+ if (strcmp(dir, "..") == 0) {
+ if (strchr(new_path, '\\')) {
+ new_path[strrchr(new_path, '\\') - new_path] = '\0';
+ } else {
+ tmp = new_path;
+ new_path = talloc_strdup(ctx, "");
+ talloc_free(tmp);
+ }
+ continue;
+ }
+ if (strcmp(dir, ".") == 0) {
+ continue;
+ }
+
+ tmp = new_path;
+ /* No prepending a backslash */
+ if (strcmp(new_path, "") == 0) {
+ new_path = talloc_strdup(ctx, dir);
+ } else {
+ new_path = talloc_asprintf(ctx, "%s\\%s", new_path, dir);
+ }
+ talloc_free(tmp);
+
+ } while ((dir = strtok(NULL, "\\")));
+
+ *ret_path = new_path;
+ return WERR_OK;
+}
+
+/* *
+ * ck/cd - change key
+ * ls - list values/keys
+ * rmval/rm - remove value
+ * rmkey/rmdir - remove key
+ * mkkey/mkdir - make key
+ * ch - change hive
+ * info - show key info
+ * save - save hive
+ * print - print value
+ * help
+ * exit
+ */
+
+static WERROR cmd_info(struct regshell_context *ctx, int argc, const char **argv)
+{
+ struct security_descriptor *sec_desc = NULL;
+ time_t last_mod;
+ WERROR error;
+ const char *classname = NULL;
+ NTTIME last_change;
+ uint32_t max_subkeynamelen;
+ uint32_t max_valnamelen;
+ uint32_t max_valbufsize;
+ uint32_t num_subkeys;
+ uint32_t num_values;
+
+ error = reg_key_get_info(ctx, ctx->current, &classname, &num_subkeys, &num_values,
+ &last_change, &max_subkeynamelen, &max_valnamelen, &max_valbufsize);
+ if (!W_ERROR_IS_OK(error)) {
+ printf("Error getting key info: %s\n", win_errstr(error));
+ return error;
+ }
+
+
+ printf("Name: %s\n", strchr(ctx->path, '\\')?strrchr(ctx->path, '\\')+1:
+ ctx->path);
+ printf("Full path: %s\n", ctx->path);
+ if (classname != NULL)
+ printf("Key Class: %s\n", classname);
+ last_mod = nt_time_to_unix(last_change);
+ printf("Time Last Modified: %s", ctime(&last_mod));
+ printf("Number of subkeys: %d\n", num_subkeys);
+ printf("Number of values: %d\n", num_values);
+
+ if (max_valnamelen > 0)
+ printf("Maximum value name length: %d\n", max_valnamelen);
+
+ if (max_valbufsize > 0)
+ printf("Maximum value data length: %d\n", max_valbufsize);
+
+ if (max_subkeynamelen > 0)
+ printf("Maximum sub key name length: %d\n", max_subkeynamelen);
+
+ error = reg_get_sec_desc(ctx, ctx->current, &sec_desc);
+ if (!W_ERROR_IS_OK(error)) {
+ printf("Error getting security descriptor: %s\n", win_errstr(error));
+ return WERR_OK;
+ }
+ NDR_PRINT_DEBUG(security_descriptor, sec_desc);
+ talloc_free(sec_desc);
+
+ return WERR_OK;
+}
+
+static WERROR cmd_predef(struct regshell_context *ctx, int argc, const char **argv)
+{
+ struct registry_key *ret = NULL;
+ if (argc < 2) {
+ fprintf(stderr, "Usage: predef predefined-key-name\n");
+ } else if (!ctx) {
+ fprintf(stderr, "No full registry loaded, no predefined keys defined\n");
+ } else {
+ WERROR error = reg_get_predefined_key_by_name(ctx->registry,
+ argv[1], &ret);
+
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Error opening predefined key %s: %s\n",
+ argv[1], win_errstr(error));
+ return error;
+ }
+
+ ctx->predef = strupper_talloc(ctx, argv[1]);
+ ctx->current = ret;
+ ctx->root = ret;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR cmd_pwd(struct regshell_context *ctx,
+ int argc, const char **argv)
+{
+ if (ctx->predef) {
+ printf("%s\\", ctx->predef);
+ }
+ printf("%s\n", ctx->path);
+ return WERR_OK;
+}
+
+static WERROR cmd_set(struct regshell_context *ctx, int argc, const char **argv)
+{
+ struct registry_value val;
+ WERROR error;
+
+ if (argc < 4) {
+ fprintf(stderr, "Usage: set value-name type value\n");
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (!reg_string_to_val(ctx, argv[2], argv[3], &val.data_type, &val.data)) {
+ fprintf(stderr, "Unable to interpret data\n");
+ return WERR_INVALID_PARAMETER;
+ }
+
+ error = reg_val_set(ctx->current, argv[1], val.data_type, val.data);
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Error setting value: %s\n", win_errstr(error));
+ return error;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR cmd_ck(struct regshell_context *ctx, int argc, const char **argv)
+{
+ struct registry_key *nkey = NULL;
+ char *full_path;
+ WERROR error;
+
+ if(argc == 2) {
+ if (!W_ERROR_IS_OK(get_full_path(ctx, argv[1], &full_path))) {
+ fprintf(stderr, "Unable to parse the supplied path\n");
+ return WERR_INVALID_PARAMETER;
+ }
+ error = reg_open_key(ctx->registry, ctx->root, full_path,
+ &nkey);
+ if(!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error opening specified key: %s\n",
+ win_errstr(error)));
+ return error;
+ }
+
+ talloc_free(ctx->path);
+ ctx->path = full_path;
+
+ ctx->current = nkey;
+ }
+ printf("New path is: %s\\%s\n", ctx->predef?ctx->predef:"", ctx->path);
+
+ return WERR_OK;
+}
+
+static WERROR cmd_print(struct regshell_context *ctx, int argc, const char **argv)
+{
+ uint32_t value_type;
+ DATA_BLOB value_data;
+ WERROR error;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: print <valuename>\n");
+ return WERR_INVALID_PARAMETER;
+ }
+
+ error = reg_key_get_value_by_name(ctx, ctx->current, argv[1],
+ &value_type, &value_data);
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "No such value '%s'\n", argv[1]);
+ return error;
+ }
+
+ printf("%s\n%s\n", str_regtype(value_type),
+ reg_val_data_string(ctx, value_type, value_data));
+
+ return WERR_OK;
+}
+
+static WERROR cmd_ls(struct regshell_context *ctx, int argc, const char **argv)
+{
+ unsigned int i;
+ WERROR error;
+ uint32_t valuetype;
+ DATA_BLOB valuedata;
+ const char *name = NULL;
+
+ for (i = 0; W_ERROR_IS_OK(error = reg_key_get_subkey_by_index(ctx,
+ ctx->current,
+ i,
+ &name,
+ NULL,
+ NULL)); i++) {
+ printf("K %s\n", name);
+ }
+
+ if (!W_ERROR_EQUAL(error, WERR_NO_MORE_ITEMS)) {
+ fprintf(stderr, "Error occurred while browsing through keys: %s\n",
+ win_errstr(error));
+ return error;
+ }
+
+ for (i = 0; W_ERROR_IS_OK(error = reg_key_get_value_by_index(ctx,
+ ctx->current, i, &name, &valuetype, &valuedata)); i++)
+ printf("V \"%s\" %s %s\n", name, str_regtype(valuetype),
+ reg_val_data_string(ctx, valuetype, valuedata));
+
+ return WERR_OK;
+}
+static WERROR cmd_mkkey(struct regshell_context *ctx, int argc, const char **argv)
+{
+ struct registry_key *tmp;
+ WERROR error;
+
+ if(argc < 2) {
+ fprintf(stderr, "Usage: mkkey <keyname>\n");
+ return WERR_INVALID_PARAMETER;
+ }
+
+ error = reg_key_add_name(ctx, ctx->current, argv[1], 0, NULL, &tmp);
+
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Error adding new subkey '%s': %s\n", argv[1],
+ win_errstr(error));
+ return error;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR cmd_rmkey(struct regshell_context *ctx,
+ int argc, const char **argv)
+{
+ WERROR error;
+
+ if(argc < 2) {
+ fprintf(stderr, "Usage: rmkey <name>\n");
+ return WERR_INVALID_PARAMETER;
+ }
+
+ error = reg_key_del(ctx, ctx->current, argv[1]);
+ if(!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Error deleting '%s'\n", argv[1]);
+ return error;
+ } else {
+ fprintf(stderr, "Successfully deleted '%s'\n", argv[1]);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR cmd_rmval(struct regshell_context *ctx, int argc, const char **argv)
+{
+ WERROR error;
+
+ if(argc < 2) {
+ fprintf(stderr, "Usage: rmval <valuename>\n");
+ return WERR_INVALID_PARAMETER;
+ }
+
+ error = reg_del_value(ctx, ctx->current, argv[1]);
+ if(!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Error deleting value '%s'\n", argv[1]);
+ return error;
+ } else {
+ fprintf(stderr, "Successfully deleted value '%s'\n", argv[1]);
+ }
+
+ return WERR_OK;
+}
+
+_NORETURN_ static WERROR cmd_exit(struct regshell_context *ctx,
+ int argc, const char **argv)
+{
+ exit(0);
+}
+
+static WERROR cmd_help(struct regshell_context *ctx, int, const char **);
+
+static struct {
+ const char *name;
+ const char *alias;
+ const char *help;
+ WERROR (*handle)(struct regshell_context *ctx, int argc, const char **argv);
+} regshell_cmds[] = {
+ {"ck", "cd", "Change current key", cmd_ck },
+ {"info", "i", "Show detailed information of a key", cmd_info },
+ {"list", "ls", "List values/keys in current key", cmd_ls },
+ {"print", "p", "Print value", cmd_print },
+ {"mkkey", "mkdir", "Make new key", cmd_mkkey },
+ {"rmval", "rm", "Remove value", cmd_rmval },
+ {"rmkey", "rmdir", "Remove key", cmd_rmkey },
+ {"pwd", "pwk", "Printing current key", cmd_pwd },
+ {"set", "update", "Update value", cmd_set },
+ {"help", "?", "Help", cmd_help },
+ {"exit", "quit", "Exit", cmd_exit },
+ {"predef", "predefined", "Go to predefined key", cmd_predef },
+ {0}
+};
+
+static WERROR cmd_help(struct regshell_context *ctx,
+ int argc, const char **argv)
+{
+ unsigned int i;
+ printf("Available commands:\n");
+ for(i = 0; regshell_cmds[i].name; i++) {
+ printf("%s - %s\n", regshell_cmds[i].name,
+ regshell_cmds[i].help);
+ }
+ return WERR_OK;
+}
+
+static WERROR process_cmd(struct regshell_context *ctx,
+ char *line)
+{
+ int argc;
+ const char **argv = NULL;
+ int ret, i;
+
+ if ((ret = poptParseArgvString(line, &argc, &argv)) != 0) {
+ fprintf(stderr, "regshell: %s\n", poptStrerror(ret));
+ return WERR_INVALID_PARAMETER;
+ }
+
+ for(i = 0; regshell_cmds[i].name; i++) {
+ if(!strcmp(regshell_cmds[i].name, argv[0]) ||
+ (regshell_cmds[i].alias && !strcmp(regshell_cmds[i].alias, argv[0]))) {
+ return regshell_cmds[i].handle(ctx, argc, argv);
+ }
+ }
+
+ fprintf(stderr, "No such command '%s'\n", argv[0]);
+
+ return WERR_INVALID_PARAMETER;
+}
+
+#define MAX_COMPLETIONS 100
+
+static struct registry_key *current_key = NULL;
+
+static char **reg_complete_command(const char *text, int start, int end)
+{
+ /* Complete command */
+ char **matches;
+ size_t len, samelen=0;
+ size_t i, count = 1;
+
+ matches = malloc_array_p(char *, MAX_COMPLETIONS);
+ if (!matches) return NULL;
+ matches[0] = NULL;
+
+ len = strlen(text);
+ for (i=0;regshell_cmds[i].handle && count < MAX_COMPLETIONS-1;i++) {
+ if (strncmp(text, regshell_cmds[i].name, len) == 0) {
+ matches[count] = strdup(regshell_cmds[i].name);
+ if (!matches[count])
+ goto cleanup;
+ if (count == 1)
+ samelen = strlen(matches[count]);
+ else
+ while (strncmp(matches[count], matches[count-1], samelen) != 0)
+ samelen--;
+ count++;
+ }
+ }
+
+ switch (count) {
+ case 0: /* should never happen */
+ case 1:
+ goto cleanup;
+ case 2:
+ matches[0] = strdup(matches[1]);
+ break;
+ default:
+ matches[0] = strndup(matches[1], samelen);
+ }
+ matches[count] = NULL;
+ return matches;
+
+cleanup:
+ for (i = 0; i < count; i++) {
+ free(matches[i]);
+ }
+ free(matches);
+ return NULL;
+}
+
+static char **reg_complete_key(const char *text, int start, int end)
+{
+ struct registry_key *base;
+ const char *subkeyname;
+ unsigned int i, j = 1;
+ size_t len, samelen = 0;
+ char **matches;
+ const char *base_n = "";
+ TALLOC_CTX *mem_ctx;
+ WERROR status;
+ int ret;
+
+ matches = malloc_array_p(char *, MAX_COMPLETIONS);
+ if (!matches) return NULL;
+ matches[0] = NULL;
+ mem_ctx = talloc_init("completion");
+
+ base = current_key;
+
+ len = strlen(text);
+ for(i = 0; j < MAX_COMPLETIONS-1; i++) {
+ status = reg_key_get_subkey_by_index(mem_ctx, base, i,
+ &subkeyname, NULL, NULL);
+ if(W_ERROR_IS_OK(status)) {
+ if(!strncmp(text, subkeyname, len)) {
+ matches[j] = strdup(subkeyname);
+ j++;
+
+ if (j == 1)
+ samelen = strlen(matches[j]);
+ else
+ while (strncmp(matches[j], matches[j-1], samelen) != 0)
+ samelen--;
+ }
+ } else if(W_ERROR_EQUAL(status, WERR_NO_MORE_ITEMS)) {
+ break;
+ } else {
+ int n;
+
+ printf("Error creating completion list: %s\n",
+ win_errstr(status));
+
+ for (n = j; n >= 0; n--) {
+ SAFE_FREE(matches[n]);
+ }
+ SAFE_FREE(matches);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+ }
+
+ if (j == 1) { /* No matches at all */
+ SAFE_FREE(matches);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ if (j == 2) { /* Exact match */
+ ret = asprintf(&matches[0], "%s%s", base_n, matches[1]);
+ } else {
+ ret = asprintf(&matches[0], "%s%s", base_n,
+ talloc_strndup(mem_ctx, matches[1], samelen));
+ }
+ talloc_free(mem_ctx);
+ if (ret == -1) {
+ SAFE_FREE(matches);
+ return NULL;
+ }
+
+ matches[j] = NULL;
+ return matches;
+}
+
+static char **reg_completion(const char *text, int start, int end)
+{
+ smb_readline_ca_char(' ');
+
+ if (start == 0) {
+ return reg_complete_command(text, start, end);
+ } else {
+ return reg_complete_key(text, start, end);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ const char **argv_const = discard_const_p(const char *, argv);
+ int opt;
+ const char *file = NULL;
+ poptContext pc;
+ const char *remote = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+ struct loadparm_context *lp_ctx = NULL;
+ struct cli_credentials *creds = NULL;
+ struct regshell_context *ctx;
+ struct tevent_context *ev_ctx;
+ bool ret = true;
+ bool ok;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"file", 'F', POPT_ARG_STRING, &file, 0, "open hive file", NULL },
+ {"remote", 'R', POPT_ARG_STRING, &remote, 0, "connect to specified remote server", NULL},
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CREDENTIALS
+ POPT_LEGACY_S4
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ mem_ctx = talloc_init("regshell.c/main");
+ if (mem_ctx == NULL) {
+ exit(ENOMEM);
+ }
+
+ ok = samba_cmdline_init(mem_ctx,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(mem_ctx);
+ exit(1);
+ }
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv_const,
+ long_options,
+ 0);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ TALLOC_FREE(mem_ctx);
+ exit(1);
+ }
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case POPT_ERROR_BADOPT:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ poptFreeContext(pc);
+ samba_cmdline_burn(argc, argv);
+
+ ctx = talloc_zero(mem_ctx, struct regshell_context);
+
+ ev_ctx = s4_event_context_init(ctx);
+ lp_ctx = samba_cmdline_get_lp_ctx();
+ creds = samba_cmdline_get_creds();
+
+ if (remote != NULL) {
+ ctx->registry = reg_common_open_remote(remote, ev_ctx,
+ lp_ctx,
+ creds);
+ } else if (file != NULL) {
+ ctx->current = reg_common_open_file(file, ev_ctx,
+ lp_ctx,
+ creds);
+ if (ctx->current == NULL) {
+ TALLOC_FREE(mem_ctx);
+ exit(1);
+ }
+ ctx->registry = ctx->current->context;
+ ctx->path = talloc_strdup(ctx, "");
+ ctx->predef = NULL;
+ ctx->root = ctx->current;
+ } else {
+ ctx->registry = reg_common_open_local(creds,
+ ev_ctx,
+ lp_ctx);
+ }
+
+ if (ctx->registry == NULL) {
+ TALLOC_FREE(mem_ctx);
+ exit(1);
+ }
+
+ if (ctx->current == NULL) {
+ unsigned int i;
+
+ for (i = 0; (reg_predefined_keys[i].handle != 0) &&
+ (ctx->current == NULL); i++) {
+ WERROR err;
+ err = reg_get_predefined_key(ctx->registry,
+ reg_predefined_keys[i].handle,
+ &ctx->current);
+ if (W_ERROR_IS_OK(err)) {
+ ctx->predef = talloc_strdup(ctx,
+ reg_predefined_keys[i].name);
+ ctx->path = talloc_strdup(ctx, "");
+ ctx->root = ctx->current;
+ break;
+ } else {
+ ctx->current = NULL;
+ ctx->root = NULL;
+ }
+ }
+ }
+
+ if (ctx->current == NULL) {
+ fprintf(stderr, "Unable to access any of the predefined keys\n");
+ TALLOC_FREE(mem_ctx);
+ exit(1);
+ }
+
+ while (true) {
+ char *line, *prompt;
+
+ if (asprintf(&prompt, "%s\\%s> ", ctx->predef?ctx->predef:"",
+ ctx->path) < 0) {
+ ret = false;
+ break;
+ }
+
+ current_key = ctx->current; /* No way to pass a void * pointer
+ via readline :-( */
+ line = smb_readline(prompt, NULL, reg_completion);
+
+ if (line == NULL) {
+ free(prompt);
+ break;
+ }
+
+ if (line[0] != '\n') {
+ ret = W_ERROR_IS_OK(process_cmd(ctx, line));
+ }
+ free(line);
+ free(prompt);
+ }
+ TALLOC_FREE(mem_ctx);
+
+ return (ret?0:1);
+}
diff --git a/source4/lib/registry/tools/regtree.c b/source4/lib/registry/tools/regtree.c
new file mode 100644
index 0000000..1f0dac2
--- /dev/null
+++ b/source4/lib/registry/tools/regtree.c
@@ -0,0 +1,209 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple registry frontend
+
+ Copyright (C) Jelmer Vernooij 2004-2007
+
+ 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 "lib/registry/registry.h"
+#include "lib/registry/tools/common.h"
+#include "lib/events/events.h"
+#include "lib/cmdline/cmdline.h"
+#include "param/param.h"
+
+/**
+ * Print a registry key recursively
+ *
+ * @param level Level at which to print
+ * @param p Key to print
+ * @param fullpath Whether the full pat hshould be printed or just the last bit
+ * @param novals Whether values should not be printed
+ */
+static void print_tree(unsigned int level, struct registry_key *p,
+ const char *name,
+ bool fullpath, bool novals)
+{
+ struct registry_key *subkey;
+ const char *valuename, *keyname;
+ uint32_t valuetype;
+ DATA_BLOB valuedata;
+ struct security_descriptor *sec_desc;
+ WERROR error;
+ unsigned int i;
+ TALLOC_CTX *mem_ctx;
+
+ for(i = 0; i < level; i++) putchar(' ');
+ puts(name);
+
+ mem_ctx = talloc_init("print_tree");
+ for (i = 0; W_ERROR_IS_OK(error = reg_key_get_subkey_by_index(mem_ctx,
+ p,
+ i,
+ &keyname,
+ NULL,
+ NULL)); i++) {
+
+ SMB_ASSERT(strlen(keyname) > 0);
+ if (!W_ERROR_IS_OK(reg_open_key(mem_ctx, p, keyname, &subkey)))
+ continue;
+
+ print_tree(level+1, subkey, (fullpath && strlen(name))?
+ talloc_asprintf(mem_ctx, "%s\\%s",
+ name, keyname):
+ keyname, fullpath, novals);
+ talloc_free(subkey);
+ }
+ talloc_free(mem_ctx);
+
+ if(!W_ERROR_EQUAL(error, WERR_NO_MORE_ITEMS)) {
+ DEBUG(0, ("Error occurred while fetching subkeys for '%s': %s\n",
+ name, win_errstr(error)));
+ }
+
+ if (!novals) {
+ mem_ctx = talloc_init("print_tree");
+ for(i = 0; W_ERROR_IS_OK(error = reg_key_get_value_by_index(
+ mem_ctx, p, i, &valuename, &valuetype, &valuedata));
+ i++) {
+ unsigned int j;
+ for(j = 0; j < level+1; j++) putchar(' ');
+ printf("%s\n", reg_val_description(mem_ctx,
+ valuename, valuetype, valuedata));
+ }
+ talloc_free(mem_ctx);
+
+ if(!W_ERROR_EQUAL(error, WERR_NO_MORE_ITEMS)) {
+ DEBUG(0, ("Error occurred while fetching values for '%s': %s\n",
+ name, win_errstr(error)));
+ }
+ }
+
+ mem_ctx = talloc_init("sec_desc");
+ if (!W_ERROR_IS_OK(reg_get_sec_desc(mem_ctx, p, &sec_desc))) {
+ DEBUG(0, ("Error getting security descriptor\n"));
+ }
+ talloc_free(mem_ctx);
+}
+
+int main(int argc, char **argv)
+{
+ const char **argv_const = discard_const_p(const char *, argv);
+ bool ok;
+ TALLOC_CTX *mem_ctx = NULL;
+ int opt;
+ unsigned int i;
+ const char *file = NULL;
+ const char *remote = NULL;
+ poptContext pc;
+ struct registry_context *h = NULL;
+ struct registry_key *start_key = NULL;
+ struct tevent_context *ev_ctx;
+ struct loadparm_context *lp_ctx = NULL;
+ struct cli_credentials *creds = NULL;
+ WERROR error;
+ bool fullpath = false, no_values = false;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"file", 'F', POPT_ARG_STRING, &file, 0, "file path", NULL },
+ {"remote", 'R', POPT_ARG_STRING, &remote, 0, "connect to specified remote server", NULL },
+ {"fullpath", 'f', POPT_ARG_NONE, &fullpath, 0, "show full paths", NULL},
+ {"no-values", 'V', POPT_ARG_NONE, &no_values, 0, "don't show values", NULL},
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CREDENTIALS
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ mem_ctx = talloc_init("regtree.c/main");
+ if (mem_ctx == NULL) {
+ exit(ENOMEM);
+ }
+
+ ok = samba_cmdline_init(mem_ctx,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(mem_ctx);
+ exit(1);
+ }
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv_const,
+ long_options,
+ 0);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ TALLOC_FREE(mem_ctx);
+ exit(1);
+ }
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case POPT_ERROR_BADOPT:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ poptFreeContext(pc);
+ samba_cmdline_burn(argc, argv);
+
+ ev_ctx = s4_event_context_init(NULL);
+ lp_ctx = samba_cmdline_get_lp_ctx();
+ creds = samba_cmdline_get_creds();
+
+ if (remote != NULL) {
+ h = reg_common_open_remote(remote, ev_ctx, lp_ctx, creds);
+ } else if (file != NULL) {
+ start_key = reg_common_open_file(file, ev_ctx, lp_ctx, creds);
+ } else {
+ h = reg_common_open_local(creds, ev_ctx, lp_ctx);
+ }
+
+ if (h == NULL && start_key == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return 1;
+ }
+
+ error = WERR_OK;
+
+ if (start_key != NULL) {
+ print_tree(0, start_key, "", fullpath, no_values);
+ } else {
+ for(i = 0; reg_predefined_keys[i].handle; i++) {
+ error = reg_get_predefined_key(h,
+ reg_predefined_keys[i].handle,
+ &start_key);
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Skipping %s: %s\n",
+ reg_predefined_keys[i].name,
+ win_errstr(error));
+ continue;
+ }
+ SMB_ASSERT(start_key != NULL);
+ print_tree(0, start_key, reg_predefined_keys[i].name,
+ fullpath, no_values);
+ }
+ }
+
+ TALLOC_FREE(mem_ctx);
+ return 0;
+}
diff --git a/source4/lib/registry/util.c b/source4/lib/registry/util.c
new file mode 100644
index 0000000..1197adb
--- /dev/null
+++ b/source4/lib/registry/util.c
@@ -0,0 +1,302 @@
+/*
+ Unix SMB/CIFS implementation.
+ Transparent registry backend handling
+ Copyright (C) Jelmer Vernooij 2003-2007.
+ Copyright (C) Wilco Baan Hofman 2010.
+
+ 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 "lib/registry/registry.h"
+#include "librpc/gen_ndr/winreg.h"
+#include "lib/util/data_blob.h"
+
+_PUBLIC_ char *reg_val_data_string(TALLOC_CTX *mem_ctx, uint32_t type,
+ const DATA_BLOB data)
+{
+ size_t converted_size = 0;
+ char *ret = NULL;
+
+ if (data.length == 0)
+ return talloc_strdup(mem_ctx, "");
+
+ switch (type) {
+ case REG_EXPAND_SZ:
+ case REG_SZ:
+ convert_string_talloc(mem_ctx,
+ CH_UTF16, CH_UNIX,
+ data.data, data.length,
+ (void **)&ret, &converted_size);
+ break;
+ case REG_DWORD:
+ case REG_DWORD_BIG_ENDIAN:
+ SMB_ASSERT(data.length == sizeof(uint32_t));
+ ret = talloc_asprintf(mem_ctx, "0x%8.8x",
+ IVAL(data.data, 0));
+ break;
+ case REG_QWORD:
+ SMB_ASSERT(data.length == sizeof(uint64_t));
+ ret = talloc_asprintf(mem_ctx, "0x%16.16llx",
+ (long long)BVAL(data.data, 0));
+ break;
+ case REG_BINARY:
+ ret = data_blob_hex_string_upper(mem_ctx, &data);
+ break;
+ case REG_NONE:
+ /* "NULL" is the right return value */
+ break;
+ case REG_MULTI_SZ:
+ /* FIXME: We don't support this yet */
+ break;
+ default:
+ /* FIXME */
+ /* Other datatypes aren't supported -> return "NULL" */
+ break;
+ }
+
+ return ret;
+}
+
+/** Generate a string that describes a registry value */
+_PUBLIC_ char *reg_val_description(TALLOC_CTX *mem_ctx,
+ const char *name,
+ uint32_t data_type,
+ const DATA_BLOB data)
+{
+ return talloc_asprintf(mem_ctx, "%s = %s : %s", name?name:"<No Name>",
+ str_regtype(data_type),
+ reg_val_data_string(mem_ctx, data_type, data));
+}
+
+/*
+ * This implements reading hex bytes that include comma's.
+ * It was previously handled by strhex_to_data_blob, but that did not cover
+ * the format used by windows.
+ */
+static DATA_BLOB reg_strhex_to_data_blob(TALLOC_CTX *mem_ctx, const char *str)
+{
+ DATA_BLOB ret;
+ const char *HEXCHARS = "0123456789ABCDEF";
+ size_t i, j;
+ char *hi, *lo;
+
+ ret = data_blob_talloc_zero(mem_ctx, (strlen(str)+(strlen(str) % 3))/3);
+ j = 0;
+ for (i = 0; i < strlen(str); i++) {
+ hi = strchr(HEXCHARS, toupper(str[i]));
+ if (hi == NULL)
+ continue;
+
+ i++;
+ lo = strchr(HEXCHARS, toupper(str[i]));
+ if (lo == NULL)
+ break;
+
+ ret.data[j] = PTR_DIFF(hi, HEXCHARS) << 4;
+ ret.data[j] += PTR_DIFF(lo, HEXCHARS);
+ j++;
+
+ if (j > ret.length) {
+ DEBUG(0, ("Trouble converting hex string to bin\n"));
+ break;
+ }
+ }
+ return ret;
+}
+
+
+_PUBLIC_ bool reg_string_to_val(TALLOC_CTX *mem_ctx, const char *type_str,
+ const char *data_str, uint32_t *type, DATA_BLOB *data)
+{
+ char *tmp_type_str, *p, *q;
+ int result;
+
+ *type = regtype_by_string(type_str);
+
+ if (*type == -1) {
+ /* Normal windows format is hex, hex(type int as string),
+ dword or just a string. */
+ if (strncmp(type_str, "hex(", 4) == 0) {
+ /* there is a hex string with the value type between
+ the braces */
+ tmp_type_str = talloc_strdup(mem_ctx, type_str);
+ q = p = tmp_type_str + strlen("hex(");
+
+ /* Go to the closing brace or end of the string */
+ while (*q != ')' && *q != '\0') q++;
+ *q = '\0';
+
+ /* Convert hex string to int, store it in type */
+ result = sscanf(p, "%x", type);
+ if (!result) {
+ DEBUG(0, ("Could not convert hex to int\n"));
+ return false;
+ }
+ talloc_free(tmp_type_str);
+ } else if (strcmp(type_str, "hex") == 0) {
+ *type = REG_BINARY;
+ } else if (strcmp(type_str, "dword") == 0) {
+ *type = REG_DWORD;
+ }
+ }
+
+ if (*type == -1)
+ return false;
+
+ /* Convert data appropriately */
+
+ switch (*type) {
+ case REG_SZ:
+ return convert_string_talloc(mem_ctx,
+ CH_UNIX, CH_UTF16,
+ data_str, strlen(data_str)+1,
+ (void **)&data->data,
+ &data->length);
+ break;
+ case REG_MULTI_SZ:
+ case REG_EXPAND_SZ:
+ case REG_BINARY:
+ *data = reg_strhex_to_data_blob(mem_ctx, data_str);
+ break;
+ case REG_DWORD:
+ case REG_DWORD_BIG_ENDIAN: {
+ uint32_t tmp = strtol(data_str, NULL, 16);
+ *data = data_blob_talloc(mem_ctx, NULL, sizeof(uint32_t));
+ if (data->data == NULL) return false;
+ SIVAL(data->data, 0, tmp);
+ }
+ break;
+ case REG_QWORD: {
+ uint64_t tmp = strtoll(data_str, NULL, 16);
+ *data = data_blob_talloc(mem_ctx, NULL, sizeof(uint64_t));
+ if (data->data == NULL) return false;
+ SBVAL(data->data, 0, tmp);
+ }
+ break;
+ case REG_NONE:
+ ZERO_STRUCTP(data);
+ break;
+ default:
+ /* FIXME */
+ /* Other datatypes aren't supported -> return no success */
+ return false;
+ }
+ return true;
+}
+
+/** Open a key by name (including the predefined key name!) */
+WERROR reg_open_key_abs(TALLOC_CTX *mem_ctx, struct registry_context *handle,
+ const char *name, struct registry_key **result)
+{
+ struct registry_key *predef;
+ WERROR error;
+ size_t predeflength;
+ char *predefname;
+
+ if (strchr(name, '\\') != NULL)
+ predeflength = strchr(name, '\\')-name;
+ else
+ predeflength = strlen(name);
+
+ predefname = talloc_strndup(mem_ctx, name, predeflength);
+ W_ERROR_HAVE_NO_MEMORY(predefname);
+ error = reg_get_predefined_key_by_name(handle, predefname, &predef);
+ talloc_free(predefname);
+
+ if (!W_ERROR_IS_OK(error)) {
+ return error;
+ }
+
+ if (strchr(name, '\\')) {
+ return reg_open_key(mem_ctx, predef, strchr(name, '\\')+1,
+ result);
+ } else {
+ *result = predef;
+ return WERR_OK;
+ }
+}
+
+static WERROR get_abs_parent(TALLOC_CTX *mem_ctx, struct registry_context *ctx,
+ const char *path, struct registry_key **parent,
+ const char **name)
+{
+ char *parent_name;
+ WERROR error;
+
+ if (strchr(path, '\\') == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ parent_name = talloc_strndup(mem_ctx, path, strrchr(path, '\\')-path);
+ W_ERROR_HAVE_NO_MEMORY(parent_name);
+ error = reg_open_key_abs(mem_ctx, ctx, parent_name, parent);
+ talloc_free(parent_name);
+ if (!W_ERROR_IS_OK(error)) {
+ return error;
+ }
+
+ *name = talloc_strdup(mem_ctx, strrchr(path, '\\')+1);
+ W_ERROR_HAVE_NO_MEMORY(*name);
+
+ return WERR_OK;
+}
+
+WERROR reg_key_del_abs(struct registry_context *ctx, const char *path)
+{
+ struct registry_key *parent;
+ const char *n;
+ TALLOC_CTX *mem_ctx = talloc_init("reg_key_del_abs");
+ WERROR error;
+
+ if (!strchr(path, '\\')) {
+ return WERR_FOOBAR;
+ }
+
+ error = get_abs_parent(mem_ctx, ctx, path, &parent, &n);
+ if (W_ERROR_IS_OK(error)) {
+ error = reg_key_del(mem_ctx, parent, n);
+ }
+
+ talloc_free(mem_ctx);
+
+ return error;
+}
+
+WERROR reg_key_add_abs(TALLOC_CTX *mem_ctx, struct registry_context *ctx,
+ const char *path, uint32_t access_mask,
+ struct security_descriptor *sec_desc,
+ struct registry_key **result)
+{
+ struct registry_key *parent;
+ const char *n;
+ WERROR error;
+
+ *result = NULL;
+
+ if (!strchr(path, '\\')) {
+ return WERR_ALREADY_EXISTS;
+ }
+
+ error = get_abs_parent(mem_ctx, ctx, path, &parent, &n);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(2, ("Opening parent of %s failed with %s\n", path,
+ win_errstr(error)));
+ return error;
+ }
+
+ error = reg_key_add_name(mem_ctx, parent, n, NULL, sec_desc, result);
+
+ return error;
+}
diff --git a/source4/lib/registry/wine.c b/source4/lib/registry/wine.c
new file mode 100644
index 0000000..77d2ce6
--- /dev/null
+++ b/source4/lib/registry/wine.c
@@ -0,0 +1,45 @@
+/*
+ Unix SMB/CIFS implementation.
+ Registry interface
+ Copyright (C) Jelmer Vernooij 2007.
+
+ 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 "lib/registry/common/registry.h"
+#include "windows/registry.h"
+
+static WERROR wine_open_reg (struct registry_hive *h, struct registry_key **key)
+{
+ /* FIXME: Open h->location and mmap it */
+}
+
+static REG_OPS reg_backend_wine = {
+ .name = "wine",
+ .open_hive = wine_open_reg,
+
+};
+
+NTSTATUS registry_wine_init(void)
+{
+ register_backend("registry", &reg_backend_wine);
+ return NT_STATUS_OK;
+}
+
+WERROR reg_open_wine(struct registry_key **ctx)
+{
+ /* FIXME: Open ~/.wine/system.reg, etc */
+ return WERR_NOT_SUPPORTED;
+}
diff --git a/source4/lib/registry/wscript_build b/source4/lib/registry/wscript_build
new file mode 100644
index 0000000..2e01e43
--- /dev/null
+++ b/source4/lib/registry/wscript_build
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+
+bld.SAMBA_PIDL('PIDL_REG',
+ source='regf.idl',
+ options='--header --tdr-parser')
+
+bld.SAMBA_SUBSYSTEM('TDR_REGF',
+ source='tdr_regf.c',
+ public_deps='TDR'
+ )
+
+
+bld.SAMBA_LIBRARY('registry',
+ source='interface.c util.c samba.c patchfile_dotreg.c patchfile_preg.c patchfile.c regf.c hive.c local.c ldb.c rpc.c',
+ public_deps='dcerpc samba-util TDR_REGF ldb RPC_NDR_WINREG ldbsamba util_reg',
+ private_headers='registry.h',
+ private_library=True
+ )
+
+
+bld.SAMBA_SUBSYSTEM('registry_common',
+ source='tools/common.c',
+ autoproto='tools/common.h',
+ public_deps='registry'
+ )
+
+
+bld.SAMBA_BINARY('regdiff',
+ source='tools/regdiff.c',
+ manpages='man/regdiff.1',
+ deps='samba-hostconfig registry popt CMDLINE_S4'
+ )
+
+
+bld.SAMBA_BINARY('regpatch',
+ source='tools/regpatch.c',
+ manpages='man/regpatch.1',
+ deps='samba-hostconfig registry popt CMDLINE_S4 registry_common'
+ )
+
+
+bld.SAMBA_BINARY('regshell',
+ source='tools/regshell.c',
+ manpages='man/regshell.1',
+ deps='samba-hostconfig popt registry CMDLINE_S4 SMBREADLINE registry_common'
+ )
+
+
+bld.SAMBA_BINARY('regtree',
+ source='tools/regtree.c',
+ manpages='man/regtree.1',
+ deps='samba-hostconfig popt registry CMDLINE_S4 registry_common'
+ )
+
+
+bld.SAMBA_SUBSYSTEM('torture_registry',
+ source='tests/generic.c tests/hive.c tests/diff.c tests/registry.c',
+ autoproto='tests/proto.h',
+ deps='torture registry'
+ )
+
+pytalloc_util = bld.pyembed_libname('pytalloc-util')
+pyparam_util = bld.pyembed_libname('pyparam_util')
+
+bld.SAMBA_PYTHON('py_registry',
+ source='pyregistry.c',
+ public_deps='registry %s %s' % (pytalloc_util, pyparam_util),
+ realname='samba/registry.so'
+ )
diff --git a/source4/lib/samba3/README b/source4/lib/samba3/README
new file mode 100644
index 0000000..3f6553f
--- /dev/null
+++ b/source4/lib/samba3/README
@@ -0,0 +1,5 @@
+This directory contains various files and functions for the purpose of
+Samba3 import, migration and compatibility.
+
+For example, the first file in this directory (smbpasswd.c) handles
+portions of the smbpasswd file format.
diff --git a/source4/lib/samba3/samba3.h b/source4/lib/samba3/samba3.h
new file mode 100644
index 0000000..f1c5d44
--- /dev/null
+++ b/source4/lib/samba3/samba3.h
@@ -0,0 +1,29 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba3 interfaces
+ Copyright (C) Jelmer Vernooij 2005.
+
+ 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 _SAMBA3_H /* _SAMBA3_H */
+#define _SAMBA3_H
+
+#include "librpc/gen_ndr/security.h"
+#include "librpc/gen_ndr/samr.h"
+
+struct samr_Password *smbpasswd_gethexpwd(TALLOC_CTX *mem_ctx, const char *p);
+char *smbpasswd_sethexpwd(TALLOC_CTX *mem_ctx, struct samr_Password *pwd, uint16_t acb_info);
+
+#endif /* _SAMBA3_H */
diff --git a/source4/lib/samba3/smbpasswd.c b/source4/lib/samba3/smbpasswd.c
new file mode 100644
index 0000000..ae361b7
--- /dev/null
+++ b/source4/lib/samba3/smbpasswd.c
@@ -0,0 +1,111 @@
+/*
+ Unix SMB/CIFS implementation.
+ smbpasswd file format routines
+
+ Copyright (C) Andrew Tridgell 1992-1998
+ Modified by Jeremy Allison 1995.
+ Modified by Gerald (Jerry) Carter 2000-2001
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2005
+
+ 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/>.
+*/
+
+/*! \file lib/smbpasswd.c
+
+ The smbpasswd file is used to store encrypted passwords in a similar
+ fashion to the /etc/passwd file. The format is colon separated fields
+ with one user per line like so:
+
+ <username>:<uid>:<lanman hash>:<nt hash>:<acb info>:<last change time>
+
+ The username and uid must correspond to an entry in the /etc/passwd
+ file. The lanman and nt password hashes are 32 hex digits corresponding
+ to the 16-byte lanman and nt hashes respectively.
+
+ The password last change time is stored as a string of the format
+ LCD-<change time> where the change time is expressed as an
+
+ 'N' No password
+ 'D' Disabled
+ 'H' Homedir required
+ 'T' Temp account.
+ 'U' User account (normal)
+ 'M' MNS logon user account - what is this ?
+ 'W' Workstation account
+ 'S' Server account
+ 'L' Locked account
+ 'X' No Xpiry on password
+ 'I' Interdomain trust account
+
+*/
+
+#include "includes.h"
+#include "system/locale.h"
+#include "lib/samba3/samba3.h"
+
+/*! Convert 32 hex characters into a 16 byte array. */
+
+struct samr_Password *smbpasswd_gethexpwd(TALLOC_CTX *mem_ctx, const char *p)
+{
+ int i;
+ unsigned char lonybble, hinybble;
+ const char *hexchars = "0123456789ABCDEF";
+ const char *p1, *p2;
+ struct samr_Password *pwd = talloc(mem_ctx, struct samr_Password);
+
+ if (!p) return NULL;
+
+ for (i = 0; i < (sizeof(pwd->hash) * 2); i += 2)
+ {
+ hinybble = toupper(p[i]);
+ lonybble = toupper(p[i + 1]);
+
+ p1 = strchr_m(hexchars, hinybble);
+ p2 = strchr_m(hexchars, lonybble);
+
+ if (!p1 || !p2) {
+ return NULL;
+ }
+
+ hinybble = PTR_DIFF(p1, hexchars);
+ lonybble = PTR_DIFF(p2, hexchars);
+
+ pwd->hash[i / 2] = (hinybble << 4) | lonybble;
+ }
+ return pwd;
+}
+
+/*! Convert a 16-byte array into 32 hex characters. */
+char *smbpasswd_sethexpwd(TALLOC_CTX *mem_ctx, struct samr_Password *pwd, uint16_t acb_info)
+{
+ char *p;
+ if (pwd != NULL) {
+ int i;
+ p = talloc_array(mem_ctx, char, 33);
+ if (!p) {
+ return NULL;
+ }
+
+ for (i = 0; i < sizeof(pwd->hash); i++)
+ slprintf(&p[i*2], 3, "%02X", pwd->hash[i]);
+ } else {
+ if (acb_info & ACB_PWNOTREQ)
+ p = talloc_strdup(mem_ctx, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX");
+ else
+ p = talloc_strdup(mem_ctx, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
+ }
+ return p;
+}
diff --git a/source4/lib/samba3/wscript_build b/source4/lib/samba3/wscript_build
new file mode 100644
index 0000000..98248c9
--- /dev/null
+++ b/source4/lib/samba3/wscript_build
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+
+bld.SAMBA_LIBRARY('smbpasswdparser',
+ source='smbpasswd.c',
+ deps='samba-util',
+ private_library=True
+ )
+
diff --git a/source4/lib/socket/access.c b/source4/lib/socket/access.c
new file mode 100644
index 0000000..c019fd6
--- /dev/null
+++ b/source4/lib/socket/access.c
@@ -0,0 +1,129 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ check access rules for socket connections
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+/*
+ This module is an adaption of code from the tcpd-1.4 package written
+ by Wietse Venema, Eindhoven University of Technology, The Netherlands.
+
+ The code is used here with permission.
+
+ The code has been considerably changed from the original. Bug reports
+ should be sent to samba-technical@lists.samba.org
+*/
+
+#include "includes.h"
+#include "system/network.h"
+#include "lib/socket/socket.h"
+#include "lib/util/util_net.h"
+#include "lib/util/access.h"
+
+/* return true if the char* contains ip addrs only. Used to avoid
+gethostbyaddr() calls */
+
+static bool only_ipaddrs_in_list(const char** list)
+{
+ bool only_ip = true;
+
+ if (!list)
+ return true;
+
+ for (; *list ; list++) {
+ /* factor out the special strings */
+ if (strcmp(*list, "ALL")==0 ||
+ strcmp(*list, "FAIL")==0 ||
+ strcmp(*list, "EXCEPT")==0) {
+ continue;
+ }
+
+ if (!is_ipaddress(*list)) {
+ /*
+ * if we failed, make sure that it was not because the token
+ * was a network/netmask pair. Only network/netmask pairs
+ * have a '/' in them
+ */
+ if ((strchr(*list, '/')) == NULL) {
+ only_ip = false;
+ DEBUG(3,("only_ipaddrs_in_list: list has non-ip address (%s)\n", *list));
+ break;
+ }
+ }
+ }
+
+ return only_ip;
+}
+
+/* return true if access should be allowed to a service for a socket */
+bool socket_check_access(struct socket_context *sock,
+ const char *service_name,
+ const char **allow_list, const char **deny_list)
+{
+ bool ret;
+ const char *name="";
+ struct socket_address *addr;
+ TALLOC_CTX *mem_ctx;
+
+ if ((!deny_list || *deny_list==0) &&
+ (!allow_list || *allow_list==0)) {
+ return true;
+ }
+
+ mem_ctx = talloc_init("socket_check_access");
+ if (!mem_ctx) {
+ return false;
+ }
+
+ addr = socket_get_peer_addr(sock, mem_ctx);
+ if (!addr) {
+ DEBUG(0,("socket_check_access: Denied connection from unknown host: could not get peer address from kernel\n"));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ /* bypass gethostbyaddr() calls if the lists only contain IP addrs */
+ if (!only_ipaddrs_in_list(allow_list) ||
+ !only_ipaddrs_in_list(deny_list)) {
+ name = socket_get_peer_name(sock, mem_ctx);
+ if (!name) {
+ name = addr->addr;
+ }
+ }
+
+ if (!addr) {
+ DEBUG(0,("socket_check_access: Denied connection from unknown host\n"));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ ret = allow_access(deny_list, allow_list, name, addr->addr);
+
+ if (ret) {
+ DEBUG(2,("socket_check_access: Allowed connection to '%s' from %s (%s)\n",
+ service_name, name, addr->addr));
+ } else {
+ DEBUG(0,("socket_check_access: Denied connection to '%s' from %s (%s)\n",
+ service_name, name, addr->addr));
+ }
+
+ talloc_free(mem_ctx);
+
+ return ret;
+}
diff --git a/source4/lib/socket/connect.c b/source4/lib/socket/connect.c
new file mode 100644
index 0000000..1da8b41
--- /dev/null
+++ b/source4/lib/socket/connect.c
@@ -0,0 +1,158 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ implements a non-blocking connect operation that is aware of the samba4
+ events system
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Volker Lendecke 2005
+
+ 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 "lib/socket/socket.h"
+#include "lib/events/events.h"
+#include "libcli/composite/composite.h"
+
+
+struct connect_state {
+ struct socket_context *sock;
+ const struct socket_address *my_address;
+ const struct socket_address *server_address;
+ uint32_t flags;
+};
+
+static void socket_connect_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags, void *private_data);
+
+/*
+ call the real socket_connect() call, and setup event handler
+*/
+static void socket_send_connect(struct composite_context *result)
+{
+ struct tevent_fd *fde;
+ struct connect_state *state = talloc_get_type(result->private_data,
+ struct connect_state);
+
+ result->status = socket_connect(state->sock,
+ state->my_address,
+ state->server_address,
+ state->flags);
+ if (NT_STATUS_IS_ERR(result->status) &&
+ !NT_STATUS_EQUAL(result->status,
+ NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ composite_error(result, result->status);
+ return;
+ }
+
+ fde = tevent_add_fd(result->event_ctx, result,
+ socket_get_fd(state->sock),
+ TEVENT_FD_READ|TEVENT_FD_WRITE,
+ socket_connect_handler, result);
+ composite_nomem(fde, result);
+}
+
+
+/*
+ send a socket connect, potentially doing some name resolution first
+*/
+struct composite_context *socket_connect_send(struct socket_context *sock,
+ struct socket_address *my_address,
+ struct socket_address *server_address,
+ uint32_t flags,
+ struct tevent_context *event_ctx)
+{
+ struct composite_context *result;
+ struct connect_state *state;
+
+ result = composite_create(sock, event_ctx);
+ if (result == NULL) return NULL;
+
+ state = talloc_zero(result, struct connect_state);
+ if (composite_nomem(state, result)) return result;
+ result->private_data = state;
+
+ state->sock = talloc_reference(state, sock);
+ if (composite_nomem(state->sock, result)) return result;
+
+ if (my_address) {
+ void *ref = talloc_reference(state, my_address);
+ if (composite_nomem(ref, result)) {
+ return result;
+ }
+ state->my_address = my_address;
+ }
+
+ {
+ void *ref = talloc_reference(state, server_address);
+ if (composite_nomem(ref, result)) {
+ return result;
+ }
+ state->server_address = server_address;
+ }
+
+ state->flags = flags;
+
+ set_blocking(socket_get_fd(sock), false);
+
+ socket_send_connect(result);
+
+ return result;
+}
+
+/*
+ handle write events on connect completion
+*/
+static void socket_connect_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct composite_context *result =
+ talloc_get_type(private_data, struct composite_context);
+ struct connect_state *state = talloc_get_type(result->private_data,
+ struct connect_state);
+
+ result->status = socket_connect_complete(state->sock, state->flags);
+ if (!composite_is_ok(result)) return;
+
+ composite_done(result);
+}
+
+/*
+ wait for a socket_connect_send() to finish
+*/
+NTSTATUS socket_connect_recv(struct composite_context *result)
+{
+ NTSTATUS status = composite_wait(result);
+ talloc_free(result);
+ return status;
+}
+
+
+/*
+ like socket_connect() but takes an event context, doing a semi-async connect
+*/
+NTSTATUS socket_connect_ev(struct socket_context *sock,
+ struct socket_address *my_address,
+ struct socket_address *server_address,
+ uint32_t flags,
+ struct tevent_context *ev)
+{
+ struct composite_context *ctx;
+ ctx = socket_connect_send(sock, my_address,
+ server_address, flags, ev);
+ return socket_connect_recv(ctx);
+}
diff --git a/source4/lib/socket/connect_multi.c b/source4/lib/socket/connect_multi.c
new file mode 100644
index 0000000..b29fffb
--- /dev/null
+++ b/source4/lib/socket/connect_multi.c
@@ -0,0 +1,392 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Fire connect requests to a host and a number of ports, with a timeout
+ between the connect request. Return if the first connect comes back
+ successfully or return the last error.
+
+ Copyright (C) Volker Lendecke 2005
+
+ 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 "lib/socket/socket.h"
+#include "lib/events/events.h"
+#include "libcli/composite/composite.h"
+#include "libcli/resolve/resolve.h"
+
+#define MULTI_PORT_DELAY 2000 /* microseconds */
+
+/*
+ overall state
+*/
+struct connect_multi_state {
+ struct socket_address **server_address;
+ unsigned num_address, current_address, current_port;
+ int num_ports;
+ uint16_t *ports;
+
+ struct socket_context *sock;
+ uint16_t result_port;
+
+ int num_connects_sent, num_connects_recv;
+
+ struct socket_connect_multi_ex *ex;
+};
+
+/*
+ state of an individual socket_connect_send() call
+*/
+struct connect_one_state {
+ struct composite_context *result;
+ struct socket_context *sock;
+ struct socket_address *addr;
+};
+
+static void continue_resolve_name(struct composite_context *creq);
+static void connect_multi_timer(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv, void *p);
+static void connect_multi_next_socket(struct composite_context *result);
+static void continue_one(struct composite_context *creq);
+static void continue_one_ex(struct tevent_req *subreq);
+
+/*
+ setup an async socket_connect, with multiple ports
+*/
+_PUBLIC_ struct composite_context *socket_connect_multi_ex_send(
+ TALLOC_CTX *mem_ctx,
+ const char *server_name,
+ int num_server_ports,
+ uint16_t *server_ports,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx,
+ struct socket_connect_multi_ex *ex)
+{
+ struct composite_context *result;
+ struct connect_multi_state *multi;
+ int i;
+
+ struct nbt_name name;
+ struct composite_context *creq;
+
+ result = talloc_zero(mem_ctx, struct composite_context);
+ if (result == NULL) return NULL;
+ result->state = COMPOSITE_STATE_IN_PROGRESS;
+ result->event_ctx = event_ctx;
+
+ multi = talloc_zero(result, struct connect_multi_state);
+ if (composite_nomem(multi, result)) goto failed;
+ result->private_data = multi;
+
+ multi->num_ports = num_server_ports;
+ multi->ports = talloc_array(multi, uint16_t, multi->num_ports);
+ if (composite_nomem(multi->ports, result)) goto failed;
+
+ for (i=0; i<multi->num_ports; i++) {
+ multi->ports[i] = server_ports[i];
+ }
+
+ multi->ex = ex;
+
+ /*
+ we don't want to do the name resolution separately
+ for each port, so start it now, then only start on
+ the real sockets once we have an IP
+ */
+ make_nbt_name_server(&name, server_name);
+
+ creq = resolve_name_all_send(resolve_ctx, multi, 0, multi->ports[0], &name, result->event_ctx);
+ if (composite_nomem(creq, result)) goto failed;
+
+ composite_continue(result, creq, continue_resolve_name, result);
+
+ return result;
+
+
+ failed:
+ composite_error(result, result->status);
+ return result;
+}
+
+/*
+ start connecting to the next socket/port in the list
+*/
+static void connect_multi_next_socket(struct composite_context *result)
+{
+ struct connect_multi_state *multi = talloc_get_type(result->private_data,
+ struct connect_multi_state);
+ struct connect_one_state *state;
+ struct composite_context *creq;
+ int next = multi->num_connects_sent;
+
+ if (next == multi->num_address * multi->num_ports) {
+ /* don't do anything, just wait for the existing ones to finish */
+ return;
+ }
+
+ if (multi->current_address == multi->num_address) {
+ multi->current_address = 0;
+ multi->current_port += 1;
+ }
+ multi->num_connects_sent += 1;
+
+ if (multi->server_address == NULL || multi->server_address[multi->current_address] == NULL) {
+ composite_error(result, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+
+ state = talloc(multi, struct connect_one_state);
+ if (composite_nomem(state, result)) return;
+
+ state->result = result;
+ result->status = socket_create(
+ state, multi->server_address[multi->current_address]->family,
+ SOCKET_TYPE_STREAM, &state->sock, 0);
+ if (!composite_is_ok(result)) return;
+
+ state->addr = socket_address_copy(state, multi->server_address[multi->current_address]);
+ if (composite_nomem(state->addr, result)) return;
+
+ socket_address_set_port(state->addr, multi->ports[multi->current_port]);
+
+ creq = socket_connect_send(state->sock, NULL,
+ state->addr, 0,
+ result->event_ctx);
+ if (composite_nomem(creq, result)) return;
+ talloc_steal(state, creq);
+
+ multi->current_address++;
+ composite_continue(result, creq, continue_one, state);
+
+ /* if there are more ports / addresses to go then setup a timer to fire when we have waited
+ for a couple of milli-seconds, when that goes off we try the next port regardless
+ of whether this port has completed */
+ if (multi->num_ports * multi->num_address > multi->num_connects_sent) {
+ /* note that this timer is a child of the single
+ connect attempt state, so it will go away when this
+ request completes */
+ tevent_add_timer(result->event_ctx, state,
+ timeval_current_ofs_usec(MULTI_PORT_DELAY),
+ connect_multi_timer, result);
+ }
+}
+
+/*
+ a timer has gone off telling us that we should try the next port
+*/
+static void connect_multi_timer(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv, void *p)
+{
+ struct composite_context *result = talloc_get_type(p, struct composite_context);
+ connect_multi_next_socket(result);
+}
+
+
+/*
+ recv name resolution reply then send the next connect
+*/
+static void continue_resolve_name(struct composite_context *creq)
+{
+ struct composite_context *result = talloc_get_type(creq->async.private_data,
+ struct composite_context);
+ struct connect_multi_state *multi = talloc_get_type(result->private_data,
+ struct connect_multi_state);
+ struct socket_address **addr;
+ unsigned i;
+
+ result->status = resolve_name_all_recv(creq, multi, &addr, NULL);
+ if (!composite_is_ok(result)) return;
+
+ for(i=0; addr[i]; i++);
+ multi->num_address = i;
+ multi->server_address = talloc_steal(multi, addr);
+
+ connect_multi_next_socket(result);
+}
+
+/*
+ one of our socket_connect_send() calls hash finished. If it got a
+ connection or there are none left then we are done
+*/
+static void continue_one(struct composite_context *creq)
+{
+ struct connect_one_state *state = talloc_get_type(creq->async.private_data,
+ struct connect_one_state);
+ struct composite_context *result = state->result;
+ struct connect_multi_state *multi = talloc_get_type(result->private_data,
+ struct connect_multi_state);
+ NTSTATUS status;
+
+ status = socket_connect_recv(creq);
+
+ if (multi->ex) {
+ struct tevent_req *subreq;
+
+ subreq = multi->ex->establish_send(state,
+ result->event_ctx,
+ state->sock,
+ state->addr,
+ multi->ex->private_data);
+ if (composite_nomem(subreq, result)) return;
+ tevent_req_set_callback(subreq, continue_one_ex, state);
+ return;
+ }
+
+ multi->num_connects_recv++;
+
+ if (NT_STATUS_IS_OK(status)) {
+ multi->sock = talloc_steal(multi, state->sock);
+ multi->result_port = state->addr->port;
+ }
+
+ talloc_free(state);
+
+ if (NT_STATUS_IS_OK(status) ||
+ multi->num_connects_recv == (multi->num_address * multi->num_ports)) {
+ result->status = status;
+ composite_done(result);
+ return;
+ }
+
+ /* try the next port */
+ connect_multi_next_socket(result);
+}
+
+/*
+ one of our multi->ex->establish_send() calls hash finished. If it got a
+ connection or there are none left then we are done
+*/
+static void continue_one_ex(struct tevent_req *subreq)
+{
+ struct connect_one_state *state =
+ tevent_req_callback_data(subreq,
+ struct connect_one_state);
+ struct composite_context *result = state->result;
+ struct connect_multi_state *multi =
+ talloc_get_type_abort(result->private_data,
+ struct connect_multi_state);
+ NTSTATUS status;
+ multi->num_connects_recv++;
+
+ status = multi->ex->establish_recv(subreq);
+ TALLOC_FREE(subreq);
+
+ if (NT_STATUS_IS_OK(status)) {
+ multi->sock = talloc_steal(multi, state->sock);
+ multi->result_port = state->addr->port;
+ }
+
+ talloc_free(state);
+
+ if (NT_STATUS_IS_OK(status) ||
+ multi->num_connects_recv == (multi->num_address * multi->num_ports)) {
+ result->status = status;
+ composite_done(result);
+ return;
+ }
+
+ /* try the next port */
+ connect_multi_next_socket(result);
+}
+
+/*
+ async recv routine for socket_connect_multi()
+ */
+_PUBLIC_ NTSTATUS socket_connect_multi_ex_recv(struct composite_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct socket_context **sock,
+ uint16_t *port)
+{
+ NTSTATUS status = composite_wait(ctx);
+ if (NT_STATUS_IS_OK(status)) {
+ struct connect_multi_state *multi =
+ talloc_get_type(ctx->private_data,
+ struct connect_multi_state);
+ *sock = talloc_steal(mem_ctx, multi->sock);
+ *port = multi->result_port;
+ }
+ talloc_free(ctx);
+ return status;
+}
+
+NTSTATUS socket_connect_multi_ex(TALLOC_CTX *mem_ctx,
+ const char *server_address,
+ int num_server_ports, uint16_t *server_ports,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx,
+ struct socket_connect_multi_ex *ex,
+ struct socket_context **result,
+ uint16_t *result_port)
+{
+ struct composite_context *ctx =
+ socket_connect_multi_ex_send(mem_ctx, server_address,
+ num_server_ports, server_ports,
+ resolve_ctx,
+ event_ctx,
+ ex);
+ return socket_connect_multi_ex_recv(ctx, mem_ctx, result, result_port);
+}
+
+/*
+ setup an async socket_connect, with multiple ports
+*/
+_PUBLIC_ struct composite_context *socket_connect_multi_send(
+ TALLOC_CTX *mem_ctx,
+ const char *server_name,
+ int num_server_ports,
+ uint16_t *server_ports,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx)
+{
+ return socket_connect_multi_ex_send(mem_ctx,
+ server_name,
+ num_server_ports,
+ server_ports,
+ resolve_ctx,
+ event_ctx,
+ NULL); /* ex */
+}
+
+/*
+ async recv routine for socket_connect_multi()
+ */
+_PUBLIC_ NTSTATUS socket_connect_multi_recv(struct composite_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct socket_context **sock,
+ uint16_t *port)
+{
+ return socket_connect_multi_ex_recv(ctx, mem_ctx, sock, port);
+}
+
+NTSTATUS socket_connect_multi(TALLOC_CTX *mem_ctx,
+ const char *server_address,
+ int num_server_ports, uint16_t *server_ports,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx,
+ struct socket_context **result,
+ uint16_t *result_port)
+{
+ return socket_connect_multi_ex(mem_ctx,
+ server_address,
+ num_server_ports,
+ server_ports,
+ resolve_ctx,
+ event_ctx,
+ NULL, /* ex */
+ result,
+ result_port);
+}
diff --git a/source4/lib/socket/interface.c b/source4/lib/socket/interface.c
new file mode 100644
index 0000000..65543c6
--- /dev/null
+++ b/source4/lib/socket/interface.c
@@ -0,0 +1,525 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ multiple interface handling
+
+ Copyright (C) Andrew Tridgell 1992-2005
+
+ 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/network.h"
+#include "param/param.h"
+#include "lib/socket/netif.h"
+#include "../lib/util/util_net.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/util/smb_strtox.h"
+
+/* used for network interfaces */
+struct interface {
+ struct interface *next, *prev;
+ char *name;
+ int flags;
+ struct sockaddr_storage ip;
+ struct sockaddr_storage netmask;
+ struct sockaddr_storage bcast;
+ const char *ip_s;
+ const char *bcast_s;
+ const char *nmask_s;
+};
+
+#define ALLONES ((uint32_t)0xFFFFFFFF)
+/*
+ address construction based on a patch from fred@datalync.com
+*/
+#define MKBCADDR(_IP, _NM) ((_IP & _NM) | (_NM ^ ALLONES))
+#define MKNETADDR(_IP, _NM) (_IP & _NM)
+
+/****************************************************************************
+Try and find an interface that matches an ip. If we cannot, return NULL
+ **************************************************************************/
+static struct interface *iface_list_find(struct interface *interfaces,
+ const struct sockaddr *ip,
+ bool check_mask)
+{
+ struct interface *i;
+
+ if (is_address_any(ip)) {
+ return interfaces;
+ }
+
+ for (i=interfaces;i;i=i->next) {
+ if (check_mask) {
+ if (same_net(ip, (struct sockaddr *)&i->ip, (struct sockaddr *)&i->netmask)) {
+ return i;
+ }
+ } else if (sockaddr_equal((struct sockaddr *)&i->ip, ip)) {
+ return i;
+ }
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+add an interface to the linked list of interfaces
+****************************************************************************/
+static void add_interface(TALLOC_CTX *mem_ctx, const struct iface_struct *ifs, struct interface **interfaces)
+{
+ char addr[INET6_ADDRSTRLEN];
+ struct interface *iface;
+
+ if (iface_list_find(*interfaces, (const struct sockaddr *)&ifs->ip, false)) {
+ DEBUG(3,("add_interface: not adding duplicate interface %s\n",
+ print_sockaddr(addr, sizeof(addr), &ifs->ip) ));
+ return;
+ }
+
+ if (ifs->ip.ss_family == AF_INET &&
+ !(ifs->flags & (IFF_BROADCAST|IFF_LOOPBACK))) {
+ DEBUG(3,("not adding non-broadcast interface %s\n",
+ ifs->name ));
+ return;
+ }
+
+ if (*interfaces != NULL) {
+ mem_ctx = *interfaces;
+ }
+
+ iface = talloc_zero(mem_ctx, struct interface);
+ if (iface == NULL) {
+ return;
+ }
+
+ iface->name = talloc_strdup(iface, ifs->name);
+ if (!iface->name) {
+ SAFE_FREE(iface);
+ return;
+ }
+ iface->flags = ifs->flags;
+ iface->ip = ifs->ip;
+ iface->netmask = ifs->netmask;
+ iface->bcast = ifs->bcast;
+
+ /* keep string versions too, to avoid people tripping over the implied
+ static in inet_ntoa() */
+ print_sockaddr(addr, sizeof(addr), &iface->ip);
+ DEBUG(4,("added interface %s ip=%s ",
+ iface->name, addr));
+ iface->ip_s = talloc_strdup(iface, addr);
+
+ print_sockaddr(addr, sizeof(addr),
+ &iface->bcast);
+ DEBUG(4,("bcast=%s ", addr));
+ iface->bcast_s = talloc_strdup(iface, addr);
+
+ print_sockaddr(addr, sizeof(addr),
+ &iface->netmask);
+ DEBUG(4,("netmask=%s\n", addr));
+ iface->nmask_s = talloc_strdup(iface, addr);
+
+ /*
+ this needs to be a ADD_END, as some tests (such as the
+ spoolss notify test) depend on the interfaces ordering
+ */
+ DLIST_ADD_END(*interfaces, iface);
+}
+
+/**
+interpret a single element from a interfaces= config line
+
+This handles the following different forms:
+
+1) wildcard interface name
+2) DNS name
+3) IP/masklen
+4) ip/mask
+5) bcast/mask
+**/
+static void interpret_interface(TALLOC_CTX *mem_ctx,
+ const char *token,
+ struct iface_struct *probed_ifaces,
+ int total_probed,
+ struct interface **local_interfaces)
+{
+ struct sockaddr_storage ss;
+ struct sockaddr_storage ss_mask;
+ struct sockaddr_storage ss_net;
+ struct sockaddr_storage ss_bcast;
+ struct iface_struct ifs;
+ char *p;
+ int i;
+ bool added=false;
+ bool goodaddr = false;
+
+ /* first check if it is an interface name */
+ for (i=0;i<total_probed;i++) {
+ if (gen_fnmatch(token, probed_ifaces[i].name) == 0) {
+ add_interface(mem_ctx, &probed_ifaces[i],
+ local_interfaces);
+ added = true;
+ }
+ }
+ if (added) {
+ return;
+ }
+
+ p = strchr_m(token, ';');
+ if (p != NULL) {
+ /*
+ * skip smbd-specific extra data:
+ * link speed, capabilities, and interface index
+ */
+ *p = 0;
+ }
+
+ /* maybe it is a DNS name */
+ p = strchr_m(token,'/');
+ if (p == NULL) {
+ if (!interpret_string_addr(&ss, token, 0)) {
+ DEBUG(2, ("interpret_interface: Can't find address "
+ "for %s\n", token));
+ return;
+ }
+
+ for (i=0;i<total_probed;i++) {
+ if (sockaddr_equal((struct sockaddr *)&ss, (struct sockaddr *)&probed_ifaces[i].ip)) {
+ add_interface(mem_ctx, &probed_ifaces[i],
+ local_interfaces);
+ return;
+ }
+ }
+ DEBUG(2,("interpret_interface: "
+ "can't determine interface for %s\n",
+ token));
+ return;
+ }
+
+ /* parse it into an IP address/netmasklength pair */
+ *p = 0;
+ goodaddr = interpret_string_addr(&ss, token, 0);
+ *p++ = '/';
+
+ if (!goodaddr) {
+ DEBUG(2,("interpret_interface: "
+ "can't determine interface for %s\n",
+ token));
+ return;
+ }
+
+ if (strlen(p) > 2) {
+ goodaddr = interpret_string_addr(&ss_mask, p, 0);
+ if (!goodaddr) {
+ DEBUG(2,("interpret_interface: "
+ "can't determine netmask from %s\n",
+ p));
+ return;
+ }
+ } else {
+ int error = 0;
+
+ unsigned long val = smb_strtoul(p,
+ NULL,
+ 0,
+ &error,
+ SMB_STR_FULL_STR_CONV);
+ if (error != 0) {
+ DEBUG(2,("interpret_interface: "
+ "can't determine netmask value from %s\n",
+ p));
+ return;
+ }
+ if (!make_netmask(&ss_mask, &ss, val)) {
+ DEBUG(2,("interpret_interface: "
+ "can't apply netmask value %lu from %s\n",
+ val,
+ p));
+ return;
+ }
+ }
+
+ make_bcast(&ss_bcast, &ss, &ss_mask);
+ make_net(&ss_net, &ss, &ss_mask);
+
+ /* Maybe the first component was a broadcast address. */
+ if (sockaddr_equal((struct sockaddr *)&ss_bcast, (struct sockaddr *)&ss) ||
+ sockaddr_equal((struct sockaddr *)&ss_net, (struct sockaddr *)&ss)) {
+ for (i=0;i<total_probed;i++) {
+ if (same_net((struct sockaddr *)&ss,
+ (struct sockaddr *)&probed_ifaces[i].ip,
+ (struct sockaddr *)&ss_mask)) {
+ /* Temporarily replace netmask on
+ * the detected interface - user knows
+ * best.... */
+ struct sockaddr_storage saved_mask =
+ probed_ifaces[i].netmask;
+ probed_ifaces[i].netmask = ss_mask;
+ DEBUG(2,("interpret_interface: "
+ "using netmask value %s from "
+ "config file on interface %s\n",
+ p,
+ probed_ifaces[i].name));
+ add_interface(mem_ctx, &probed_ifaces[i],
+ local_interfaces);
+ probed_ifaces[i].netmask = saved_mask;
+ return;
+ }
+ }
+ DEBUG(2,("interpret_interface: Can't determine ip for "
+ "broadcast address %s\n",
+ token));
+ return;
+ }
+
+ /* Just fake up the interface definition. User knows best. */
+
+ DEBUG(2,("interpret_interface: Adding interface %s\n",
+ token));
+
+ ZERO_STRUCT(ifs);
+ (void)strlcpy(ifs.name, token, sizeof(ifs.name));
+ ifs.flags = IFF_BROADCAST;
+ ifs.ip = ss;
+ ifs.netmask = ss_mask;
+ ifs.bcast = ss_bcast;
+ add_interface(mem_ctx, &ifs, local_interfaces);
+}
+
+
+/**
+load the list of network interfaces
+**/
+void load_interface_list(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx, struct interface **local_interfaces)
+{
+ const char **ptr = lpcfg_interfaces(lp_ctx);
+ int i;
+ struct iface_struct *ifaces = NULL;
+ int total_probed;
+
+ *local_interfaces = NULL;
+
+ /* probe the kernel for interfaces */
+ total_probed = get_interfaces(mem_ctx, &ifaces);
+
+ /* if we don't have a interfaces line then use all interfaces
+ except loopback */
+ if (!ptr || !*ptr || !**ptr) {
+ if (total_probed <= 0) {
+ DEBUG(0,("ERROR: Could not determine network interfaces, you must use a interfaces config line\n"));
+ }
+ for (i=0;i<total_probed;i++) {
+ if (!is_loopback_addr((struct sockaddr *)&ifaces[i].ip)) {
+ add_interface(mem_ctx, &ifaces[i], local_interfaces);
+ }
+ }
+ }
+
+ while (ptr && *ptr) {
+ interpret_interface(mem_ctx, *ptr, ifaces, total_probed, local_interfaces);
+ ptr++;
+ }
+
+ if (!*local_interfaces) {
+ DEBUG(0,("WARNING: no network interfaces found\n"));
+ }
+ talloc_free(ifaces);
+}
+
+/**
+ how many interfaces do we have
+ **/
+int iface_list_count(struct interface *ifaces)
+{
+ int ret = 0;
+ struct interface *i;
+
+ for (i=ifaces;i;i=i->next)
+ ret++;
+ return ret;
+}
+
+/**
+ return IP of the Nth interface
+ **/
+const char *iface_list_n_ip(struct interface *ifaces, int n)
+{
+ struct interface *i;
+
+ for (i=ifaces;i && n;i=i->next)
+ n--;
+
+ if (i) {
+ return i->ip_s;
+ }
+ return NULL;
+}
+
+
+/**
+ return the first IPv4 interface address we have registered
+ **/
+const char *iface_list_first_v4(struct interface *ifaces)
+{
+ struct interface *i;
+
+ for (i=ifaces; i; i=i->next) {
+ if (i->ip.ss_family == AF_INET) {
+ return i->ip_s;
+ }
+ }
+ return NULL;
+}
+
+/**
+ return the first IPv6 interface address we have registered
+ **/
+static const char *iface_list_first_v6(struct interface *ifaces)
+{
+ struct interface *i;
+
+#ifdef HAVE_IPV6
+ for (i=ifaces; i; i=i->next) {
+ if (i->ip.ss_family == AF_INET6) {
+ return i->ip_s;
+ }
+ }
+#endif
+ return NULL;
+}
+
+/**
+ check if an interface is IPv4
+ **/
+bool iface_list_n_is_v4(struct interface *ifaces, int n)
+{
+ struct interface *i;
+
+ for (i=ifaces;i && n;i=i->next)
+ n--;
+
+ if (i) {
+ return i->ip.ss_family == AF_INET;
+ }
+ return false;
+}
+
+/**
+ return bcast of the Nth interface
+ **/
+const char *iface_list_n_bcast(struct interface *ifaces, int n)
+{
+ struct interface *i;
+
+ for (i=ifaces;i && n;i=i->next)
+ n--;
+
+ if (i) {
+ return i->bcast_s;
+ }
+ return NULL;
+}
+
+/**
+ return netmask of the Nth interface
+ **/
+const char *iface_list_n_netmask(struct interface *ifaces, int n)
+{
+ struct interface *i;
+
+ for (i=ifaces;i && n;i=i->next)
+ n--;
+
+ if (i) {
+ return i->nmask_s;
+ }
+ return NULL;
+}
+
+/**
+ return the local IP address that best matches a destination IP, or
+ our first interface if none match
+*/
+const char *iface_list_best_ip(struct interface *ifaces, const char *dest)
+{
+ struct interface *iface;
+ struct sockaddr_storage ss;
+
+ if (!interpret_string_addr(&ss, dest, AI_NUMERICHOST)) {
+ return iface_list_n_ip(ifaces, 0);
+ }
+ iface = iface_list_find(ifaces, (const struct sockaddr *)&ss, true);
+ if (iface) {
+ return iface->ip_s;
+ }
+#ifdef HAVE_IPV6
+ if (ss.ss_family == AF_INET6) {
+ return iface_list_first_v6(ifaces);
+ }
+#endif
+ return iface_list_first_v4(ifaces);
+}
+
+/**
+ return true if an IP is one one of our local networks
+*/
+bool iface_list_is_local(struct interface *ifaces, const char *dest)
+{
+ struct sockaddr_storage ss;
+
+ if (!interpret_string_addr(&ss, dest, AI_NUMERICHOST)) {
+ return false;
+ }
+ if (iface_list_find(ifaces, (const struct sockaddr *)&ss, true)) {
+ return true;
+ }
+ return false;
+}
+
+/**
+ return true if a IP matches a IP/netmask pair
+*/
+bool iface_list_same_net(const char *ip1, const char *ip2, const char *netmask)
+{
+ struct sockaddr_storage ip1_ss, ip2_ss, nm_ss;
+
+ if (!interpret_string_addr(&ip1_ss, ip1, AI_NUMERICHOST)) {
+ return false;
+ }
+ if (!interpret_string_addr(&ip2_ss, ip2, AI_NUMERICHOST)) {
+ return false;
+ }
+ if (!interpret_string_addr(&nm_ss, netmask, AI_NUMERICHOST)) {
+ return false;
+ }
+
+ return same_net((struct sockaddr *)&ip1_ss,
+ (struct sockaddr *)&ip2_ss,
+ (struct sockaddr *)&nm_ss);
+}
+
+/**
+ return the list of wildcard interfaces
+ this will include the IPv4 0.0.0.0, and may include IPv6 ::
+*/
+char **iface_list_wildcard(TALLOC_CTX *mem_ctx)
+{
+ char **ret;
+#ifdef HAVE_IPV6
+ ret = str_list_make(mem_ctx, "::,0.0.0.0", NULL);
+#else
+ ret = str_list_make(mem_ctx, "0.0.0.0", NULL);
+#endif
+ return ret;
+}
diff --git a/source4/lib/socket/netif.h b/source4/lib/socket/netif.h
new file mode 100644
index 0000000..1d90a4f
--- /dev/null
+++ b/source4/lib/socket/netif.h
@@ -0,0 +1,24 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ structures for lib/netif/
+
+ Copyright (C) Andrew Tridgell 2004
+
+ 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 "system/network.h"
+#include "lib/socket/interfaces.h"
+#include "lib/socket/netif_proto.h"
diff --git a/source4/lib/socket/socket.c b/source4/lib/socket/socket.c
new file mode 100644
index 0000000..26f23f5
--- /dev/null
+++ b/source4/lib/socket/socket.c
@@ -0,0 +1,640 @@
+/*
+ Unix SMB/CIFS implementation.
+ Socket functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Tim Potter 2000-2001
+ Copyright (C) Stefan Metzmacher 2004
+
+ 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 "lib/socket/socket.h"
+#include "system/filesys.h"
+#include "system/network.h"
+#include "param/param.h"
+#include "../lib/tsocket/tsocket.h"
+#include "lib/util/util_net.h"
+
+/*
+ auto-close sockets on free
+*/
+static int socket_destructor(struct socket_context *sock)
+{
+ if (sock->ops->fn_close &&
+ !(sock->flags & SOCKET_FLAG_NOCLOSE)) {
+ sock->ops->fn_close(sock);
+ }
+ return 0;
+}
+
+_PUBLIC_ void socket_tevent_fd_close_fn(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ int fd,
+ void *private_data)
+{
+ /* this might be the socket_wrapper swrap_close() */
+ close(fd);
+}
+
+_PUBLIC_ NTSTATUS socket_create_with_ops(TALLOC_CTX *mem_ctx, const struct socket_ops *ops,
+ struct socket_context **new_sock,
+ enum socket_type type, uint32_t flags)
+{
+ NTSTATUS status;
+
+ (*new_sock) = talloc(mem_ctx, struct socket_context);
+ if (!(*new_sock)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*new_sock)->type = type;
+ (*new_sock)->state = SOCKET_STATE_UNDEFINED;
+ (*new_sock)->flags = flags;
+
+ (*new_sock)->fd = -1;
+
+ (*new_sock)->private_data = NULL;
+ (*new_sock)->ops = ops;
+ (*new_sock)->backend_name = NULL;
+
+ status = (*new_sock)->ops->fn_init((*new_sock));
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(*new_sock);
+ return status;
+ }
+
+ /* by enabling "testnonblock" mode, all socket receive and
+ send calls on non-blocking sockets will randomly recv/send
+ less data than requested */
+
+ if (type == SOCKET_TYPE_STREAM &&
+ getenv("SOCKET_TESTNONBLOCK") != NULL) {
+ (*new_sock)->flags |= SOCKET_FLAG_TESTNONBLOCK;
+ }
+
+ /* we don't do a connect() on dgram sockets, so need to set
+ non-blocking at socket create time */
+ if (type == SOCKET_TYPE_DGRAM) {
+ set_blocking(socket_get_fd(*new_sock), false);
+ }
+
+ talloc_set_destructor(*new_sock, socket_destructor);
+
+ return NT_STATUS_OK;
+}
+
+_PUBLIC_ NTSTATUS socket_create(TALLOC_CTX *mem_ctx,
+ const char *name, enum socket_type type,
+ struct socket_context **new_sock, uint32_t flags)
+{
+ const struct socket_ops *ops;
+
+ ops = socket_getops_byname(name, type);
+ if (!ops) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return socket_create_with_ops(mem_ctx, ops, new_sock, type, flags);
+}
+
+_PUBLIC_ NTSTATUS socket_connect(struct socket_context *sock,
+ const struct socket_address *my_address,
+ const struct socket_address *server_address,
+ uint32_t flags)
+{
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (sock->state != SOCKET_STATE_UNDEFINED) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!sock->ops->fn_connect) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return sock->ops->fn_connect(sock, my_address, server_address, flags);
+}
+
+_PUBLIC_ NTSTATUS socket_connect_complete(struct socket_context *sock, uint32_t flags)
+{
+ if (!sock->ops->fn_connect_complete) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return sock->ops->fn_connect_complete(sock, flags);
+}
+
+_PUBLIC_ NTSTATUS socket_listen(struct socket_context *sock,
+ const struct socket_address *my_address,
+ int queue_size, uint32_t flags)
+{
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (sock->state != SOCKET_STATE_UNDEFINED) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!sock->ops->fn_listen) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return sock->ops->fn_listen(sock, my_address, queue_size, flags);
+}
+
+_PUBLIC_ NTSTATUS socket_accept(struct socket_context *sock, struct socket_context **new_sock)
+{
+ NTSTATUS status;
+
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (sock->type != SOCKET_TYPE_STREAM) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (sock->state != SOCKET_STATE_SERVER_LISTEN) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!sock->ops->fn_accept) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ status = sock->ops->fn_accept(sock, new_sock);
+
+ if (NT_STATUS_IS_OK(status)) {
+ talloc_set_destructor(*new_sock, socket_destructor);
+ (*new_sock)->flags = 0;
+ }
+
+ return status;
+}
+
+_PUBLIC_ NTSTATUS socket_recv(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread)
+{
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
+ sock->state != SOCKET_STATE_SERVER_CONNECTED &&
+ sock->type != SOCKET_TYPE_DGRAM) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!sock->ops->fn_recv) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK)
+ && wantlen > 1) {
+
+ if (random() % 10 == 0) {
+ *nread = 0;
+ return STATUS_MORE_ENTRIES;
+ }
+ return sock->ops->fn_recv(sock, buf, 1+(random() % wantlen), nread);
+ }
+ return sock->ops->fn_recv(sock, buf, wantlen, nread);
+}
+
+_PUBLIC_ NTSTATUS socket_recvfrom(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread,
+ TALLOC_CTX *mem_ctx, struct socket_address **src_addr)
+{
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (sock->type != SOCKET_TYPE_DGRAM) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!sock->ops->fn_recvfrom) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return sock->ops->fn_recvfrom(sock, buf, wantlen, nread,
+ mem_ctx, src_addr);
+}
+
+_PUBLIC_ NTSTATUS socket_send(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen)
+{
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
+ sock->state != SOCKET_STATE_SERVER_CONNECTED) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!sock->ops->fn_send) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK)
+ && blob->length > 1) {
+ DATA_BLOB blob2 = *blob;
+ if (random() % 10 == 0) {
+ *sendlen = 0;
+ return STATUS_MORE_ENTRIES;
+ }
+ /* The random size sends are incompatible with TLS and SASL
+ * sockets, which require re-sends to be consistant */
+ if (!(sock->flags & SOCKET_FLAG_ENCRYPT)) {
+ blob2.length = 1+(random() % blob2.length);
+ } else {
+ /* This is particularly stressful on buggy
+ * LDAP clients, that don't expect on LDAP
+ * packet in many SASL packets */
+ blob2.length = 1 + blob2.length/2;
+ }
+ return sock->ops->fn_send(sock, &blob2, sendlen);
+ }
+ return sock->ops->fn_send(sock, blob, sendlen);
+}
+
+
+_PUBLIC_ NTSTATUS socket_sendto(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen,
+ const struct socket_address *dest_addr)
+{
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (sock->type != SOCKET_TYPE_DGRAM) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (sock->state == SOCKET_STATE_CLIENT_CONNECTED ||
+ sock->state == SOCKET_STATE_SERVER_CONNECTED) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!sock->ops->fn_sendto) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return sock->ops->fn_sendto(sock, blob, sendlen, dest_addr);
+}
+
+
+/*
+ ask for the number of bytes in a pending incoming packet
+*/
+_PUBLIC_ NTSTATUS socket_pending(struct socket_context *sock, size_t *npending)
+{
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (!sock->ops->fn_pending) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return sock->ops->fn_pending(sock, npending);
+}
+
+
+_PUBLIC_ NTSTATUS socket_set_option(struct socket_context *sock, const char *option, const char *val)
+{
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (!sock->ops->fn_set_option) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return sock->ops->fn_set_option(sock, option, val);
+}
+
+_PUBLIC_ char *socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ if (!sock->ops->fn_get_peer_name) {
+ return NULL;
+ }
+
+ return sock->ops->fn_get_peer_name(sock, mem_ctx);
+}
+
+_PUBLIC_ struct socket_address *socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ if (!sock->ops->fn_get_peer_addr) {
+ return NULL;
+ }
+
+ return sock->ops->fn_get_peer_addr(sock, mem_ctx);
+}
+
+_PUBLIC_ struct socket_address *socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ if (!sock->ops->fn_get_my_addr) {
+ return NULL;
+ }
+
+ return sock->ops->fn_get_my_addr(sock, mem_ctx);
+}
+
+_PUBLIC_ struct tsocket_address *socket_address_to_tsocket_address(TALLOC_CTX *mem_ctx,
+ const struct socket_address *a)
+{
+ struct tsocket_address *r;
+ int ret;
+
+ if (!a) {
+ return NULL;
+ }
+ if (a->sockaddr) {
+ ret = tsocket_address_bsd_from_sockaddr(mem_ctx,
+ a->sockaddr,
+ a->sockaddrlen,
+ &r);
+ } else {
+ ret = tsocket_address_inet_from_strings(mem_ctx,
+ a->family,
+ a->addr,
+ a->port,
+ &r);
+ }
+
+ if (ret != 0) {
+ return NULL;
+ }
+
+ return r;
+}
+
+_PUBLIC_ void socket_address_set_port(struct socket_address *a,
+ uint16_t port)
+{
+ if (a->sockaddr) {
+ set_sockaddr_port(a->sockaddr, port);
+ } else {
+ a->port = port;
+ }
+
+}
+
+_PUBLIC_ struct socket_address *tsocket_address_to_socket_address(TALLOC_CTX *mem_ctx,
+ const struct tsocket_address *a)
+{
+ ssize_t ret;
+ struct sockaddr_storage ss;
+ size_t sslen = sizeof(ss);
+
+ ret = tsocket_address_bsd_sockaddr(a, (struct sockaddr *)(void *)&ss, sslen);
+ if (ret < 0) {
+ return NULL;
+ }
+
+ return socket_address_from_sockaddr(mem_ctx, (struct sockaddr *)(void *)&ss, ret);
+}
+
+_PUBLIC_ struct tsocket_address *socket_get_remote_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct socket_address *a;
+ struct tsocket_address *r;
+
+ a = socket_get_peer_addr(sock, mem_ctx);
+ if (a == NULL) {
+ return NULL;
+ }
+
+ r = socket_address_to_tsocket_address(mem_ctx, a);
+ talloc_free(a);
+ return r;
+}
+
+_PUBLIC_ struct tsocket_address *socket_get_local_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct socket_address *a;
+ struct tsocket_address *r;
+
+ a = socket_get_my_addr(sock, mem_ctx);
+ if (a == NULL) {
+ return NULL;
+ }
+
+ r = socket_address_to_tsocket_address(mem_ctx, a);
+ talloc_free(a);
+ return r;
+}
+
+_PUBLIC_ int socket_get_fd(struct socket_context *sock)
+{
+ if (!sock->ops->fn_get_fd) {
+ return -1;
+ }
+
+ return sock->ops->fn_get_fd(sock);
+}
+
+/*
+ call dup() on a socket, and close the old fd. This is used to change
+ the fd to the lowest available number, to make select() more
+ efficient (select speed depends on the maxiumum fd number passed to
+ it)
+*/
+_PUBLIC_ NTSTATUS socket_dup(struct socket_context *sock)
+{
+ int fd;
+ if (sock->fd == -1) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ fd = dup(sock->fd);
+ if (fd == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+ close(sock->fd);
+ sock->fd = fd;
+ return NT_STATUS_OK;
+
+}
+
+/* Create a new socket_address. The type must match the socket type.
+ * The host parameter may be an IP or a hostname
+ */
+
+_PUBLIC_ struct socket_address *socket_address_from_strings(TALLOC_CTX *mem_ctx,
+ const char *family,
+ const char *host,
+ int port)
+{
+ struct socket_address *addr = talloc(mem_ctx, struct socket_address);
+ if (!addr) {
+ return NULL;
+ }
+
+ if (strcmp(family, "ip") == 0 && is_ipaddress_v6(host)) {
+ /* leaving as "ip" would force IPv4 */
+ family = "ipv6";
+ }
+
+ addr->family = family;
+ addr->addr = talloc_strdup(addr, host);
+ if (!addr->addr) {
+ talloc_free(addr);
+ return NULL;
+ }
+ addr->port = port;
+ addr->sockaddr = NULL;
+ addr->sockaddrlen = 0;
+
+ return addr;
+}
+
+/* Create a new socket_address. Copy the struct sockaddr into the new
+ * structure. Used for hooks in the kerberos libraries, where they
+ * supply only a struct sockaddr */
+
+_PUBLIC_ struct socket_address *socket_address_from_sockaddr(TALLOC_CTX *mem_ctx,
+ struct sockaddr *sockaddr,
+ size_t sockaddrlen)
+{
+ struct socket_address *addr = talloc(mem_ctx, struct socket_address);
+ if (!addr) {
+ return NULL;
+ }
+ switch (sockaddr->sa_family) {
+ case AF_INET:
+ addr->family = "ipv4";
+ break;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ addr->family = "ipv6";
+ break;
+#endif
+ case AF_UNIX:
+ addr->family = "unix";
+ break;
+ }
+ addr->addr = NULL;
+ addr->port = 0;
+ addr->sockaddr = (struct sockaddr *)talloc_memdup(addr, sockaddr, sockaddrlen);
+ if (!addr->sockaddr) {
+ talloc_free(addr);
+ return NULL;
+ }
+ addr->sockaddrlen = sockaddrlen;
+ return addr;
+}
+
+
+/*
+ Create a new socket_address from sockaddr_storage
+ */
+_PUBLIC_ struct socket_address *socket_address_from_sockaddr_storage(TALLOC_CTX *mem_ctx,
+ const struct sockaddr_storage *sockaddr,
+ uint16_t port)
+{
+ struct socket_address *addr = talloc_zero(mem_ctx, struct socket_address);
+ char addr_str[INET6_ADDRSTRLEN+1];
+ const char *str;
+
+ if (!addr) {
+ return NULL;
+ }
+ addr->port = port;
+ switch (sockaddr->ss_family) {
+ case AF_INET:
+ addr->family = "ipv4";
+ break;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ addr->family = "ipv6";
+ break;
+#endif
+ default:
+ talloc_free(addr);
+ return NULL;
+ }
+
+ str = print_sockaddr(addr_str, sizeof(addr_str), sockaddr);
+ if (str == NULL) {
+ talloc_free(addr);
+ return NULL;
+ }
+ addr->addr = talloc_strdup(addr, str);
+ if (addr->addr == NULL) {
+ talloc_free(addr);
+ return NULL;
+ }
+
+ return addr;
+}
+
+/* Copy a socket_address structure */
+struct socket_address *socket_address_copy(TALLOC_CTX *mem_ctx,
+ const struct socket_address *oaddr)
+{
+ struct socket_address *addr = talloc_zero(mem_ctx, struct socket_address);
+ if (!addr) {
+ return NULL;
+ }
+ addr->family = oaddr->family;
+ if (oaddr->addr) {
+ addr->addr = talloc_strdup(addr, oaddr->addr);
+ if (!addr->addr) {
+ goto nomem;
+ }
+ }
+ addr->port = oaddr->port;
+ if (oaddr->sockaddr) {
+ addr->sockaddr = (struct sockaddr *)talloc_memdup(addr,
+ oaddr->sockaddr,
+ oaddr->sockaddrlen);
+ if (!addr->sockaddr) {
+ goto nomem;
+ }
+ addr->sockaddrlen = oaddr->sockaddrlen;
+ }
+
+ return addr;
+
+nomem:
+ talloc_free(addr);
+ return NULL;
+}
+
+_PUBLIC_ const struct socket_ops *socket_getops_byname(const char *family, enum socket_type type)
+{
+ extern const struct socket_ops *socket_ipv4_ops(enum socket_type);
+ extern const struct socket_ops *socket_ipv6_ops(enum socket_type);
+ extern const struct socket_ops *socket_unixdom_ops(enum socket_type);
+
+ if (strcmp("ip", family) == 0 ||
+ strcmp("ipv4", family) == 0) {
+ return socket_ipv4_ops(type);
+ }
+
+#ifdef HAVE_IPV6
+ if (strcmp("ipv6", family) == 0) {
+ return socket_ipv6_ops(type);
+ }
+#endif
+
+ if (strcmp("unix", family) == 0) {
+ return socket_unixdom_ops(type);
+ }
+
+ return NULL;
+}
+
+/*
+ set some flags on a socket
+ */
+void socket_set_flags(struct socket_context *sock, unsigned flags)
+{
+ sock->flags |= flags;
+}
diff --git a/source4/lib/socket/socket.h b/source4/lib/socket/socket.h
new file mode 100644
index 0000000..d0fb5e0
--- /dev/null
+++ b/source4/lib/socket/socket.h
@@ -0,0 +1,256 @@
+/*
+ Unix SMB/CIFS implementation.
+ Socket functions
+ Copyright (C) Stefan Metzmacher 2004
+
+ 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 _SAMBA_SOCKET_H
+#define _SAMBA_SOCKET_H
+
+struct tevent_context;
+struct tevent_fd;
+struct socket_context;
+
+enum socket_type {
+ SOCKET_TYPE_STREAM,
+ SOCKET_TYPE_DGRAM
+};
+
+struct socket_address {
+ const char *family;
+ char *addr;
+ int port;
+ struct sockaddr *sockaddr;
+ size_t sockaddrlen;
+};
+
+struct socket_ops {
+ const char *name;
+
+ NTSTATUS (*fn_init)(struct socket_context *sock);
+
+ /* client ops */
+ NTSTATUS (*fn_connect)(struct socket_context *sock,
+ const struct socket_address *my_address,
+ const struct socket_address *server_address,
+ uint32_t flags);
+
+ /* complete a non-blocking connect */
+ NTSTATUS (*fn_connect_complete)(struct socket_context *sock,
+ uint32_t flags);
+
+ /* server ops */
+ NTSTATUS (*fn_listen)(struct socket_context *sock,
+ const struct socket_address *my_address,
+ int queue_size, uint32_t flags);
+ NTSTATUS (*fn_accept)(struct socket_context *sock,
+ struct socket_context **new_sock);
+
+ /* general ops */
+ NTSTATUS (*fn_recv)(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread);
+ NTSTATUS (*fn_send)(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen);
+
+ NTSTATUS (*fn_sendto)(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen,
+ const struct socket_address *dest_addr);
+ NTSTATUS (*fn_recvfrom)(struct socket_context *sock,
+ void *buf, size_t wantlen, size_t *nread,
+ TALLOC_CTX *addr_ctx, struct socket_address **src_addr);
+ NTSTATUS (*fn_pending)(struct socket_context *sock, size_t *npending);
+
+ void (*fn_close)(struct socket_context *sock);
+
+ NTSTATUS (*fn_set_option)(struct socket_context *sock, const char *option, const char *val);
+
+ char *(*fn_get_peer_name)(struct socket_context *sock, TALLOC_CTX *mem_ctx);
+ struct socket_address *(*fn_get_peer_addr)(struct socket_context *sock, TALLOC_CTX *mem_ctx);
+ struct socket_address *(*fn_get_my_addr)(struct socket_context *sock, TALLOC_CTX *mem_ctx);
+
+ int (*fn_get_fd)(struct socket_context *sock);
+};
+
+enum socket_state {
+ SOCKET_STATE_UNDEFINED,
+
+ SOCKET_STATE_CLIENT_START,
+ SOCKET_STATE_CLIENT_CONNECTED,
+ SOCKET_STATE_CLIENT_STARTTLS,
+ SOCKET_STATE_CLIENT_ERROR,
+
+ SOCKET_STATE_SERVER_LISTEN,
+ SOCKET_STATE_SERVER_CONNECTED,
+ SOCKET_STATE_SERVER_STARTTLS,
+ SOCKET_STATE_SERVER_ERROR
+};
+
+#define SOCKET_FLAG_PEEK 0x00000002
+#define SOCKET_FLAG_TESTNONBLOCK 0x00000004
+#define SOCKET_FLAG_ENCRYPT 0x00000008 /* This socket
+ * implementation requires
+ * that re-sends be
+ * consistant, because it
+ * is encrypting data.
+ * This modifies the
+ * TESTNONBLOCK case */
+#define SOCKET_FLAG_NOCLOSE 0x00000010 /* don't auto-close on free */
+
+
+struct socket_context {
+ enum socket_type type;
+ enum socket_state state;
+ uint32_t flags;
+
+ int fd;
+
+ void *private_data;
+ const struct socket_ops *ops;
+ const char *backend_name;
+
+ /* specific to the ip backend */
+ int family;
+};
+
+struct resolve_context;
+struct tsocket_address;
+
+/* prototypes */
+NTSTATUS socket_create_with_ops(TALLOC_CTX *mem_ctx, const struct socket_ops *ops,
+ struct socket_context **new_sock,
+ enum socket_type type, uint32_t flags);
+NTSTATUS socket_create(TALLOC_CTX *mem_ctx,
+ const char *name, enum socket_type type,
+ struct socket_context **new_sock, uint32_t flags);
+NTSTATUS socket_connect(struct socket_context *sock,
+ const struct socket_address *my_address,
+ const struct socket_address *server_address,
+ uint32_t flags);
+NTSTATUS socket_connect_complete(struct socket_context *sock, uint32_t flags);
+NTSTATUS socket_listen(struct socket_context *sock,
+ const struct socket_address *my_address,
+ int queue_size, uint32_t flags);
+NTSTATUS socket_accept(struct socket_context *sock, struct socket_context **new_sock);
+NTSTATUS socket_recv(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread);
+NTSTATUS socket_recvfrom(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread,
+ TALLOC_CTX *addr_ctx, struct socket_address **src_addr);
+NTSTATUS socket_send(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen);
+NTSTATUS socket_sendto(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen,
+ const struct socket_address *dest_addr);
+NTSTATUS socket_pending(struct socket_context *sock, size_t *npending);
+NTSTATUS socket_set_option(struct socket_context *sock, const char *option, const char *val);
+char *socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx);
+struct socket_address *socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx);
+struct socket_address *socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx);
+struct tsocket_address *socket_address_to_tsocket_address(TALLOC_CTX *mem_ctx,
+ const struct socket_address *a);
+struct socket_address *tsocket_address_to_socket_address(TALLOC_CTX *mem_ctx,
+ const struct tsocket_address *a);
+struct tsocket_address *socket_get_remote_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx);
+struct tsocket_address *socket_get_local_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx);
+int socket_get_fd(struct socket_context *sock);
+NTSTATUS socket_dup(struct socket_context *sock);
+struct socket_address *socket_address_from_strings(TALLOC_CTX *mem_ctx,
+ const char *type,
+ const char *host,
+ int port);
+struct socket_address *socket_address_from_sockaddr(TALLOC_CTX *mem_ctx,
+ struct sockaddr *sockaddr,
+ size_t addrlen);
+struct sockaddr_storage;
+struct socket_address *socket_address_from_sockaddr_storage(TALLOC_CTX *mem_ctx,
+ const struct sockaddr_storage *sockaddr,
+ uint16_t port);
+_PUBLIC_ void socket_address_set_port(struct socket_address *a,
+ uint16_t port);
+struct socket_address *socket_address_copy(TALLOC_CTX *mem_ctx,
+ const struct socket_address *oaddr);
+const struct socket_ops *socket_getops_byname(const char *name, enum socket_type type);
+bool socket_check_access(struct socket_context *sock,
+ const char *service_name,
+ const char **allow_list, const char **deny_list);
+
+struct composite_context *socket_connect_send(struct socket_context *sock,
+ struct socket_address *my_address,
+ struct socket_address *server_address,
+ uint32_t flags,
+ struct tevent_context *event_ctx);
+NTSTATUS socket_connect_recv(struct composite_context *ctx);
+NTSTATUS socket_connect_ev(struct socket_context *sock,
+ struct socket_address *my_address,
+ struct socket_address *server_address,
+ uint32_t flags,
+ struct tevent_context *ev);
+
+struct socket_connect_multi_ex {
+ void *private_data;
+ struct tevent_req *(*establish_send)(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct socket_context *sock,
+ struct socket_address *addr,
+ void *private_data);
+ NTSTATUS (*establish_recv)(struct tevent_req *req);
+};
+struct composite_context *socket_connect_multi_ex_send(TALLOC_CTX *mem_ctx,
+ const char *server_address,
+ int num_server_ports,
+ uint16_t *server_ports,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx,
+ struct socket_connect_multi_ex *ex);
+NTSTATUS socket_connect_multi_ex_recv(struct composite_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct socket_context **result,
+ uint16_t *port);
+NTSTATUS socket_connect_multi_ex(TALLOC_CTX *mem_ctx, const char *server_address,
+ int num_server_ports, uint16_t *server_ports,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx,
+ struct socket_connect_multi_ex *ex,
+ struct socket_context **result,
+ uint16_t *port);
+
+struct composite_context *socket_connect_multi_send(TALLOC_CTX *mem_ctx,
+ const char *server_address,
+ int num_server_ports,
+ uint16_t *server_ports,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx);
+NTSTATUS socket_connect_multi_recv(struct composite_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct socket_context **result,
+ uint16_t *port);
+NTSTATUS socket_connect_multi(TALLOC_CTX *mem_ctx, const char *server_address,
+ int num_server_ports, uint16_t *server_ports,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx,
+ struct socket_context **result,
+ uint16_t *port);
+void set_socket_options(int fd, const char *options);
+void socket_set_flags(struct socket_context *sock, unsigned flags);
+
+void socket_tevent_fd_close_fn(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ int fd,
+ void *private_data);
+
+extern bool testnonblock;
+
+#endif /* _SAMBA_SOCKET_H */
diff --git a/source4/lib/socket/socket_ip.c b/source4/lib/socket/socket_ip.c
new file mode 100644
index 0000000..62dbf1d
--- /dev/null
+++ b/source4/lib/socket/socket_ip.c
@@ -0,0 +1,1033 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Socket IPv4/IPv6 functions
+
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Andrew Tridgell 2004-2005
+ Copyright (C) Jelmer Vernooij 2004
+
+ 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 "lib/socket/socket.h"
+#include "system/network.h"
+#include "lib/util/util_net.h"
+
+#undef strcasecmp
+
+_PUBLIC_ const struct socket_ops *socket_ipv4_ops(enum socket_type type);
+_PUBLIC_ const struct socket_ops *socket_ipv6_ops(enum socket_type type);
+
+static NTSTATUS ipv4_init(struct socket_context *sock)
+{
+ int type;
+
+ switch (sock->type) {
+ case SOCKET_TYPE_STREAM:
+ type = SOCK_STREAM;
+ break;
+ case SOCKET_TYPE_DGRAM:
+ type = SOCK_DGRAM;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ sock->fd = socket(PF_INET, type, 0);
+ if (sock->fd == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ smb_set_close_on_exec(sock->fd);
+
+ sock->backend_name = "ipv4";
+ sock->family = AF_INET;
+
+ return NT_STATUS_OK;
+}
+
+static void ip_close(struct socket_context *sock)
+{
+ if (sock->fd != -1) {
+ close(sock->fd);
+ sock->fd = -1;
+ }
+}
+
+static NTSTATUS ip_connect_complete(struct socket_context *sock, uint32_t flags)
+{
+ int error=0, ret;
+ socklen_t len = sizeof(error);
+
+ /* check for any errors that may have occurred - this is needed
+ for non-blocking connect */
+ ret = getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &error, &len);
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+ if (error != 0) {
+ return map_nt_error_from_unix_common(error);
+ }
+
+ ret = set_blocking(sock->fd, false);
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ sock->state = SOCKET_STATE_CLIENT_CONNECTED;
+
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS ipv4_connect(struct socket_context *sock,
+ const struct socket_address *my_address,
+ const struct socket_address *srv_address,
+ uint32_t flags)
+{
+ struct sockaddr_in srv_addr;
+ struct in_addr my_ip;
+ struct in_addr srv_ip;
+ int ret;
+
+ if (my_address && my_address->sockaddr) {
+ ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+ } else if (my_address) {
+ my_ip = interpret_addr2(my_address->addr);
+
+ if (my_ip.s_addr != 0 || my_address->port != 0) {
+ struct sockaddr_in my_addr;
+ ZERO_STRUCT(my_addr);
+#ifdef HAVE_SOCK_SIN_LEN
+ my_addr.sin_len = sizeof(my_addr);
+#endif
+ my_addr.sin_addr.s_addr = my_ip.s_addr;
+ my_addr.sin_port = htons(my_address->port);
+ my_addr.sin_family = PF_INET;
+
+ ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+ }
+ }
+
+ if (srv_address->sockaddr) {
+ ret = connect(sock->fd, srv_address->sockaddr, srv_address->sockaddrlen);
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+ } else {
+ srv_ip = interpret_addr2(srv_address->addr);
+ if (!srv_ip.s_addr) {
+ return NT_STATUS_BAD_NETWORK_NAME;
+ }
+
+ SMB_ASSERT(srv_address->port != 0);
+
+ ZERO_STRUCT(srv_addr);
+#ifdef HAVE_SOCK_SIN_LEN
+ srv_addr.sin_len = sizeof(srv_addr);
+#endif
+ srv_addr.sin_addr.s_addr= srv_ip.s_addr;
+ srv_addr.sin_port = htons(srv_address->port);
+ srv_addr.sin_family = PF_INET;
+
+ ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+ }
+
+ return ip_connect_complete(sock, flags);
+}
+
+
+/*
+ note that for simplicity of the API, socket_listen() is also
+ use for DGRAM sockets, but in reality only a bind() is done
+*/
+static NTSTATUS ipv4_listen(struct socket_context *sock,
+ const struct socket_address *my_address,
+ int queue_size, uint32_t flags)
+{
+ struct sockaddr_in my_addr;
+ struct in_addr ip_addr;
+ int ret;
+
+ socket_set_option(sock, "SO_REUSEADDR=1", NULL);
+
+ if (my_address->sockaddr) {
+ ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
+ } else {
+ ip_addr = interpret_addr2(my_address->addr);
+
+ ZERO_STRUCT(my_addr);
+#ifdef HAVE_SOCK_SIN_LEN
+ my_addr.sin_len = sizeof(my_addr);
+#endif
+ my_addr.sin_addr.s_addr = ip_addr.s_addr;
+ my_addr.sin_port = htons(my_address->port);
+ my_addr.sin_family = PF_INET;
+
+ ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
+ }
+
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ if (sock->type == SOCKET_TYPE_STREAM) {
+ ret = listen(sock->fd, queue_size);
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+ }
+
+ ret = set_blocking(sock->fd, false);
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ sock->state= SOCKET_STATE_SERVER_LISTEN;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ipv4_accept(struct socket_context *sock, struct socket_context **new_sock)
+{
+ struct sockaddr_in cli_addr;
+ socklen_t cli_addr_len = sizeof(cli_addr);
+ int new_fd, ret;
+
+ if (sock->type != SOCKET_TYPE_STREAM) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
+ if (new_fd == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ ret = set_blocking(new_fd, false);
+ if (ret == -1) {
+ close(new_fd);
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ smb_set_close_on_exec(new_fd);
+
+
+ /* TODO: we could add a 'accept_check' hook here
+ * which get the black/white lists via socket_set_accept_filter()
+ * or something like that
+ * --metze
+ */
+
+ (*new_sock) = talloc(NULL, struct socket_context);
+ if (!(*new_sock)) {
+ close(new_fd);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* copy the socket_context */
+ (*new_sock)->type = sock->type;
+ (*new_sock)->state = SOCKET_STATE_SERVER_CONNECTED;
+ (*new_sock)->flags = sock->flags;
+
+ (*new_sock)->fd = new_fd;
+
+ (*new_sock)->private_data = NULL;
+ (*new_sock)->ops = sock->ops;
+ (*new_sock)->backend_name = sock->backend_name;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ip_recv(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread)
+{
+ ssize_t gotlen;
+
+ *nread = 0;
+
+ gotlen = recv(sock->fd, buf, wantlen, 0);
+ if (gotlen == 0) {
+ return NT_STATUS_END_OF_FILE;
+ } else if (gotlen == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ *nread = gotlen;
+
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS ipv4_recvfrom(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread,
+ TALLOC_CTX *addr_ctx, struct socket_address **_src)
+{
+ ssize_t gotlen;
+ struct sockaddr_in *from_addr;
+ socklen_t from_len = sizeof(*from_addr);
+ struct socket_address *src;
+ char addrstring[INET_ADDRSTRLEN];
+
+ src = talloc(addr_ctx, struct socket_address);
+ if (!src) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ src->family = sock->backend_name;
+
+ from_addr = talloc(src, struct sockaddr_in);
+ if (!from_addr) {
+ talloc_free(src);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ src->sockaddr = (struct sockaddr *)from_addr;
+
+ *nread = 0;
+
+ gotlen = recvfrom(sock->fd, buf, wantlen, 0,
+ src->sockaddr, &from_len);
+ if (gotlen == 0) {
+ talloc_free(src);
+ return NT_STATUS_END_OF_FILE;
+ }
+ if (gotlen == -1) {
+ talloc_free(src);
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ src->sockaddrlen = from_len;
+
+ if (inet_ntop(AF_INET, &from_addr->sin_addr, addrstring,
+ sizeof(addrstring)) == NULL) {
+ talloc_free(src);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ src->addr = talloc_strdup(src, addrstring);
+ if (src->addr == NULL) {
+ talloc_free(src);
+ return NT_STATUS_NO_MEMORY;
+ }
+ src->port = ntohs(from_addr->sin_port);
+
+ *nread = gotlen;
+ *_src = src;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ip_send(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen)
+{
+ ssize_t len;
+
+ *sendlen = 0;
+
+ len = send(sock->fd, blob->data, blob->length, 0);
+ if (len == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ *sendlen = len;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ipv4_sendto(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen,
+ const struct socket_address *dest_addr)
+{
+ ssize_t len;
+
+ if (dest_addr->sockaddr) {
+ len = sendto(sock->fd, blob->data, blob->length, 0,
+ dest_addr->sockaddr, dest_addr->sockaddrlen);
+ } else {
+ struct sockaddr_in srv_addr;
+ struct in_addr addr;
+
+ SMB_ASSERT(dest_addr->port != 0);
+
+ ZERO_STRUCT(srv_addr);
+#ifdef HAVE_SOCK_SIN_LEN
+ srv_addr.sin_len = sizeof(srv_addr);
+#endif
+ addr = interpret_addr2(dest_addr->addr);
+ if (addr.s_addr == 0) {
+ return NT_STATUS_HOST_UNREACHABLE;
+ }
+ srv_addr.sin_addr.s_addr = addr.s_addr;
+ srv_addr.sin_port = htons(dest_addr->port);
+ srv_addr.sin_family = PF_INET;
+
+ *sendlen = 0;
+
+ len = sendto(sock->fd, blob->data, blob->length, 0,
+ (struct sockaddr *)&srv_addr, sizeof(srv_addr));
+ }
+ if (len == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ *sendlen = len;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ipv4_set_option(struct socket_context *sock, const char *option, const char *val)
+{
+ set_socket_options(sock->fd, option);
+ return NT_STATUS_OK;
+}
+
+static char *ipv4_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct sockaddr_in peer_addr;
+ socklen_t len = sizeof(peer_addr);
+ struct hostent *he;
+ int ret;
+
+ ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
+ if (ret == -1) {
+ return NULL;
+ }
+
+ he = gethostbyaddr((char *)&peer_addr.sin_addr, sizeof(peer_addr.sin_addr), AF_INET);
+ if (he == NULL) {
+ return NULL;
+ }
+
+ return talloc_strdup(mem_ctx, he->h_name);
+}
+
+static struct socket_address *ipv4_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct sockaddr_in *peer_addr;
+ socklen_t len = sizeof(*peer_addr);
+ struct socket_address *peer;
+ char addrstring[INET_ADDRSTRLEN];
+ int ret;
+
+ peer = talloc(mem_ctx, struct socket_address);
+ if (!peer) {
+ return NULL;
+ }
+
+ peer->family = sock->backend_name;
+ peer_addr = talloc(peer, struct sockaddr_in);
+ if (!peer_addr) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ peer->sockaddr = (struct sockaddr *)peer_addr;
+
+ ret = getpeername(sock->fd, peer->sockaddr, &len);
+ if (ret == -1) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ peer->sockaddrlen = len;
+
+ if (inet_ntop(AF_INET, &peer_addr->sin_addr, addrstring,
+ sizeof(addrstring)) == NULL) {
+ talloc_free(peer);
+ return NULL;
+ }
+ peer->addr = talloc_strdup(peer, addrstring);
+ if (!peer->addr) {
+ talloc_free(peer);
+ return NULL;
+ }
+ peer->port = ntohs(peer_addr->sin_port);
+
+ return peer;
+}
+
+static struct socket_address *ipv4_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct sockaddr_in *local_addr;
+ socklen_t len = sizeof(*local_addr);
+ struct socket_address *local;
+ char addrstring[INET_ADDRSTRLEN];
+ int ret;
+
+ local = talloc(mem_ctx, struct socket_address);
+ if (!local) {
+ return NULL;
+ }
+
+ local->family = sock->backend_name;
+ local_addr = talloc(local, struct sockaddr_in);
+ if (!local_addr) {
+ talloc_free(local);
+ return NULL;
+ }
+
+ local->sockaddr = (struct sockaddr *)local_addr;
+
+ ret = getsockname(sock->fd, local->sockaddr, &len);
+ if (ret == -1) {
+ talloc_free(local);
+ return NULL;
+ }
+
+ local->sockaddrlen = len;
+
+ if (inet_ntop(AF_INET, &local_addr->sin_addr, addrstring,
+ sizeof(addrstring)) == NULL) {
+ talloc_free(local);
+ return NULL;
+ }
+ local->addr = talloc_strdup(local, addrstring);
+ if (!local->addr) {
+ talloc_free(local);
+ return NULL;
+ }
+ local->port = ntohs(local_addr->sin_port);
+
+ return local;
+}
+static int ip_get_fd(struct socket_context *sock)
+{
+ return sock->fd;
+}
+
+static NTSTATUS ip_pending(struct socket_context *sock, size_t *npending)
+{
+ int value = 0;
+ if (ioctl(sock->fd, FIONREAD, &value) == 0) {
+ *npending = value;
+ return NT_STATUS_OK;
+ }
+ return map_nt_error_from_unix_common(errno);
+}
+
+static const struct socket_ops ipv4_ops = {
+ .name = "ipv4",
+ .fn_init = ipv4_init,
+ .fn_connect = ipv4_connect,
+ .fn_connect_complete = ip_connect_complete,
+ .fn_listen = ipv4_listen,
+ .fn_accept = ipv4_accept,
+ .fn_recv = ip_recv,
+ .fn_recvfrom = ipv4_recvfrom,
+ .fn_send = ip_send,
+ .fn_sendto = ipv4_sendto,
+ .fn_pending = ip_pending,
+ .fn_close = ip_close,
+
+ .fn_set_option = ipv4_set_option,
+
+ .fn_get_peer_name = ipv4_get_peer_name,
+ .fn_get_peer_addr = ipv4_get_peer_addr,
+ .fn_get_my_addr = ipv4_get_my_addr,
+
+ .fn_get_fd = ip_get_fd
+};
+
+_PUBLIC_ const struct socket_ops *socket_ipv4_ops(enum socket_type type)
+{
+ return &ipv4_ops;
+}
+
+#ifdef HAVE_IPV6
+
+static struct in6_addr interpret_addr6(const char *name)
+{
+ char addr[INET6_ADDRSTRLEN];
+ struct in6_addr dest6;
+ const char *sp = name;
+ char *p;
+ int ret;
+
+ if (sp == NULL) return in6addr_any;
+
+ p = strchr_m(sp, '%');
+
+ if (strcasecmp(sp, "localhost") == 0) {
+ sp = "::1";
+ }
+
+ /*
+ * Cope with link-local.
+ * This is IP:v6:addr%ifname.
+ */
+
+ if (p && (p > sp) && (if_nametoindex(p+1) != 0)) {
+ strlcpy(addr, sp,
+ MIN(PTR_DIFF(p,sp)+1,
+ sizeof(addr)));
+ sp = addr;
+ }
+
+ ret = inet_pton(AF_INET6, sp, &dest6);
+ if (ret > 0) {
+ return dest6;
+ }
+
+ return in6addr_any;
+}
+
+static NTSTATUS ipv6_init(struct socket_context *sock)
+{
+ int type;
+
+ switch (sock->type) {
+ case SOCKET_TYPE_STREAM:
+ type = SOCK_STREAM;
+ break;
+ case SOCKET_TYPE_DGRAM:
+ type = SOCK_DGRAM;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ sock->fd = socket(PF_INET6, type, 0);
+ if (sock->fd == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ smb_set_close_on_exec(sock->fd);
+
+ sock->backend_name = "ipv6";
+ sock->family = AF_INET6;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ipv6_tcp_connect(struct socket_context *sock,
+ const struct socket_address *my_address,
+ const struct socket_address *srv_address,
+ uint32_t flags)
+{
+ int ret;
+
+ if (my_address && my_address->sockaddr) {
+ ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+ } else if (my_address) {
+ struct in6_addr my_ip;
+ my_ip = interpret_addr6(my_address->addr);
+
+ if (memcmp(&my_ip, &in6addr_any, sizeof(my_ip)) || my_address->port != 0) {
+ struct sockaddr_in6 my_addr;
+ ZERO_STRUCT(my_addr);
+ my_addr.sin6_addr = my_ip;
+ my_addr.sin6_port = htons(my_address->port);
+ my_addr.sin6_family = PF_INET6;
+
+ ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+ }
+ }
+
+ if (srv_address->sockaddr) {
+ ret = connect(sock->fd, srv_address->sockaddr, srv_address->sockaddrlen);
+ } else {
+ struct in6_addr srv_ip;
+ struct sockaddr_in6 srv_addr;
+ srv_ip = interpret_addr6(srv_address->addr);
+ if (memcmp(&srv_ip, &in6addr_any, sizeof(srv_ip)) == 0) {
+ return NT_STATUS_BAD_NETWORK_NAME;
+ }
+
+ ZERO_STRUCT(srv_addr);
+ srv_addr.sin6_addr = srv_ip;
+ srv_addr.sin6_port = htons(srv_address->port);
+ srv_addr.sin6_family = PF_INET6;
+
+ ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
+ }
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ return ip_connect_complete(sock, flags);
+}
+
+/*
+ fix the sin6_scope_id based on the address interface
+ */
+static void fix_scope_id(struct sockaddr_in6 *in6,
+ const char *address)
+{
+ const char *p = strchr(address, '%');
+ if (p != NULL) {
+ in6->sin6_scope_id = if_nametoindex(p+1);
+ }
+}
+
+
+static NTSTATUS ipv6_listen(struct socket_context *sock,
+ const struct socket_address *my_address,
+ int queue_size, uint32_t flags)
+{
+ struct sockaddr_in6 my_addr;
+ struct in6_addr ip_addr;
+ int ret;
+
+ socket_set_option(sock, "SO_REUSEADDR=1", NULL);
+
+ if (my_address->sockaddr) {
+ ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
+ } else {
+ int one = 1;
+ ip_addr = interpret_addr6(my_address->addr);
+
+ ZERO_STRUCT(my_addr);
+ my_addr.sin6_addr = ip_addr;
+ my_addr.sin6_port = htons(my_address->port);
+ my_addr.sin6_family = PF_INET6;
+ fix_scope_id(&my_addr, my_address->addr);
+
+ /* when binding on ipv6 we always want to only bind on v6 */
+ ret = setsockopt(sock->fd, IPPROTO_IPV6, IPV6_V6ONLY,
+ (const void *)&one, sizeof(one));
+ if (ret != -1) {
+ ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
+ }
+ }
+
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ if (sock->type == SOCKET_TYPE_STREAM) {
+ ret = listen(sock->fd, queue_size);
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+ }
+
+ ret = set_blocking(sock->fd, false);
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ sock->state= SOCKET_STATE_SERVER_LISTEN;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ipv6_tcp_accept(struct socket_context *sock, struct socket_context **new_sock)
+{
+ struct sockaddr_in6 cli_addr;
+ socklen_t cli_addr_len = sizeof(cli_addr);
+ int new_fd, ret;
+
+ if (sock->type != SOCKET_TYPE_STREAM) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
+ if (new_fd == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ ret = set_blocking(new_fd, false);
+ if (ret == -1) {
+ close(new_fd);
+ return map_nt_error_from_unix_common(errno);
+ }
+ smb_set_close_on_exec(new_fd);
+
+ /* TODO: we could add a 'accept_check' hook here
+ * which get the black/white lists via socket_set_accept_filter()
+ * or something like that
+ * --metze
+ */
+
+ (*new_sock) = talloc(NULL, struct socket_context);
+ if (!(*new_sock)) {
+ close(new_fd);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* copy the socket_context */
+ (*new_sock)->type = sock->type;
+ (*new_sock)->state = SOCKET_STATE_SERVER_CONNECTED;
+ (*new_sock)->flags = sock->flags;
+
+ (*new_sock)->fd = new_fd;
+
+ (*new_sock)->private_data = NULL;
+ (*new_sock)->ops = sock->ops;
+ (*new_sock)->backend_name = sock->backend_name;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ipv6_recvfrom(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread,
+ TALLOC_CTX *addr_ctx, struct socket_address **_src)
+{
+ ssize_t gotlen;
+ struct sockaddr_in6 *from_addr;
+ socklen_t from_len = sizeof(*from_addr);
+ struct socket_address *src;
+ char addrstring[INET6_ADDRSTRLEN];
+
+ src = talloc(addr_ctx, struct socket_address);
+ if (!src) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ src->family = sock->backend_name;
+
+ from_addr = talloc(src, struct sockaddr_in6);
+ if (!from_addr) {
+ talloc_free(src);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ src->sockaddr = (struct sockaddr *)from_addr;
+
+ *nread = 0;
+
+ gotlen = recvfrom(sock->fd, buf, wantlen, 0,
+ src->sockaddr, &from_len);
+ if (gotlen == 0) {
+ talloc_free(src);
+ return NT_STATUS_END_OF_FILE;
+ } else if (gotlen == -1) {
+ talloc_free(src);
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ src->sockaddrlen = from_len;
+
+ if (inet_ntop(AF_INET6, &from_addr->sin6_addr, addrstring, sizeof(addrstring)) == NULL) {
+ DEBUG(0, ("Unable to convert address to string: %s\n", strerror(errno)));
+ talloc_free(src);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ src->addr = talloc_strdup(src, addrstring);
+ if (src->addr == NULL) {
+ talloc_free(src);
+ return NT_STATUS_NO_MEMORY;
+ }
+ src->port = ntohs(from_addr->sin6_port);
+
+ *nread = gotlen;
+ *_src = src;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ipv6_sendto(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen,
+ const struct socket_address *dest_addr)
+{
+ ssize_t len;
+
+ if (dest_addr->sockaddr) {
+ len = sendto(sock->fd, blob->data, blob->length, 0,
+ dest_addr->sockaddr, dest_addr->sockaddrlen);
+ } else {
+ struct sockaddr_in6 srv_addr;
+ struct in6_addr addr;
+
+ ZERO_STRUCT(srv_addr);
+ addr = interpret_addr6(dest_addr->addr);
+ if (memcmp(&addr.s6_addr, &in6addr_any,
+ sizeof(addr.s6_addr)) == 0) {
+ return NT_STATUS_HOST_UNREACHABLE;
+ }
+ srv_addr.sin6_addr = addr;
+ srv_addr.sin6_port = htons(dest_addr->port);
+ srv_addr.sin6_family = PF_INET6;
+
+ *sendlen = 0;
+
+ len = sendto(sock->fd, blob->data, blob->length, 0,
+ (struct sockaddr *)&srv_addr, sizeof(srv_addr));
+ }
+ if (len == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ *sendlen = len;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ipv6_set_option(struct socket_context *sock, const char *option, const char *val)
+{
+ set_socket_options(sock->fd, option);
+ return NT_STATUS_OK;
+}
+
+static char *ipv6_tcp_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct sockaddr_in6 peer_addr;
+ socklen_t len = sizeof(peer_addr);
+ struct hostent *he;
+ int ret;
+
+ ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
+ if (ret == -1) {
+ return NULL;
+ }
+
+ he = gethostbyaddr((char *)&peer_addr.sin6_addr, sizeof(peer_addr.sin6_addr), AF_INET6);
+ if (he == NULL) {
+ return NULL;
+ }
+
+ return talloc_strdup(mem_ctx, he->h_name);
+}
+
+static struct socket_address *ipv6_tcp_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct sockaddr_in6 *peer_addr;
+ socklen_t len = sizeof(*peer_addr);
+ struct socket_address *peer;
+ int ret;
+ char addr[128];
+ const char *addr_ret;
+
+ peer = talloc(mem_ctx, struct socket_address);
+ if (!peer) {
+ return NULL;
+ }
+
+ peer->family = sock->backend_name;
+ peer_addr = talloc(peer, struct sockaddr_in6);
+ if (!peer_addr) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ peer->sockaddr = (struct sockaddr *)peer_addr;
+
+ ret = getpeername(sock->fd, peer->sockaddr, &len);
+ if (ret == -1) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ peer->sockaddrlen = len;
+
+ addr_ret = inet_ntop(AF_INET6, &peer_addr->sin6_addr, addr, sizeof(addr));
+ if (addr_ret == NULL) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ peer->addr = talloc_strdup(peer, addr_ret);
+ if (peer->addr == NULL) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ peer->port = ntohs(peer_addr->sin6_port);
+
+ return peer;
+}
+
+static struct socket_address *ipv6_tcp_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct sockaddr_in6 *local_addr;
+ socklen_t len = sizeof(*local_addr);
+ struct socket_address *local;
+ int ret;
+ char addrstring[INET6_ADDRSTRLEN];
+
+ local = talloc(mem_ctx, struct socket_address);
+ if (!local) {
+ return NULL;
+ }
+
+ local->family = sock->backend_name;
+ local_addr = talloc(local, struct sockaddr_in6);
+ if (!local_addr) {
+ talloc_free(local);
+ return NULL;
+ }
+
+ local->sockaddr = (struct sockaddr *)local_addr;
+
+ ret = getsockname(sock->fd, local->sockaddr, &len);
+ if (ret == -1) {
+ talloc_free(local);
+ return NULL;
+ }
+
+ local->sockaddrlen = len;
+
+ if (inet_ntop(AF_INET6, &local_addr->sin6_addr, addrstring,
+ sizeof(addrstring)) == NULL) {
+ DEBUG(0, ("Unable to convert address to string: %s\n",
+ strerror(errno)));
+ talloc_free(local);
+ return NULL;
+ }
+
+ local->addr = talloc_strdup(local, addrstring);
+ if (!local->addr) {
+ talloc_free(local);
+ return NULL;
+ }
+ local->port = ntohs(local_addr->sin6_port);
+
+ return local;
+}
+
+static const struct socket_ops ipv6_tcp_ops = {
+ .name = "ipv6",
+ .fn_init = ipv6_init,
+ .fn_connect = ipv6_tcp_connect,
+ .fn_connect_complete = ip_connect_complete,
+ .fn_listen = ipv6_listen,
+ .fn_accept = ipv6_tcp_accept,
+ .fn_recv = ip_recv,
+ .fn_recvfrom = ipv6_recvfrom,
+ .fn_send = ip_send,
+ .fn_sendto = ipv6_sendto,
+ .fn_pending = ip_pending,
+ .fn_close = ip_close,
+
+ .fn_set_option = ipv6_set_option,
+
+ .fn_get_peer_name = ipv6_tcp_get_peer_name,
+ .fn_get_peer_addr = ipv6_tcp_get_peer_addr,
+ .fn_get_my_addr = ipv6_tcp_get_my_addr,
+
+ .fn_get_fd = ip_get_fd
+};
+
+_PUBLIC_ const struct socket_ops *socket_ipv6_ops(enum socket_type type)
+{
+ return &ipv6_tcp_ops;
+}
+
+#endif
diff --git a/source4/lib/socket/socket_unix.c b/source4/lib/socket/socket_unix.c
new file mode 100644
index 0000000..d4946d8
--- /dev/null
+++ b/source4/lib/socket/socket_unix.c
@@ -0,0 +1,436 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ unix domain socket functions
+
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Andrew Tridgell 2004-2005
+
+ 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 "lib/socket/socket.h"
+#include "system/network.h"
+#include "system/filesys.h"
+
+_PUBLIC_ const struct socket_ops *socket_unixdom_ops(enum socket_type type);
+
+
+/*
+ approximate errno mapping
+*/
+static NTSTATUS unixdom_error(int ernum)
+{
+ return map_nt_error_from_unix_common(ernum);
+}
+
+static NTSTATUS unixdom_init(struct socket_context *sock)
+{
+ int type;
+
+ switch (sock->type) {
+ case SOCKET_TYPE_STREAM:
+ type = SOCK_STREAM;
+ break;
+ case SOCKET_TYPE_DGRAM:
+ type = SOCK_DGRAM;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ sock->fd = socket(PF_UNIX, type, 0);
+ if (sock->fd == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+ sock->private_data = NULL;
+
+ sock->backend_name = "unix";
+
+ smb_set_close_on_exec(sock->fd);
+
+ return NT_STATUS_OK;
+}
+
+static void unixdom_close(struct socket_context *sock)
+{
+ close(sock->fd);
+}
+
+static NTSTATUS unixdom_connect_complete(struct socket_context *sock, uint32_t flags)
+{
+ int error=0, ret;
+ socklen_t len = sizeof(error);
+
+ /* check for any errors that may have occurred - this is needed
+ for non-blocking connect */
+ ret = getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &error, &len);
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+ if (error != 0) {
+ return map_nt_error_from_unix_common(error);
+ }
+
+ ret = set_blocking(sock->fd, false);
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ sock->state = SOCKET_STATE_CLIENT_CONNECTED;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS unixdom_connect(struct socket_context *sock,
+ const struct socket_address *my_address,
+ const struct socket_address *srv_address,
+ uint32_t flags)
+{
+ int ret;
+
+ if (srv_address->sockaddr) {
+ ret = connect(sock->fd, srv_address->sockaddr, srv_address->sockaddrlen);
+ } else {
+ struct sockaddr_un srv_addr;
+ if (strlen(srv_address->addr)+1 > sizeof(srv_addr.sun_path)) {
+ return NT_STATUS_OBJECT_PATH_INVALID;
+ }
+
+ ZERO_STRUCT(srv_addr);
+ srv_addr.sun_family = AF_UNIX;
+ snprintf(srv_addr.sun_path, sizeof(srv_addr.sun_path), "%s", srv_address->addr);
+
+ ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
+ }
+ if (ret == -1) {
+ return unixdom_error(errno);
+ }
+
+ return unixdom_connect_complete(sock, flags);
+}
+
+static NTSTATUS unixdom_listen(struct socket_context *sock,
+ const struct socket_address *my_address,
+ int queue_size, uint32_t flags)
+{
+ struct sockaddr_un my_addr;
+ int ret;
+
+ /* delete if it already exists */
+ if (my_address->addr) {
+ unlink(my_address->addr);
+ }
+
+ if (my_address->sockaddr) {
+ ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
+ } else if (my_address->addr == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ } else {
+ if (strlen(my_address->addr)+1 > sizeof(my_addr.sun_path)) {
+ return NT_STATUS_OBJECT_PATH_INVALID;
+ }
+
+
+ ZERO_STRUCT(my_addr);
+ my_addr.sun_family = AF_UNIX;
+ snprintf(my_addr.sun_path, sizeof(my_addr.sun_path), "%s", my_address->addr);
+
+ ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
+ }
+ if (ret == -1) {
+ return unixdom_error(errno);
+ }
+
+ if (sock->type == SOCKET_TYPE_STREAM) {
+ ret = listen(sock->fd, queue_size);
+ if (ret == -1) {
+ return unixdom_error(errno);
+ }
+ }
+
+ ret = set_blocking(sock->fd, false);
+ if (ret == -1) {
+ return unixdom_error(errno);
+ }
+
+ sock->state = SOCKET_STATE_SERVER_LISTEN;
+ sock->private_data = (void *)talloc_strdup(sock, my_address->addr);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS unixdom_accept(struct socket_context *sock,
+ struct socket_context **new_sock)
+{
+ struct sockaddr_un cli_addr;
+ socklen_t cli_addr_len = sizeof(cli_addr);
+ int new_fd, ret;
+
+ if (sock->type != SOCKET_TYPE_STREAM) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
+ if (new_fd == -1) {
+ return unixdom_error(errno);
+ }
+
+ ret = set_blocking(new_fd, false);
+ if (ret == -1) {
+ close(new_fd);
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ smb_set_close_on_exec(new_fd);
+
+ (*new_sock) = talloc(NULL, struct socket_context);
+ if (!(*new_sock)) {
+ close(new_fd);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* copy the socket_context */
+ (*new_sock)->type = sock->type;
+ (*new_sock)->state = SOCKET_STATE_SERVER_CONNECTED;
+ (*new_sock)->flags = sock->flags;
+
+ (*new_sock)->fd = new_fd;
+
+ (*new_sock)->private_data = NULL;
+ (*new_sock)->ops = sock->ops;
+ (*new_sock)->backend_name = sock->backend_name;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS unixdom_recv(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread)
+{
+ ssize_t gotlen;
+
+ *nread = 0;
+
+ gotlen = recv(sock->fd, buf, wantlen, 0);
+ if (gotlen == 0) {
+ return NT_STATUS_END_OF_FILE;
+ } else if (gotlen == -1) {
+ return unixdom_error(errno);
+ }
+
+ *nread = gotlen;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS unixdom_send(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen)
+{
+ ssize_t len;
+
+ *sendlen = 0;
+
+ len = send(sock->fd, blob->data, blob->length, 0);
+ if (len == -1) {
+ return unixdom_error(errno);
+ }
+
+ *sendlen = len;
+
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS unixdom_sendto(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen,
+ const struct socket_address *dest)
+{
+ struct sockaddr_un srv_addr;
+ const struct sockaddr *sa;
+ socklen_t sa_len;
+ ssize_t len;
+
+ *sendlen = 0;
+
+ if (dest->sockaddr) {
+ sa = dest->sockaddr;
+ sa_len = dest->sockaddrlen;
+ } else {
+ if (strlen(dest->addr)+1 > sizeof(srv_addr.sun_path)) {
+ return NT_STATUS_OBJECT_PATH_INVALID;
+ }
+
+ ZERO_STRUCT(srv_addr);
+ srv_addr.sun_family = AF_UNIX;
+ snprintf(srv_addr.sun_path, sizeof(srv_addr.sun_path), "%s",
+ dest->addr);
+ sa = (struct sockaddr *) &srv_addr;
+ sa_len = sizeof(srv_addr);
+ }
+
+ len = sendto(sock->fd, blob->data, blob->length, 0, sa, sa_len);
+
+ /* retry once */
+ if (len == -1 && errno == EMSGSIZE) {
+ /* round up in 1K increments */
+ int bufsize = ((blob->length + 1023) & (~1023));
+ if (setsockopt(sock->fd, SOL_SOCKET, SO_SNDBUF, &bufsize,
+ sizeof(bufsize)) == -1)
+ {
+ return map_nt_error_from_unix_common(EMSGSIZE);
+ }
+ len = sendto(sock->fd, blob->data, blob->length, 0, sa, sa_len);
+ }
+
+ if (len == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ *sendlen = len;
+
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS unixdom_set_option(struct socket_context *sock,
+ const char *option, const char *val)
+{
+ return NT_STATUS_OK;
+}
+
+static char *unixdom_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ return talloc_strdup(mem_ctx, "LOCAL/unixdom");
+}
+
+static struct socket_address *unixdom_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct sockaddr_un *peer_addr;
+ socklen_t len = sizeof(*peer_addr);
+ struct socket_address *peer;
+ int ret;
+
+ peer = talloc(mem_ctx, struct socket_address);
+ if (!peer) {
+ return NULL;
+ }
+
+ peer->family = sock->backend_name;
+ peer_addr = talloc(peer, struct sockaddr_un);
+ if (!peer_addr) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ peer->sockaddr = (struct sockaddr *)peer_addr;
+
+ ret = getpeername(sock->fd, peer->sockaddr, &len);
+ if (ret == -1) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ peer->sockaddrlen = len;
+
+ peer->port = 0;
+ peer->addr = talloc_strdup(peer, "LOCAL/unixdom");
+ if (!peer->addr) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ return peer;
+}
+
+static struct socket_address *unixdom_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct sockaddr_un *local_addr;
+ socklen_t len = sizeof(*local_addr);
+ struct socket_address *local;
+ int ret;
+
+ local = talloc(mem_ctx, struct socket_address);
+ if (!local) {
+ return NULL;
+ }
+
+ local->family = sock->backend_name;
+ local_addr = talloc(local, struct sockaddr_un);
+ if (!local_addr) {
+ talloc_free(local);
+ return NULL;
+ }
+
+ local->sockaddr = (struct sockaddr *)local_addr;
+
+ ret = getsockname(sock->fd, local->sockaddr, &len);
+ if (ret == -1) {
+ talloc_free(local);
+ return NULL;
+ }
+
+ local->sockaddrlen = len;
+
+ local->port = 0;
+ local->addr = talloc_strdup(local, "LOCAL/unixdom");
+ if (!local->addr) {
+ talloc_free(local);
+ return NULL;
+ }
+
+ return local;
+}
+
+static int unixdom_get_fd(struct socket_context *sock)
+{
+ return sock->fd;
+}
+
+static NTSTATUS unixdom_pending(struct socket_context *sock, size_t *npending)
+{
+ int value = 0;
+ if (ioctl(sock->fd, FIONREAD, &value) == 0) {
+ *npending = value;
+ return NT_STATUS_OK;
+ }
+ return map_nt_error_from_unix_common(errno);
+}
+
+static const struct socket_ops unixdom_ops = {
+ .name = "unix",
+ .fn_init = unixdom_init,
+ .fn_connect = unixdom_connect,
+ .fn_connect_complete = unixdom_connect_complete,
+ .fn_listen = unixdom_listen,
+ .fn_accept = unixdom_accept,
+ .fn_recv = unixdom_recv,
+ .fn_send = unixdom_send,
+ .fn_sendto = unixdom_sendto,
+ .fn_close = unixdom_close,
+ .fn_pending = unixdom_pending,
+
+ .fn_set_option = unixdom_set_option,
+
+ .fn_get_peer_name = unixdom_get_peer_name,
+ .fn_get_peer_addr = unixdom_get_peer_addr,
+ .fn_get_my_addr = unixdom_get_my_addr,
+
+ .fn_get_fd = unixdom_get_fd
+};
+
+_PUBLIC_ const struct socket_ops *socket_unixdom_ops(enum socket_type type)
+{
+ return &unixdom_ops;
+}
diff --git a/source4/lib/socket/testsuite.c b/source4/lib/socket/testsuite.c
new file mode 100644
index 0000000..1df96e3
--- /dev/null
+++ b/source4/lib/socket/testsuite.c
@@ -0,0 +1,194 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local testing of socket routines.
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 "lib/socket/socket.h"
+#include "lib/events/events.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "torture/torture.h"
+#include "torture/local/proto.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+
+/**
+ basic testing of udp routines
+*/
+static bool test_udp(struct torture_context *tctx)
+{
+ struct socket_context *sock1, *sock2;
+ NTSTATUS status;
+ struct socket_address *srv_addr, *from_addr, *localhost;
+ size_t size = 100 + (random() % 100);
+ DATA_BLOB blob, blob2;
+ size_t sent, nread;
+ TALLOC_CTX *mem_ctx = tctx;
+ struct interface *ifaces;
+
+ load_interface_list(tctx, tctx->lp_ctx, &ifaces);
+
+ status = socket_create(mem_ctx, "ip", SOCKET_TYPE_DGRAM, &sock1, 0);
+ torture_assert_ntstatus_ok(tctx, status, "creating DGRAM IP socket 1");
+
+ status = socket_create(mem_ctx, "ip", SOCKET_TYPE_DGRAM, &sock2, 0);
+ torture_assert_ntstatus_ok(tctx, status, "creating DGRAM IP socket 1");
+
+ localhost = socket_address_from_strings(sock1, sock1->backend_name,
+ iface_list_best_ip(ifaces, "127.0.0.1"), 0);
+
+ torture_assert(tctx, localhost, "Localhost not found");
+
+ status = socket_listen(sock1, localhost, 0, 0);
+ torture_assert_ntstatus_ok(tctx, status, "listen on socket 1");
+
+ srv_addr = socket_get_my_addr(sock1, mem_ctx);
+ torture_assert(tctx, srv_addr != NULL &&
+ strcmp(srv_addr->addr, iface_list_best_ip(ifaces, "127.0.0.1")) == 0,
+ talloc_asprintf(tctx,
+ "Expected server address of %s but got %s",
+ iface_list_best_ip(ifaces, "127.0.0.1"), srv_addr ? srv_addr->addr : NULL));
+
+ torture_comment(tctx, "server port is %d\n", srv_addr->port);
+
+ blob = data_blob_talloc(mem_ctx, NULL, size);
+ blob2 = data_blob_talloc(mem_ctx, NULL, size);
+ generate_random_buffer(blob.data, blob.length);
+
+ sent = size;
+ status = socket_sendto(sock2, &blob, &sent, srv_addr);
+ torture_assert_ntstatus_ok(tctx, status, "sendto() on socket 2");
+
+ status = socket_recvfrom(sock1, blob2.data, size, &nread,
+ sock1, &from_addr);
+ torture_assert_ntstatus_ok(tctx, status, "recvfrom() on socket 1");
+
+ torture_assert_str_equal(tctx, from_addr->addr, srv_addr->addr,
+ "different address");
+
+ torture_assert_int_equal(tctx, nread, size, "Unexpected recvfrom size");
+
+ torture_assert_mem_equal(tctx, blob2.data, blob.data, size,
+ "Bad data in recvfrom");
+
+ generate_random_buffer(blob.data, blob.length);
+ status = socket_sendto(sock1, &blob, &sent, from_addr);
+ torture_assert_ntstatus_ok(tctx, status, "sendto() on socket 1");
+
+ status = socket_recvfrom(sock2, blob2.data, size, &nread,
+ sock2, &from_addr);
+ torture_assert_ntstatus_ok(tctx, status, "recvfrom() on socket 2");
+ torture_assert_str_equal(tctx, from_addr->addr, srv_addr->addr,
+ "Unexpected recvfrom addr");
+
+ torture_assert_int_equal(tctx, nread, size, "Unexpected recvfrom size");
+
+ torture_assert_int_equal(tctx, from_addr->port, srv_addr->port,
+ "Unexpected recvfrom port");
+
+ torture_assert_mem_equal(tctx, blob2.data, blob.data, size,
+ "Bad data in recvfrom");
+
+ talloc_free(sock1);
+ talloc_free(sock2);
+ return true;
+}
+
+/*
+ basic testing of tcp routines
+*/
+static bool test_tcp(struct torture_context *tctx)
+{
+ struct socket_context *sock1, *sock2, *sock3;
+ NTSTATUS status;
+ struct socket_address *srv_addr, *from_addr, *localhost;
+ size_t size = 100 + (random() % 100);
+ DATA_BLOB blob, blob2;
+ size_t sent, nread;
+ TALLOC_CTX *mem_ctx = tctx;
+ struct tevent_context *ev = tctx->ev;
+ struct interface *ifaces;
+
+ status = socket_create(mem_ctx, "ip", SOCKET_TYPE_STREAM, &sock1, 0);
+ torture_assert_ntstatus_ok(tctx, status, "creating IP stream socket 1");
+
+ status = socket_create(mem_ctx, "ip", SOCKET_TYPE_STREAM, &sock2, 0);
+ torture_assert_ntstatus_ok(tctx, status, "creating IP stream socket 1");
+
+ load_interface_list(tctx, tctx->lp_ctx, &ifaces);
+ localhost = socket_address_from_strings(sock1, sock1->backend_name,
+ iface_list_best_ip(ifaces, "127.0.0.1"), 0);
+ torture_assert(tctx, localhost, "Localhost not found");
+
+ status = socket_listen(sock1, localhost, 0, 0);
+ torture_assert_ntstatus_ok(tctx, status, "listen on socket 1");
+
+ srv_addr = socket_get_my_addr(sock1, mem_ctx);
+ torture_assert(tctx, srv_addr && srv_addr->addr,
+ "Unexpected socket_get_my_addr NULL\n");
+
+ torture_assert_str_equal(tctx, srv_addr->addr, iface_list_best_ip(ifaces, "127.0.0.1"),
+ "Unexpected server address");
+
+ torture_comment(tctx, "server port is %d\n", srv_addr->port);
+
+ status = socket_connect_ev(sock2, NULL, srv_addr, 0, ev);
+ torture_assert_ntstatus_ok(tctx, status, "connect() on socket 2");
+
+ status = socket_accept(sock1, &sock3);
+ torture_assert_ntstatus_ok(tctx, status, "accept() on socket 1");
+ talloc_steal(mem_ctx, sock3);
+ talloc_free(sock1);
+
+ blob = data_blob_talloc(mem_ctx, NULL, size);
+ blob2 = data_blob_talloc(mem_ctx, NULL, size);
+ generate_random_buffer(blob.data, blob.length);
+
+ sent = size;
+ status = socket_send(sock2, &blob, &sent);
+ torture_assert_ntstatus_ok(tctx, status, "send() on socket 2");
+
+ status = socket_recv(sock3, blob2.data, size, &nread);
+ torture_assert_ntstatus_ok(tctx, status, "recv() on socket 3");
+
+ from_addr = socket_get_peer_addr(sock3, mem_ctx);
+
+ torture_assert(tctx, from_addr && from_addr->addr,
+ "Unexpected recvfrom addr NULL");
+
+ torture_assert_str_equal(tctx, from_addr->addr, srv_addr->addr,
+ "Unexpected recvfrom addr");
+
+ torture_assert_int_equal(tctx, nread, size, "Unexpected recvfrom size");
+
+ torture_assert_mem_equal(tctx, blob2.data, blob.data, size,
+ "Bad data in recv");
+ return true;
+}
+
+struct torture_suite *torture_local_socket(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "socket");
+
+ torture_suite_add_simple_test(suite, "udp", test_udp);
+ torture_suite_add_simple_test(suite, "tcp", test_tcp);
+
+ return suite;
+}
diff --git a/source4/lib/socket/wscript_build b/source4/lib/socket/wscript_build
new file mode 100644
index 0000000..e243824
--- /dev/null
+++ b/source4/lib/socket/wscript_build
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+
+bld.SAMBA_LIBRARY('netif',
+ source='interface.c',
+ deps='samba-util interfaces samba-hostconfig',
+ private_library=True,
+ autoproto='netif_proto.h'
+ )
+
+bld.SAMBA_MODULE('socket_ip',
+ source='socket_ip.c',
+ subsystem='samba_socket',
+ deps='samba-errors',
+ internal_module=True
+ )
+
+bld.SAMBA_MODULE('socket_unix',
+ source='socket_unix.c',
+ subsystem='samba_socket',
+ deps='talloc',
+ internal_module=True
+ )
+
+bld.SAMBA_SUBSYSTEM('samba_socket',
+ source='socket.c access.c connect_multi.c connect.c',
+ public_deps='talloc LIBTSOCKET',
+ deps='cli_composite LIBCLI_RESOLVE socket_ip socket_unix access'
+ )
+
diff --git a/source4/lib/stream/packet.c b/source4/lib/stream/packet.c
new file mode 100644
index 0000000..f78d678
--- /dev/null
+++ b/source4/lib/stream/packet.c
@@ -0,0 +1,626 @@
+/*
+ Unix SMB/CIFS Implementation.
+
+ helper layer for breaking up streams into discrete requests
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 "../lib/util/dlinklist.h"
+#include "lib/events/events.h"
+#include "lib/socket/socket.h"
+#include "lib/stream/packet.h"
+#include "libcli/raw/smb.h"
+
+struct packet_context {
+ packet_callback_fn_t callback;
+ packet_full_request_fn_t full_request;
+ packet_error_handler_fn_t error_handler;
+ DATA_BLOB partial;
+ uint32_t num_read;
+ uint32_t initial_read;
+ struct socket_context *sock;
+ struct tevent_context *ev;
+ size_t packet_size;
+ void *private_data;
+ struct tevent_fd *fde;
+ bool serialise;
+ int processing;
+ bool recv_disable;
+ bool recv_need_enable;
+ bool nofree;
+
+ bool busy;
+ bool destructor_called;
+
+ bool unreliable_select;
+
+ struct send_element {
+ struct send_element *next, *prev;
+ DATA_BLOB blob;
+ size_t nsent;
+ packet_send_callback_fn_t send_callback;
+ void *send_callback_private;
+ } *send_queue;
+};
+
+/*
+ a destructor used when we are processing packets to prevent freeing of this
+ context while it is being used
+*/
+static int packet_destructor(struct packet_context *pc)
+{
+ if (pc->busy) {
+ pc->destructor_called = true;
+ /* now we refuse the talloc_free() request. The free will
+ happen again in the packet_recv() code */
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ initialise a packet receiver
+*/
+_PUBLIC_ struct packet_context *packet_init(TALLOC_CTX *mem_ctx)
+{
+ struct packet_context *pc = talloc_zero(mem_ctx, struct packet_context);
+ if (pc != NULL) {
+ talloc_set_destructor(pc, packet_destructor);
+ }
+ return pc;
+}
+
+
+/*
+ set the request callback, called when a full request is ready
+*/
+_PUBLIC_ void packet_set_callback(struct packet_context *pc, packet_callback_fn_t callback)
+{
+ pc->callback = callback;
+}
+
+/*
+ set the error handler
+*/
+_PUBLIC_ void packet_set_error_handler(struct packet_context *pc, packet_error_handler_fn_t handler)
+{
+ pc->error_handler = handler;
+}
+
+/*
+ set the private pointer passed to the callback functions
+*/
+_PUBLIC_ void packet_set_private(struct packet_context *pc, void *private_data)
+{
+ pc->private_data = private_data;
+}
+
+/*
+ set the full request callback. Should return as follows:
+ NT_STATUS_OK == blob is a full request.
+ STATUS_MORE_ENTRIES == blob is not complete yet
+ any error == blob is not a valid
+*/
+_PUBLIC_ void packet_set_full_request(struct packet_context *pc, packet_full_request_fn_t callback)
+{
+ pc->full_request = callback;
+}
+
+/*
+ set a socket context to use. You must set a socket_context
+*/
+_PUBLIC_ void packet_set_socket(struct packet_context *pc, struct socket_context *sock)
+{
+ pc->sock = sock;
+}
+
+/*
+ set an event context. If this is set then the code will ensure that
+ packets arrive with separate events, by creating a immediate event
+ for any secondary packets when more than one packet is read at one
+ time on a socket. This can matter for code that relies on not
+ getting more than one packet per event
+*/
+_PUBLIC_ void packet_set_event_context(struct packet_context *pc, struct tevent_context *ev)
+{
+ pc->ev = ev;
+}
+
+/*
+ tell the packet layer the fde for the socket
+*/
+_PUBLIC_ void packet_set_fde(struct packet_context *pc, struct tevent_fd *fde)
+{
+ pc->fde = fde;
+}
+
+/*
+ tell the packet layer to serialise requests, so we don't process two
+ requests at once on one connection. You must have set the
+ event_context and fde
+*/
+_PUBLIC_ void packet_set_serialise(struct packet_context *pc)
+{
+ pc->serialise = true;
+}
+
+/*
+ tell the packet layer how much to read when starting a new packet
+ this ensures it doesn't overread
+*/
+_PUBLIC_ void packet_set_initial_read(struct packet_context *pc, uint32_t initial_read)
+{
+ pc->initial_read = initial_read;
+}
+
+/*
+ tell the packet system not to steal/free blobs given to packet_send()
+*/
+_PUBLIC_ void packet_set_nofree(struct packet_context *pc)
+{
+ pc->nofree = true;
+}
+
+/*
+ tell the packet system that select/poll/epoll on the underlying
+ socket may not be a reliable way to determine if data is available
+ for receive. This happens with underlying socket systems such as the
+ one implemented on top of GNUTLS, where there may be data in
+ encryption/compression buffers that could be received by
+ socket_recv(), while there is no data waiting at the real socket
+ level as seen by select/poll/epoll. The GNUTLS library is supposed
+ to cope with this by always leaving some data sitting in the socket
+ buffer, but it does not seem to be reliable.
+ */
+_PUBLIC_ void packet_set_unreliable_select(struct packet_context *pc)
+{
+ pc->unreliable_select = true;
+}
+
+/*
+ tell the caller we have an error
+*/
+static void packet_error(struct packet_context *pc, NTSTATUS status)
+{
+ pc->sock = NULL;
+ if (pc->error_handler) {
+ pc->error_handler(pc->private_data, status);
+ return;
+ }
+ /* default error handler is to free the callers private pointer */
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
+ DEBUG(0,("packet_error on %s - %s\n",
+ talloc_get_name(pc->private_data), nt_errstr(status)));
+ }
+ talloc_free(pc->private_data);
+ return;
+}
+
+
+/*
+ tell the caller we have EOF
+*/
+static void packet_eof(struct packet_context *pc)
+{
+ packet_error(pc, NT_STATUS_END_OF_FILE);
+}
+
+
+/*
+ used to put packets on event boundaries
+*/
+static void packet_next_event(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct packet_context *pc = talloc_get_type(private_data, struct packet_context);
+ if (pc->num_read != 0 && pc->packet_size != 0 &&
+ pc->packet_size <= pc->num_read) {
+ packet_recv(pc);
+ }
+}
+
+
+/*
+ call this when the socket becomes readable to kick off the whole
+ stream parsing process
+*/
+_PUBLIC_ void packet_recv(struct packet_context *pc)
+{
+ size_t npending;
+ NTSTATUS status;
+ size_t nread = 0;
+ DATA_BLOB blob;
+ bool recv_retry = false;
+
+ if (pc->processing) {
+ TEVENT_FD_NOT_READABLE(pc->fde);
+ pc->processing++;
+ return;
+ }
+
+ if (pc->recv_disable) {
+ pc->recv_need_enable = true;
+ TEVENT_FD_NOT_READABLE(pc->fde);
+ return;
+ }
+
+ if (pc->packet_size != 0 && pc->num_read >= pc->packet_size) {
+ goto next_partial;
+ }
+
+ if (pc->packet_size != 0) {
+ /* we've already worked out how long this next packet is, so skip the
+ socket_pending() call */
+ npending = pc->packet_size - pc->num_read;
+ } else if (pc->initial_read != 0) {
+ npending = pc->initial_read - pc->num_read;
+ } else {
+ if (pc->sock) {
+ status = socket_pending(pc->sock, &npending);
+ } else {
+ status = NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ packet_error(pc, status);
+ return;
+ }
+ }
+
+ if (npending == 0) {
+ packet_eof(pc);
+ return;
+ }
+
+again:
+
+ if (npending + pc->num_read < npending) {
+ packet_error(pc, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ if (npending + pc->num_read < pc->num_read) {
+ packet_error(pc, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ /* possibly expand the partial packet buffer */
+ if (npending + pc->num_read > pc->partial.length) {
+ if (!data_blob_realloc(pc, &pc->partial, npending+pc->num_read)) {
+ packet_error(pc, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ }
+
+ if (pc->partial.length < pc->num_read + npending) {
+ packet_error(pc, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ if ((uint8_t *)pc->partial.data + pc->num_read < (uint8_t *)pc->partial.data) {
+ packet_error(pc, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ if ((uint8_t *)pc->partial.data + pc->num_read + npending < (uint8_t *)pc->partial.data) {
+ packet_error(pc, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ status = socket_recv(pc->sock, pc->partial.data + pc->num_read,
+ npending, &nread);
+
+ if (NT_STATUS_IS_ERR(status)) {
+ packet_error(pc, status);
+ return;
+ }
+ if (recv_retry && NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+ nread = 0;
+ status = NT_STATUS_OK;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return;
+ }
+
+ if (nread == 0 && !recv_retry) {
+ packet_eof(pc);
+ return;
+ }
+
+ pc->num_read += nread;
+
+ if (pc->unreliable_select && nread != 0) {
+ recv_retry = true;
+ status = socket_pending(pc->sock, &npending);
+ if (!NT_STATUS_IS_OK(status)) {
+ packet_error(pc, status);
+ return;
+ }
+ if (npending != 0) {
+ goto again;
+ }
+ }
+
+next_partial:
+ if (pc->partial.length != pc->num_read) {
+ if (!data_blob_realloc(pc, &pc->partial, pc->num_read)) {
+ packet_error(pc, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ }
+
+ /* see if its a full request */
+ blob = pc->partial;
+ blob.length = pc->num_read;
+ status = pc->full_request(pc->private_data, blob, &pc->packet_size);
+ if (NT_STATUS_IS_ERR(status)) {
+ packet_error(pc, status);
+ return;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return;
+ }
+
+ if (pc->packet_size > pc->num_read) {
+ /* the caller made an error */
+ DEBUG(0,("Invalid packet_size %lu greater than num_read %lu\n",
+ (long)pc->packet_size, (long)pc->num_read));
+ packet_error(pc, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ /* it is a full request - give it to the caller */
+ blob = pc->partial;
+ blob.length = pc->num_read;
+
+ if (pc->packet_size < pc->num_read) {
+ pc->partial = data_blob_talloc(pc, blob.data + pc->packet_size,
+ pc->num_read - pc->packet_size);
+ if (pc->partial.data == NULL) {
+ packet_error(pc, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ /* Trunate the blob sent to the caller to only the packet length */
+ if (!data_blob_realloc(pc, &blob, pc->packet_size)) {
+ packet_error(pc, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ } else {
+ pc->partial = data_blob(NULL, 0);
+ }
+ pc->num_read -= pc->packet_size;
+ pc->packet_size = 0;
+
+ if (pc->serialise) {
+ pc->processing = 1;
+ }
+
+ pc->busy = true;
+
+ status = pc->callback(pc->private_data, blob);
+
+ pc->busy = false;
+
+ if (pc->destructor_called) {
+ talloc_free(pc);
+ return;
+ }
+
+ if (pc->processing) {
+ if (pc->processing > 1) {
+ TEVENT_FD_READABLE(pc->fde);
+ }
+ pc->processing = 0;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ packet_error(pc, status);
+ return;
+ }
+
+ /* Have we consumed the whole buffer yet? */
+ if (pc->partial.length == 0) {
+ return;
+ }
+
+ /* we got multiple packets in one tcp read */
+ if (pc->ev == NULL) {
+ goto next_partial;
+ }
+
+ blob = pc->partial;
+ blob.length = pc->num_read;
+
+ status = pc->full_request(pc->private_data, blob, &pc->packet_size);
+ if (NT_STATUS_IS_ERR(status)) {
+ packet_error(pc, status);
+ return;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return;
+ }
+
+ tevent_add_timer(pc->ev, pc, timeval_zero(), packet_next_event, pc);
+}
+
+
+/*
+ temporarily disable receiving
+*/
+_PUBLIC_ void packet_recv_disable(struct packet_context *pc)
+{
+ pc->recv_disable = true;
+}
+
+/*
+ re-enable receiving
+*/
+_PUBLIC_ void packet_recv_enable(struct packet_context *pc)
+{
+ if (pc->recv_need_enable) {
+ pc->recv_need_enable = false;
+ TEVENT_FD_READABLE(pc->fde);
+ }
+ pc->recv_disable = false;
+ if (pc->num_read != 0 && pc->packet_size >= pc->num_read) {
+ tevent_add_timer(pc->ev, pc, timeval_zero(), packet_next_event, pc);
+ }
+}
+
+/*
+ trigger a run of the send queue
+*/
+_PUBLIC_ void packet_queue_run(struct packet_context *pc)
+{
+ while (pc->send_queue) {
+ struct send_element *el = pc->send_queue;
+ NTSTATUS status;
+ size_t nwritten;
+ DATA_BLOB blob = data_blob_const(el->blob.data + el->nsent,
+ el->blob.length - el->nsent);
+
+ status = socket_send(pc->sock, &blob, &nwritten);
+
+ if (NT_STATUS_IS_ERR(status)) {
+ packet_error(pc, status);
+ return;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return;
+ }
+ el->nsent += nwritten;
+ if (el->nsent == el->blob.length) {
+ DLIST_REMOVE(pc->send_queue, el);
+ if (el->send_callback) {
+ pc->busy = true;
+ el->send_callback(el->send_callback_private);
+ pc->busy = false;
+ if (pc->destructor_called) {
+ talloc_free(pc);
+ return;
+ }
+ }
+ talloc_free(el);
+ }
+ }
+
+ /* we're out of requests to send, so don't wait for write
+ events any more */
+ TEVENT_FD_NOT_WRITEABLE(pc->fde);
+}
+
+/*
+ put a packet in the send queue. When the packet is actually sent,
+ call send_callback.
+
+ Useful for operations that must occur after sending a message, such
+ as the switch to SASL encryption after as sucessful LDAP bind relpy.
+*/
+_PUBLIC_ NTSTATUS packet_send_callback(struct packet_context *pc, DATA_BLOB blob,
+ packet_send_callback_fn_t send_callback,
+ void *private_data)
+{
+ struct send_element *el;
+ el = talloc(pc, struct send_element);
+ NT_STATUS_HAVE_NO_MEMORY(el);
+
+ DLIST_ADD_END(pc->send_queue, el);
+ el->blob = blob;
+ el->nsent = 0;
+ el->send_callback = send_callback;
+ el->send_callback_private = private_data;
+
+ /* if we aren't going to free the packet then we must reference it
+ to ensure it doesn't disappear before going out */
+ if (pc->nofree) {
+ if (!talloc_reference(el, blob.data)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ talloc_steal(el, blob.data);
+ }
+
+ if (private_data && !talloc_reference(el, private_data)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ TEVENT_FD_WRITEABLE(pc->fde);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ put a packet in the send queue
+*/
+_PUBLIC_ NTSTATUS packet_send(struct packet_context *pc, DATA_BLOB blob)
+{
+ return packet_send_callback(pc, blob, NULL, NULL);
+}
+
+
+/*
+ a full request checker for NBT formatted packets (first 3 bytes are length)
+*/
+_PUBLIC_ NTSTATUS packet_full_request_nbt(void *private_data, DATA_BLOB blob, size_t *size)
+{
+ if (blob.length < 4) {
+ return STATUS_MORE_ENTRIES;
+ }
+ /*
+ * Note: that we use smb_len_tcp() instead
+ * of smb_len_nbt() as this function is not
+ * used for nbt and the source4 copy
+ * of smb_len() was smb_len_tcp()
+ */
+ *size = 4 + smb_len_tcp(blob.data);
+ if (*size > blob.length) {
+ return STATUS_MORE_ENTRIES;
+ }
+ return NT_STATUS_OK;
+}
+
+
+/*
+ work out if a packet is complete for protocols that use a 32 bit network byte
+ order length
+*/
+_PUBLIC_ NTSTATUS packet_full_request_u32(void *private_data, DATA_BLOB blob, size_t *size)
+{
+ if (blob.length < 4) {
+ return STATUS_MORE_ENTRIES;
+ }
+ *size = 4 + RIVAL(blob.data, 0);
+ if (*size > blob.length) {
+ return STATUS_MORE_ENTRIES;
+ }
+ return NT_STATUS_OK;
+}
+
+_PUBLIC_ NTSTATUS packet_full_request_u16(void *private_data, DATA_BLOB blob, size_t *size)
+{
+ if (blob.length < 2) {
+ return STATUS_MORE_ENTRIES;
+ }
+ *size = 2 + RSVAL(blob.data, 0);
+ if (*size > blob.length) {
+ return STATUS_MORE_ENTRIES;
+ }
+ return NT_STATUS_OK;
+}
diff --git a/source4/lib/stream/packet.h b/source4/lib/stream/packet.h
new file mode 100644
index 0000000..7f27598
--- /dev/null
+++ b/source4/lib/stream/packet.h
@@ -0,0 +1,66 @@
+/*
+ Unix SMB/CIFS Implementation.
+
+ helper layer for breaking up streams into discrete requests
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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/>.
+
+*/
+
+struct packet_context;
+struct tevent_context;
+struct tevent_fd;
+struct socket_context;
+
+typedef NTSTATUS (*packet_full_request_fn_t)(void *private_data,
+ DATA_BLOB blob, size_t *packet_size);
+typedef NTSTATUS (*packet_callback_fn_t)(void *private_data, DATA_BLOB blob);
+
+/* Used to notify that a packet has been sent, and is on the wire */
+typedef void (*packet_send_callback_fn_t)(void *private_data);
+typedef void (*packet_error_handler_fn_t)(void *private_data, NTSTATUS status);
+
+
+
+struct packet_context *packet_init(TALLOC_CTX *mem_ctx);
+void packet_set_callback(struct packet_context *pc, packet_callback_fn_t callback);
+void packet_set_error_handler(struct packet_context *pc, packet_error_handler_fn_t handler);
+void packet_set_private(struct packet_context *pc, void *private_data);
+void packet_set_full_request(struct packet_context *pc, packet_full_request_fn_t callback);
+void packet_set_socket(struct packet_context *pc, struct socket_context *sock);
+void packet_set_event_context(struct packet_context *pc, struct tevent_context *ev);
+void packet_set_fde(struct packet_context *pc, struct tevent_fd *fde);
+void packet_set_serialise(struct packet_context *pc);
+void packet_set_initial_read(struct packet_context *pc, uint32_t initial_read);
+void packet_set_nofree(struct packet_context *pc);
+void packet_recv(struct packet_context *pc);
+void packet_recv_disable(struct packet_context *pc);
+void packet_recv_enable(struct packet_context *pc);
+void packet_set_unreliable_select(struct packet_context *pc);
+NTSTATUS packet_send(struct packet_context *pc, DATA_BLOB blob);
+NTSTATUS packet_send_callback(struct packet_context *pc, DATA_BLOB blob,
+ packet_send_callback_fn_t send_callback,
+ void *private_data);
+void packet_queue_run(struct packet_context *pc);
+
+/*
+ pre-canned handlers
+*/
+NTSTATUS packet_full_request_nbt(void *private_data, DATA_BLOB blob, size_t *size);
+NTSTATUS packet_full_request_u32(void *private_data, DATA_BLOB blob, size_t *size);
+NTSTATUS packet_full_request_u16(void *private_data, DATA_BLOB blob, size_t *size);
+
+
diff --git a/source4/lib/stream/wscript_build b/source4/lib/stream/wscript_build
new file mode 100644
index 0000000..8427c17
--- /dev/null
+++ b/source4/lib/stream/wscript_build
@@ -0,0 +1,8 @@
+#!/usr/bin/env python
+
+
+bld.SAMBA_SUBSYSTEM('LIBPACKET',
+ source='packet.c',
+ deps='LIBTLS'
+ )
+
diff --git a/source4/lib/tls/tls.h b/source4/lib/tls/tls.h
new file mode 100644
index 0000000..d9b18ff
--- /dev/null
+++ b/source4/lib/tls/tls.h
@@ -0,0 +1,105 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ transport layer security handling code
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 _TLS_H_
+#define _TLS_H_
+
+#include "lib/socket/socket.h"
+
+struct loadparm_context;
+
+void tls_cert_generate(TALLOC_CTX *mem_ctx,
+ const char *hostname,
+ const char *keyfile, const char *certfile,
+ const char *cafile);
+
+struct tstream_context;
+struct tstream_tls_params;
+
+enum tls_verify_peer_state {
+ TLS_VERIFY_PEER_NO_CHECK = 0,
+#define TLS_VERIFY_PEER_NO_CHECK_STRING "no_check"
+
+ TLS_VERIFY_PEER_CA_ONLY = 10,
+#define TLS_VERIFY_PEER_CA_ONLY_STRING "ca_only"
+
+ TLS_VERIFY_PEER_CA_AND_NAME_IF_AVAILABLE = 20,
+#define TLS_VERIFY_PEER_CA_AND_NAME_IF_AVAILABLE_STRING \
+ "ca_and_name_if_available"
+
+ TLS_VERIFY_PEER_CA_AND_NAME = 30,
+#define TLS_VERIFY_PEER_CA_AND_NAME_STRING "ca_and_name"
+
+ TLS_VERIFY_PEER_AS_STRICT_AS_POSSIBLE = 9999,
+#define TLS_VERIFY_PEER_AS_STRICT_AS_POSSIBLE_STRING \
+ "as_strict_as_possible"
+};
+
+const char *tls_verify_peer_string(enum tls_verify_peer_state verify_peer);
+
+NTSTATUS tstream_tls_params_client(TALLOC_CTX *mem_ctx,
+ const char *ca_file,
+ const char *crl_file,
+ const char *tls_priority,
+ enum tls_verify_peer_state verify_peer,
+ const char *peer_name,
+ struct tstream_tls_params **_tlsp);
+
+NTSTATUS tstream_tls_params_server(TALLOC_CTX *mem_ctx,
+ const char *dns_host_name,
+ bool enabled,
+ const char *key_file,
+ const char *cert_file,
+ const char *ca_file,
+ const char *crl_file,
+ const char *dhp_file,
+ const char *tls_priority,
+ struct tstream_tls_params **_params);
+
+bool tstream_tls_params_enabled(struct tstream_tls_params *params);
+
+struct tevent_req *_tstream_tls_connect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tstream_context *plain_stream,
+ struct tstream_tls_params *tls_params,
+ const char *location);
+#define tstream_tls_connect_send(mem_ctx, ev, plain_stream, tls_params) \
+ _tstream_tls_connect_send(mem_ctx, ev, plain_stream, tls_params, __location__)
+
+int tstream_tls_connect_recv(struct tevent_req *req,
+ int *perrno,
+ TALLOC_CTX *mem_ctx,
+ struct tstream_context **tls_stream);
+
+struct tevent_req *_tstream_tls_accept_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tstream_context *plain_stream,
+ struct tstream_tls_params *tls_params,
+ const char *location);
+#define tstream_tls_accept_send(mem_ctx, ev, plain_stream, tls_params) \
+ _tstream_tls_accept_send(mem_ctx, ev, plain_stream, tls_params, __location__)
+
+int tstream_tls_accept_recv(struct tevent_req *req,
+ int *perrno,
+ TALLOC_CTX *mem_ctx,
+ struct tstream_context **tls_stream);
+
+#endif /* _TLS_H_ */
diff --git a/source4/lib/tls/tls_tstream.c b/source4/lib/tls/tls_tstream.c
new file mode 100644
index 0000000..d984add
--- /dev/null
+++ b/source4/lib/tls/tls_tstream.c
@@ -0,0 +1,1467 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 2010
+
+ 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/network.h"
+#include "system/filesys.h"
+#include "system/time.h"
+#include "../util/tevent_unix.h"
+#include "../lib/tsocket/tsocket.h"
+#include "../lib/tsocket/tsocket_internal.h"
+#include "../lib/util/util_net.h"
+#include "lib/tls/tls.h"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#define DH_BITS 2048
+
+const char *tls_verify_peer_string(enum tls_verify_peer_state verify_peer)
+{
+ switch (verify_peer) {
+ case TLS_VERIFY_PEER_NO_CHECK:
+ return TLS_VERIFY_PEER_NO_CHECK_STRING;
+
+ case TLS_VERIFY_PEER_CA_ONLY:
+ return TLS_VERIFY_PEER_CA_ONLY_STRING;
+
+ case TLS_VERIFY_PEER_CA_AND_NAME_IF_AVAILABLE:
+ return TLS_VERIFY_PEER_CA_AND_NAME_IF_AVAILABLE_STRING;
+
+ case TLS_VERIFY_PEER_CA_AND_NAME:
+ return TLS_VERIFY_PEER_CA_AND_NAME_STRING;
+
+ case TLS_VERIFY_PEER_AS_STRICT_AS_POSSIBLE:
+ return TLS_VERIFY_PEER_AS_STRICT_AS_POSSIBLE_STRING;
+ }
+
+ return "unknown tls_verify_peer_state";
+}
+
+static const struct tstream_context_ops tstream_tls_ops;
+
+struct tstream_tls {
+ struct tstream_context *plain_stream;
+ int error;
+
+ gnutls_session_t tls_session;
+
+ enum tls_verify_peer_state verify_peer;
+ const char *peer_name;
+
+ struct tevent_context *current_ev;
+
+ struct tevent_immediate *retry_im;
+
+ struct {
+ uint8_t *buf;
+ off_t ofs;
+ struct iovec iov;
+ struct tevent_req *subreq;
+ struct tevent_immediate *im;
+ } push;
+
+ struct {
+ uint8_t *buf;
+ struct iovec iov;
+ struct tevent_req *subreq;
+ } pull;
+
+ struct {
+ struct tevent_req *req;
+ } handshake;
+
+ struct {
+ off_t ofs;
+ size_t left;
+ uint8_t buffer[1024];
+ struct tevent_req *req;
+ } write;
+
+ struct {
+ off_t ofs;
+ size_t left;
+ uint8_t buffer[1024];
+ struct tevent_req *req;
+ } read;
+
+ struct {
+ struct tevent_req *req;
+ } disconnect;
+};
+
+static void tstream_tls_retry_handshake(struct tstream_context *stream);
+static void tstream_tls_retry_read(struct tstream_context *stream);
+static void tstream_tls_retry_write(struct tstream_context *stream);
+static void tstream_tls_retry_disconnect(struct tstream_context *stream);
+static void tstream_tls_retry_trigger(struct tevent_context *ctx,
+ struct tevent_immediate *im,
+ void *private_data);
+
+static void tstream_tls_retry(struct tstream_context *stream, bool deferred)
+{
+
+ struct tstream_tls *tlss =
+ tstream_context_data(stream,
+ struct tstream_tls);
+
+ if (tlss->disconnect.req) {
+ tstream_tls_retry_disconnect(stream);
+ return;
+ }
+
+ if (tlss->handshake.req) {
+ tstream_tls_retry_handshake(stream);
+ return;
+ }
+
+ if (tlss->write.req && tlss->read.req && !deferred) {
+ tevent_schedule_immediate(tlss->retry_im, tlss->current_ev,
+ tstream_tls_retry_trigger,
+ stream);
+ }
+
+ if (tlss->write.req) {
+ tstream_tls_retry_write(stream);
+ return;
+ }
+
+ if (tlss->read.req) {
+ tstream_tls_retry_read(stream);
+ return;
+ }
+}
+
+static void tstream_tls_retry_trigger(struct tevent_context *ctx,
+ struct tevent_immediate *im,
+ void *private_data)
+{
+ struct tstream_context *stream =
+ talloc_get_type_abort(private_data,
+ struct tstream_context);
+
+ tstream_tls_retry(stream, true);
+}
+
+static void tstream_tls_push_trigger_write(struct tevent_context *ev,
+ struct tevent_immediate *im,
+ void *private_data);
+
+static ssize_t tstream_tls_push_function(gnutls_transport_ptr_t ptr,
+ const void *buf, size_t size)
+{
+ struct tstream_context *stream =
+ talloc_get_type_abort(ptr,
+ struct tstream_context);
+ struct tstream_tls *tlss =
+ tstream_context_data(stream,
+ struct tstream_tls);
+ uint8_t *nbuf;
+ size_t len;
+
+ if (tlss->error != 0) {
+ errno = tlss->error;
+ return -1;
+ }
+
+ if (tlss->push.subreq) {
+ errno = EAGAIN;
+ return -1;
+ }
+
+ len = MIN(size, UINT16_MAX - tlss->push.ofs);
+
+ if (len == 0) {
+ errno = EAGAIN;
+ return -1;
+ }
+
+ nbuf = talloc_realloc(tlss, tlss->push.buf,
+ uint8_t, tlss->push.ofs + len);
+ if (nbuf == NULL) {
+ if (tlss->push.buf) {
+ errno = EAGAIN;
+ return -1;
+ }
+
+ return -1;
+ }
+ tlss->push.buf = nbuf;
+
+ memcpy(tlss->push.buf + tlss->push.ofs, buf, len);
+
+ if (tlss->push.im == NULL) {
+ tlss->push.im = tevent_create_immediate(tlss);
+ if (tlss->push.im == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+ }
+
+ if (tlss->push.ofs == 0) {
+ /*
+ * We'll do start the tstream_writev
+ * in the next event cycle.
+ *
+ * This way we can batch all push requests,
+ * if they fit into a UINT16_MAX buffer.
+ *
+ * This is important as gnutls_handshake()
+ * had a bug in some versions e.g. 2.4.1
+ * and others (See bug #7218) and it doesn't
+ * handle EAGAIN.
+ */
+ tevent_schedule_immediate(tlss->push.im,
+ tlss->current_ev,
+ tstream_tls_push_trigger_write,
+ stream);
+ }
+
+ tlss->push.ofs += len;
+ return len;
+}
+
+static void tstream_tls_push_done(struct tevent_req *subreq);
+
+static void tstream_tls_push_trigger_write(struct tevent_context *ev,
+ struct tevent_immediate *im,
+ void *private_data)
+{
+ struct tstream_context *stream =
+ talloc_get_type_abort(private_data,
+ struct tstream_context);
+ struct tstream_tls *tlss =
+ tstream_context_data(stream,
+ struct tstream_tls);
+ struct tevent_req *subreq;
+
+ if (tlss->push.subreq) {
+ /* nothing todo */
+ return;
+ }
+
+ tlss->push.iov.iov_base = (char *)tlss->push.buf;
+ tlss->push.iov.iov_len = tlss->push.ofs;
+
+ subreq = tstream_writev_send(tlss,
+ tlss->current_ev,
+ tlss->plain_stream,
+ &tlss->push.iov, 1);
+ if (subreq == NULL) {
+ tlss->error = ENOMEM;
+ tstream_tls_retry(stream, false);
+ return;
+ }
+ tevent_req_set_callback(subreq, tstream_tls_push_done, stream);
+
+ tlss->push.subreq = subreq;
+}
+
+static void tstream_tls_push_done(struct tevent_req *subreq)
+{
+ struct tstream_context *stream =
+ tevent_req_callback_data(subreq,
+ struct tstream_context);
+ struct tstream_tls *tlss =
+ tstream_context_data(stream,
+ struct tstream_tls);
+ int ret;
+ int sys_errno;
+
+ tlss->push.subreq = NULL;
+ ZERO_STRUCT(tlss->push.iov);
+ TALLOC_FREE(tlss->push.buf);
+ tlss->push.ofs = 0;
+
+ ret = tstream_writev_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ tlss->error = sys_errno;
+ tstream_tls_retry(stream, false);
+ return;
+ }
+
+ tstream_tls_retry(stream, false);
+}
+
+static void tstream_tls_pull_done(struct tevent_req *subreq);
+
+static ssize_t tstream_tls_pull_function(gnutls_transport_ptr_t ptr,
+ void *buf, size_t size)
+{
+ struct tstream_context *stream =
+ talloc_get_type_abort(ptr,
+ struct tstream_context);
+ struct tstream_tls *tlss =
+ tstream_context_data(stream,
+ struct tstream_tls);
+ struct tevent_req *subreq;
+ size_t len;
+
+ if (tlss->error != 0) {
+ errno = tlss->error;
+ return -1;
+ }
+
+ if (tlss->pull.subreq) {
+ errno = EAGAIN;
+ return -1;
+ }
+
+ if (tlss->pull.iov.iov_base) {
+ uint8_t *b;
+ size_t n;
+
+ b = (uint8_t *)tlss->pull.iov.iov_base;
+
+ n = MIN(tlss->pull.iov.iov_len, size);
+ memcpy(buf, b, n);
+
+ tlss->pull.iov.iov_len -= n;
+ b += n;
+ tlss->pull.iov.iov_base = (char *)b;
+ if (tlss->pull.iov.iov_len == 0) {
+ tlss->pull.iov.iov_base = NULL;
+ TALLOC_FREE(tlss->pull.buf);
+ }
+
+ return n;
+ }
+
+ if (size == 0) {
+ return 0;
+ }
+
+ len = MIN(size, UINT16_MAX);
+
+ tlss->pull.buf = talloc_array(tlss, uint8_t, len);
+ if (tlss->pull.buf == NULL) {
+ return -1;
+ }
+
+ tlss->pull.iov.iov_base = (char *)tlss->pull.buf;
+ tlss->pull.iov.iov_len = len;
+
+ subreq = tstream_readv_send(tlss,
+ tlss->current_ev,
+ tlss->plain_stream,
+ &tlss->pull.iov, 1);
+ if (subreq == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+ tevent_req_set_callback(subreq, tstream_tls_pull_done, stream);
+
+ tlss->pull.subreq = subreq;
+ errno = EAGAIN;
+ return -1;
+}
+
+static void tstream_tls_pull_done(struct tevent_req *subreq)
+{
+ struct tstream_context *stream =
+ tevent_req_callback_data(subreq,
+ struct tstream_context);
+ struct tstream_tls *tlss =
+ tstream_context_data(stream,
+ struct tstream_tls);
+ int ret;
+ int sys_errno;
+
+ tlss->pull.subreq = NULL;
+
+ ret = tstream_readv_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ tlss->error = sys_errno;
+ tstream_tls_retry(stream, false);
+ return;
+ }
+
+ tstream_tls_retry(stream, false);
+}
+
+static int tstream_tls_destructor(struct tstream_tls *tlss)
+{
+ if (tlss->tls_session) {
+ gnutls_deinit(tlss->tls_session);
+ tlss->tls_session = NULL;
+ }
+
+ return 0;
+}
+
+static ssize_t tstream_tls_pending_bytes(struct tstream_context *stream)
+{
+ struct tstream_tls *tlss =
+ tstream_context_data(stream,
+ struct tstream_tls);
+ size_t ret;
+
+ if (tlss->error != 0) {
+ errno = tlss->error;
+ return -1;
+ }
+
+ ret = gnutls_record_check_pending(tlss->tls_session);
+ ret += tlss->read.left;
+
+ return ret;
+}
+
+struct tstream_tls_readv_state {
+ struct tstream_context *stream;
+
+ struct iovec *vector;
+ int count;
+
+ int ret;
+};
+
+static void tstream_tls_readv_crypt_next(struct tevent_req *req);
+
+static struct tevent_req *tstream_tls_readv_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tstream_context *stream,
+ struct iovec *vector,
+ size_t count)
+{
+ struct tstream_tls *tlss =
+ tstream_context_data(stream,
+ struct tstream_tls);
+ struct tevent_req *req;
+ struct tstream_tls_readv_state *state;
+
+ tlss->read.req = NULL;
+ tlss->current_ev = ev;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct tstream_tls_readv_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->stream = stream;
+ state->ret = 0;
+
+ if (tlss->error != 0) {
+ tevent_req_error(req, tlss->error);
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * we make a copy of the vector so we can change the structure
+ */
+ state->vector = talloc_array(state, struct iovec, count);
+ if (tevent_req_nomem(state->vector, req)) {
+ return tevent_req_post(req, ev);
+ }
+ memcpy(state->vector, vector, sizeof(struct iovec) * count);
+ state->count = count;
+
+ tstream_tls_readv_crypt_next(req);
+ if (!tevent_req_is_in_progress(req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void tstream_tls_readv_crypt_next(struct tevent_req *req)
+{
+ struct tstream_tls_readv_state *state =
+ tevent_req_data(req,
+ struct tstream_tls_readv_state);
+ struct tstream_tls *tlss =
+ tstream_context_data(state->stream,
+ struct tstream_tls);
+
+ /*
+ * copy the pending buffer first
+ */
+ while (tlss->read.left > 0 && state->count > 0) {
+ uint8_t *base = (uint8_t *)state->vector[0].iov_base;
+ size_t len = MIN(tlss->read.left, state->vector[0].iov_len);
+
+ memcpy(base, tlss->read.buffer + tlss->read.ofs, len);
+
+ base += len;
+ state->vector[0].iov_base = (char *) base;
+ state->vector[0].iov_len -= len;
+
+ tlss->read.ofs += len;
+ tlss->read.left -= len;
+
+ if (state->vector[0].iov_len == 0) {
+ state->vector += 1;
+ state->count -= 1;
+ }
+
+ state->ret += len;
+ }
+
+ if (state->count == 0) {
+ tevent_req_done(req);
+ return;
+ }
+
+ tlss->read.req = req;
+ tstream_tls_retry_read(state->stream);
+}
+
+static void tstream_tls_retry_read(struct tstream_context *stream)
+{
+ struct tstream_tls *tlss =
+ tstream_context_data(stream,
+ struct tstream_tls);
+ struct tevent_req *req = tlss->read.req;
+ int ret;
+
+ if (tlss->error != 0) {
+ tevent_req_error(req, tlss->error);
+ return;
+ }
+
+ tlss->read.left = 0;
+ tlss->read.ofs = 0;
+
+ ret = gnutls_record_recv(tlss->tls_session,
+ tlss->read.buffer,
+ sizeof(tlss->read.buffer));
+ if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
+ return;
+ }
+
+ tlss->read.req = NULL;
+
+ if (gnutls_error_is_fatal(ret) != 0) {
+ DEBUG(1,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+ tlss->error = EIO;
+ tevent_req_error(req, tlss->error);
+ return;
+ }
+
+ if (ret == 0) {
+ tlss->error = EPIPE;
+ tevent_req_error(req, tlss->error);
+ return;
+ }
+
+ tlss->read.left = ret;
+ tstream_tls_readv_crypt_next(req);
+}
+
+static int tstream_tls_readv_recv(struct tevent_req *req,
+ int *perrno)
+{
+ struct tstream_tls_readv_state *state =
+ tevent_req_data(req,
+ struct tstream_tls_readv_state);
+ struct tstream_tls *tlss =
+ tstream_context_data(state->stream,
+ struct tstream_tls);
+ int ret;
+
+ tlss->read.req = NULL;
+
+ ret = tsocket_simple_int_recv(req, perrno);
+ if (ret == 0) {
+ ret = state->ret;
+ }
+
+ tevent_req_received(req);
+ return ret;
+}
+
+struct tstream_tls_writev_state {
+ struct tstream_context *stream;
+
+ struct iovec *vector;
+ int count;
+
+ int ret;
+};
+
+static void tstream_tls_writev_crypt_next(struct tevent_req *req);
+
+static struct tevent_req *tstream_tls_writev_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tstream_context *stream,
+ const struct iovec *vector,
+ size_t count)
+{
+ struct tstream_tls *tlss =
+ tstream_context_data(stream,
+ struct tstream_tls);
+ struct tevent_req *req;
+ struct tstream_tls_writev_state *state;
+
+ tlss->write.req = NULL;
+ tlss->current_ev = ev;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct tstream_tls_writev_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->stream = stream;
+ state->ret = 0;
+
+ if (tlss->error != 0) {
+ tevent_req_error(req, tlss->error);
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * we make a copy of the vector so we can change the structure
+ */
+ state->vector = talloc_array(state, struct iovec, count);
+ if (tevent_req_nomem(state->vector, req)) {
+ return tevent_req_post(req, ev);
+ }
+ memcpy(state->vector, vector, sizeof(struct iovec) * count);
+ state->count = count;
+
+ tstream_tls_writev_crypt_next(req);
+ if (!tevent_req_is_in_progress(req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void tstream_tls_writev_crypt_next(struct tevent_req *req)
+{
+ struct tstream_tls_writev_state *state =
+ tevent_req_data(req,
+ struct tstream_tls_writev_state);
+ struct tstream_tls *tlss =
+ tstream_context_data(state->stream,
+ struct tstream_tls);
+
+ tlss->write.left = sizeof(tlss->write.buffer);
+ tlss->write.ofs = 0;
+
+ /*
+ * first fill our buffer
+ */
+ while (tlss->write.left > 0 && state->count > 0) {
+ uint8_t *base = (uint8_t *)state->vector[0].iov_base;
+ size_t len = MIN(tlss->write.left, state->vector[0].iov_len);
+
+ memcpy(tlss->write.buffer + tlss->write.ofs, base, len);
+
+ base += len;
+ state->vector[0].iov_base = (char *) base;
+ state->vector[0].iov_len -= len;
+
+ tlss->write.ofs += len;
+ tlss->write.left -= len;
+
+ if (state->vector[0].iov_len == 0) {
+ state->vector += 1;
+ state->count -= 1;
+ }
+
+ state->ret += len;
+ }
+
+ if (tlss->write.ofs == 0) {
+ tevent_req_done(req);
+ return;
+ }
+
+ tlss->write.left = tlss->write.ofs;
+ tlss->write.ofs = 0;
+
+ tlss->write.req = req;
+ tstream_tls_retry_write(state->stream);
+}
+
+static void tstream_tls_retry_write(struct tstream_context *stream)
+{
+ struct tstream_tls *tlss =
+ tstream_context_data(stream,
+ struct tstream_tls);
+ struct tevent_req *req = tlss->write.req;
+ int ret;
+
+ if (tlss->error != 0) {
+ tevent_req_error(req, tlss->error);
+ return;
+ }
+
+ ret = gnutls_record_send(tlss->tls_session,
+ tlss->write.buffer + tlss->write.ofs,
+ tlss->write.left);
+ if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
+ return;
+ }
+
+ tlss->write.req = NULL;
+
+ if (gnutls_error_is_fatal(ret) != 0) {
+ DEBUG(1,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+ tlss->error = EIO;
+ tevent_req_error(req, tlss->error);
+ return;
+ }
+
+ if (ret == 0) {
+ tlss->error = EPIPE;
+ tevent_req_error(req, tlss->error);
+ return;
+ }
+
+ tlss->write.ofs += ret;
+ tlss->write.left -= ret;
+
+ if (tlss->write.left > 0) {
+ tlss->write.req = req;
+ tstream_tls_retry_write(stream);
+ return;
+ }
+
+ tstream_tls_writev_crypt_next(req);
+}
+
+static int tstream_tls_writev_recv(struct tevent_req *req,
+ int *perrno)
+{
+ struct tstream_tls_writev_state *state =
+ tevent_req_data(req,
+ struct tstream_tls_writev_state);
+ struct tstream_tls *tlss =
+ tstream_context_data(state->stream,
+ struct tstream_tls);
+ int ret;
+
+ tlss->write.req = NULL;
+
+ ret = tsocket_simple_int_recv(req, perrno);
+ if (ret == 0) {
+ ret = state->ret;
+ }
+
+ tevent_req_received(req);
+ return ret;
+}
+
+struct tstream_tls_disconnect_state {
+ uint8_t _dummy;
+};
+
+static struct tevent_req *tstream_tls_disconnect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tstream_context *stream)
+{
+ struct tstream_tls *tlss =
+ tstream_context_data(stream,
+ struct tstream_tls);
+ struct tevent_req *req;
+ struct tstream_tls_disconnect_state *state;
+
+ tlss->disconnect.req = NULL;
+ tlss->current_ev = ev;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct tstream_tls_disconnect_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (tlss->error != 0) {
+ tevent_req_error(req, tlss->error);
+ return tevent_req_post(req, ev);
+ }
+
+ tlss->disconnect.req = req;
+ tstream_tls_retry_disconnect(stream);
+ if (!tevent_req_is_in_progress(req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void tstream_tls_retry_disconnect(struct tstream_context *stream)
+{
+ struct tstream_tls *tlss =
+ tstream_context_data(stream,
+ struct tstream_tls);
+ struct tevent_req *req = tlss->disconnect.req;
+ int ret;
+
+ if (tlss->error != 0) {
+ tevent_req_error(req, tlss->error);
+ return;
+ }
+
+ ret = gnutls_bye(tlss->tls_session, GNUTLS_SHUT_WR);
+ if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
+ return;
+ }
+
+ tlss->disconnect.req = NULL;
+
+ if (gnutls_error_is_fatal(ret) != 0) {
+ DEBUG(1,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+ tlss->error = EIO;
+ tevent_req_error(req, tlss->error);
+ return;
+ }
+
+ if (ret != GNUTLS_E_SUCCESS) {
+ DEBUG(1,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+ tlss->error = EIO;
+ tevent_req_error(req, tlss->error);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static int tstream_tls_disconnect_recv(struct tevent_req *req,
+ int *perrno)
+{
+ int ret;
+
+ ret = tsocket_simple_int_recv(req, perrno);
+
+ tevent_req_received(req);
+ return ret;
+}
+
+static const struct tstream_context_ops tstream_tls_ops = {
+ .name = "tls",
+
+ .pending_bytes = tstream_tls_pending_bytes,
+
+ .readv_send = tstream_tls_readv_send,
+ .readv_recv = tstream_tls_readv_recv,
+
+ .writev_send = tstream_tls_writev_send,
+ .writev_recv = tstream_tls_writev_recv,
+
+ .disconnect_send = tstream_tls_disconnect_send,
+ .disconnect_recv = tstream_tls_disconnect_recv,
+};
+
+struct tstream_tls_params {
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_dh_params_t dh_params;
+ const char *tls_priority;
+ bool tls_enabled;
+ enum tls_verify_peer_state verify_peer;
+ const char *peer_name;
+};
+
+static int tstream_tls_params_destructor(struct tstream_tls_params *tlsp)
+{
+ if (tlsp->x509_cred) {
+ gnutls_certificate_free_credentials(tlsp->x509_cred);
+ tlsp->x509_cred = NULL;
+ }
+ if (tlsp->dh_params) {
+ gnutls_dh_params_deinit(tlsp->dh_params);
+ tlsp->dh_params = NULL;
+ }
+
+ return 0;
+}
+
+bool tstream_tls_params_enabled(struct tstream_tls_params *tlsp)
+{
+ return tlsp->tls_enabled;
+}
+
+NTSTATUS tstream_tls_params_client(TALLOC_CTX *mem_ctx,
+ const char *ca_file,
+ const char *crl_file,
+ const char *tls_priority,
+ enum tls_verify_peer_state verify_peer,
+ const char *peer_name,
+ struct tstream_tls_params **_tlsp)
+{
+ struct tstream_tls_params *tlsp;
+ int ret;
+
+ tlsp = talloc_zero(mem_ctx, struct tstream_tls_params);
+ NT_STATUS_HAVE_NO_MEMORY(tlsp);
+
+ talloc_set_destructor(tlsp, tstream_tls_params_destructor);
+
+ tlsp->verify_peer = verify_peer;
+ if (peer_name != NULL) {
+ tlsp->peer_name = talloc_strdup(tlsp, peer_name);
+ if (tlsp->peer_name == NULL) {
+ talloc_free(tlsp);
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else if (tlsp->verify_peer >= TLS_VERIFY_PEER_CA_AND_NAME) {
+ DEBUG(0,("TLS failed to missing peer_name - "
+ "with 'tls verify peer = %s'\n",
+ tls_verify_peer_string(tlsp->verify_peer)));
+ talloc_free(tlsp);
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ ret = gnutls_certificate_allocate_credentials(&tlsp->x509_cred);
+ if (ret != GNUTLS_E_SUCCESS) {
+ DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+ talloc_free(tlsp);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (ca_file && *ca_file && file_exist(ca_file)) {
+ ret = gnutls_certificate_set_x509_trust_file(tlsp->x509_cred,
+ ca_file,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ DEBUG(0,("TLS failed to initialise cafile %s - %s\n",
+ ca_file, gnutls_strerror(ret)));
+ talloc_free(tlsp);
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+ } else if (tlsp->verify_peer >= TLS_VERIFY_PEER_CA_ONLY) {
+ DEBUG(0,("TLS failed to missing cafile %s - "
+ "with 'tls verify peer = %s'\n",
+ ca_file,
+ tls_verify_peer_string(tlsp->verify_peer)));
+ talloc_free(tlsp);
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ if (crl_file && *crl_file && file_exist(crl_file)) {
+ ret = gnutls_certificate_set_x509_crl_file(tlsp->x509_cred,
+ crl_file,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ DEBUG(0,("TLS failed to initialise crlfile %s - %s\n",
+ crl_file, gnutls_strerror(ret)));
+ talloc_free(tlsp);
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+ } else if (tlsp->verify_peer >= TLS_VERIFY_PEER_AS_STRICT_AS_POSSIBLE) {
+ DEBUG(0,("TLS failed to missing crlfile %s - "
+ "with 'tls verify peer = %s'\n",
+ crl_file,
+ tls_verify_peer_string(tlsp->verify_peer)));
+ talloc_free(tlsp);
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ tlsp->tls_priority = talloc_strdup(tlsp, tls_priority);
+ if (tlsp->tls_priority == NULL) {
+ talloc_free(tlsp);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ tlsp->tls_enabled = true;
+
+ *_tlsp = tlsp;
+ return NT_STATUS_OK;
+}
+
+struct tstream_tls_connect_state {
+ struct tstream_context *tls_stream;
+};
+
+struct tevent_req *_tstream_tls_connect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tstream_context *plain_stream,
+ struct tstream_tls_params *tls_params,
+ const char *location)
+{
+ struct tevent_req *req;
+ struct tstream_tls_connect_state *state;
+ const char *error_pos;
+ struct tstream_tls *tlss;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct tstream_tls_connect_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->tls_stream = tstream_context_create(state,
+ &tstream_tls_ops,
+ &tlss,
+ struct tstream_tls,
+ location);
+ if (tevent_req_nomem(state->tls_stream, req)) {
+ return tevent_req_post(req, ev);
+ }
+ ZERO_STRUCTP(tlss);
+ talloc_set_destructor(tlss, tstream_tls_destructor);
+
+ tlss->plain_stream = plain_stream;
+ tlss->verify_peer = tls_params->verify_peer;
+ if (tls_params->peer_name != NULL) {
+ tlss->peer_name = talloc_strdup(tlss, tls_params->peer_name);
+ if (tevent_req_nomem(tlss->peer_name, req)) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ tlss->current_ev = ev;
+ tlss->retry_im = tevent_create_immediate(tlss);
+ if (tevent_req_nomem(tlss->retry_im, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ ret = gnutls_init(&tlss->tls_session, GNUTLS_CLIENT);
+ if (ret != GNUTLS_E_SUCCESS) {
+ DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+ tevent_req_error(req, EINVAL);
+ return tevent_req_post(req, ev);
+ }
+
+ ret = gnutls_set_default_priority(tlss->tls_session);
+ if (ret != GNUTLS_E_SUCCESS) {
+ DBG_ERR("TLS %s - %s. Failed to set default priorities\n",
+ __location__, gnutls_strerror(ret));
+ tevent_req_error(req, EINVAL);
+ return tevent_req_post(req, ev);
+ }
+
+ if (strlen(tls_params->tls_priority) > 0) {
+ ret = gnutls_priority_set_direct(tlss->tls_session,
+ tls_params->tls_priority,
+ &error_pos);
+ if (ret != GNUTLS_E_SUCCESS) {
+ DEBUG(0,("TLS %s - %s. Check 'tls priority' option at '%s'\n",
+ __location__, gnutls_strerror(ret), error_pos));
+ tevent_req_error(req, EINVAL);
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ ret = gnutls_credentials_set(tlss->tls_session,
+ GNUTLS_CRD_CERTIFICATE,
+ tls_params->x509_cred);
+ if (ret != GNUTLS_E_SUCCESS) {
+ DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+ tevent_req_error(req, EINVAL);
+ return tevent_req_post(req, ev);
+ }
+
+ gnutls_transport_set_ptr(tlss->tls_session,
+ (gnutls_transport_ptr_t)state->tls_stream);
+ gnutls_transport_set_pull_function(tlss->tls_session,
+ (gnutls_pull_func)tstream_tls_pull_function);
+ gnutls_transport_set_push_function(tlss->tls_session,
+ (gnutls_push_func)tstream_tls_push_function);
+
+ tlss->handshake.req = req;
+ tstream_tls_retry_handshake(state->tls_stream);
+ if (!tevent_req_is_in_progress(req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+int tstream_tls_connect_recv(struct tevent_req *req,
+ int *perrno,
+ TALLOC_CTX *mem_ctx,
+ struct tstream_context **tls_stream)
+{
+ struct tstream_tls_connect_state *state =
+ tevent_req_data(req,
+ struct tstream_tls_connect_state);
+
+ if (tevent_req_is_unix_error(req, perrno)) {
+ tevent_req_received(req);
+ return -1;
+ }
+
+ *tls_stream = talloc_move(mem_ctx, &state->tls_stream);
+ tevent_req_received(req);
+ return 0;
+}
+
+/*
+ initialise global tls state
+*/
+NTSTATUS tstream_tls_params_server(TALLOC_CTX *mem_ctx,
+ const char *dns_host_name,
+ bool enabled,
+ const char *key_file,
+ const char *cert_file,
+ const char *ca_file,
+ const char *crl_file,
+ const char *dhp_file,
+ const char *tls_priority,
+ struct tstream_tls_params **_tlsp)
+{
+ struct tstream_tls_params *tlsp;
+ int ret;
+ struct stat st;
+
+ if (!enabled || key_file == NULL || *key_file == 0) {
+ tlsp = talloc_zero(mem_ctx, struct tstream_tls_params);
+ NT_STATUS_HAVE_NO_MEMORY(tlsp);
+ talloc_set_destructor(tlsp, tstream_tls_params_destructor);
+ tlsp->tls_enabled = false;
+
+ *_tlsp = tlsp;
+ return NT_STATUS_OK;
+ }
+
+ tlsp = talloc_zero(mem_ctx, struct tstream_tls_params);
+ NT_STATUS_HAVE_NO_MEMORY(tlsp);
+
+ talloc_set_destructor(tlsp, tstream_tls_params_destructor);
+
+ if (!file_exist(ca_file)) {
+ tls_cert_generate(tlsp, dns_host_name,
+ key_file, cert_file, ca_file);
+ }
+
+ if (file_exist(key_file) &&
+ !file_check_permissions(key_file, geteuid(), 0600, &st))
+ {
+ DEBUG(0, ("Invalid permissions on TLS private key file '%s':\n"
+ "owner uid %u should be %u, mode 0%o should be 0%o\n"
+ "This is known as CVE-2013-4476.\n"
+ "Removing all tls .pem files will cause an "
+ "auto-regeneration with the correct permissions.\n",
+ key_file,
+ (unsigned int)st.st_uid, geteuid(),
+ (unsigned int)(st.st_mode & 0777), 0600));
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+
+ ret = gnutls_certificate_allocate_credentials(&tlsp->x509_cred);
+ if (ret != GNUTLS_E_SUCCESS) {
+ DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+ talloc_free(tlsp);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (ca_file && *ca_file) {
+ ret = gnutls_certificate_set_x509_trust_file(tlsp->x509_cred,
+ ca_file,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ DEBUG(0,("TLS failed to initialise cafile %s - %s\n",
+ ca_file, gnutls_strerror(ret)));
+ talloc_free(tlsp);
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+ }
+
+ if (crl_file && *crl_file) {
+ ret = gnutls_certificate_set_x509_crl_file(tlsp->x509_cred,
+ crl_file,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ DEBUG(0,("TLS failed to initialise crlfile %s - %s\n",
+ crl_file, gnutls_strerror(ret)));
+ talloc_free(tlsp);
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+ }
+
+ ret = gnutls_certificate_set_x509_key_file(tlsp->x509_cred,
+ cert_file, key_file,
+ GNUTLS_X509_FMT_PEM);
+ if (ret != GNUTLS_E_SUCCESS) {
+ DEBUG(0,("TLS failed to initialise certfile %s and keyfile %s - %s\n",
+ cert_file, key_file, gnutls_strerror(ret)));
+ talloc_free(tlsp);
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+
+ ret = gnutls_dh_params_init(&tlsp->dh_params);
+ if (ret != GNUTLS_E_SUCCESS) {
+ DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+ talloc_free(tlsp);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (dhp_file && *dhp_file) {
+ gnutls_datum_t dhparms;
+ size_t size;
+
+ dhparms.data = (uint8_t *)file_load(dhp_file, &size, 0, tlsp);
+
+ if (!dhparms.data) {
+ DEBUG(0,("TLS failed to read DH Parms from %s - %d:%s\n",
+ dhp_file, errno, strerror(errno)));
+ talloc_free(tlsp);
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+ dhparms.size = size;
+
+ ret = gnutls_dh_params_import_pkcs3(tlsp->dh_params,
+ &dhparms,
+ GNUTLS_X509_FMT_PEM);
+ if (ret != GNUTLS_E_SUCCESS) {
+ DEBUG(0,("TLS failed to import pkcs3 %s - %s\n",
+ dhp_file, gnutls_strerror(ret)));
+ talloc_free(tlsp);
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+ } else {
+ ret = gnutls_dh_params_generate2(tlsp->dh_params, DH_BITS);
+ if (ret != GNUTLS_E_SUCCESS) {
+ DEBUG(0,("TLS failed to generate dh_params - %s\n",
+ gnutls_strerror(ret)));
+ talloc_free(tlsp);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ gnutls_certificate_set_dh_params(tlsp->x509_cred, tlsp->dh_params);
+
+ tlsp->tls_priority = talloc_strdup(tlsp, tls_priority);
+ if (tlsp->tls_priority == NULL) {
+ talloc_free(tlsp);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ tlsp->tls_enabled = true;
+
+ *_tlsp = tlsp;
+ return NT_STATUS_OK;
+}
+
+struct tstream_tls_accept_state {
+ struct tstream_context *tls_stream;
+};
+
+struct tevent_req *_tstream_tls_accept_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tstream_context *plain_stream,
+ struct tstream_tls_params *tlsp,
+ const char *location)
+{
+ struct tevent_req *req;
+ struct tstream_tls_accept_state *state;
+ struct tstream_tls *tlss;
+ const char *error_pos;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct tstream_tls_accept_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->tls_stream = tstream_context_create(state,
+ &tstream_tls_ops,
+ &tlss,
+ struct tstream_tls,
+ location);
+ if (tevent_req_nomem(state->tls_stream, req)) {
+ return tevent_req_post(req, ev);
+ }
+ ZERO_STRUCTP(tlss);
+ talloc_set_destructor(tlss, tstream_tls_destructor);
+
+ tlss->plain_stream = plain_stream;
+
+ tlss->current_ev = ev;
+ tlss->retry_im = tevent_create_immediate(tlss);
+ if (tevent_req_nomem(tlss->retry_im, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ ret = gnutls_init(&tlss->tls_session, GNUTLS_SERVER);
+ if (ret != GNUTLS_E_SUCCESS) {
+ DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+ tevent_req_error(req, EINVAL);
+ return tevent_req_post(req, ev);
+ }
+
+ ret = gnutls_set_default_priority(tlss->tls_session);
+ if (ret != GNUTLS_E_SUCCESS) {
+ DBG_ERR("TLS %s - %s. Failed to set default priorities\n",
+ __location__, gnutls_strerror(ret));
+ tevent_req_error(req, EINVAL);
+ return tevent_req_post(req, ev);
+ }
+
+ if (strlen(tlsp->tls_priority) > 0) {
+ ret = gnutls_priority_set_direct(tlss->tls_session,
+ tlsp->tls_priority,
+ &error_pos);
+ if (ret != GNUTLS_E_SUCCESS) {
+ DEBUG(0,("TLS %s - %s. Check 'tls priority' option at '%s'\n",
+ __location__, gnutls_strerror(ret), error_pos));
+ tevent_req_error(req, EINVAL);
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ ret = gnutls_credentials_set(tlss->tls_session, GNUTLS_CRD_CERTIFICATE,
+ tlsp->x509_cred);
+ if (ret != GNUTLS_E_SUCCESS) {
+ DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+ tevent_req_error(req, EINVAL);
+ return tevent_req_post(req, ev);
+ }
+
+ gnutls_certificate_server_set_request(tlss->tls_session,
+ GNUTLS_CERT_REQUEST);
+ gnutls_dh_set_prime_bits(tlss->tls_session, DH_BITS);
+
+ gnutls_transport_set_ptr(tlss->tls_session,
+ (gnutls_transport_ptr_t)state->tls_stream);
+ gnutls_transport_set_pull_function(tlss->tls_session,
+ (gnutls_pull_func)tstream_tls_pull_function);
+ gnutls_transport_set_push_function(tlss->tls_session,
+ (gnutls_push_func)tstream_tls_push_function);
+
+ tlss->handshake.req = req;
+ tstream_tls_retry_handshake(state->tls_stream);
+ if (!tevent_req_is_in_progress(req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void tstream_tls_retry_handshake(struct tstream_context *stream)
+{
+ struct tstream_tls *tlss =
+ tstream_context_data(stream,
+ struct tstream_tls);
+ struct tevent_req *req = tlss->handshake.req;
+ int ret;
+
+ if (tlss->error != 0) {
+ tevent_req_error(req, tlss->error);
+ return;
+ }
+
+ ret = gnutls_handshake(tlss->tls_session);
+ if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
+ return;
+ }
+
+ tlss->handshake.req = NULL;
+
+ if (gnutls_error_is_fatal(ret) != 0) {
+ DEBUG(1,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+ tlss->error = EIO;
+ tevent_req_error(req, tlss->error);
+ return;
+ }
+
+ if (ret != GNUTLS_E_SUCCESS) {
+ DEBUG(1,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+ tlss->error = EIO;
+ tevent_req_error(req, tlss->error);
+ return;
+ }
+
+ if (tlss->verify_peer >= TLS_VERIFY_PEER_CA_ONLY) {
+ unsigned int status = UINT32_MAX;
+ bool ip = true;
+ const char *hostname = NULL;
+
+ if (tlss->peer_name != NULL) {
+ ip = is_ipaddress(tlss->peer_name);
+ }
+
+ if (!ip) {
+ hostname = tlss->peer_name;
+ }
+
+ if (tlss->verify_peer == TLS_VERIFY_PEER_CA_ONLY) {
+ hostname = NULL;
+ }
+
+ if (tlss->verify_peer >= TLS_VERIFY_PEER_CA_AND_NAME) {
+ if (hostname == NULL) {
+ DEBUG(1,("TLS %s - no hostname available for "
+ "verify_peer[%s] and peer_name[%s]\n",
+ __location__,
+ tls_verify_peer_string(tlss->verify_peer),
+ tlss->peer_name));
+ tlss->error = EINVAL;
+ tevent_req_error(req, tlss->error);
+ return;
+ }
+ }
+
+ ret = gnutls_certificate_verify_peers3(tlss->tls_session,
+ hostname,
+ &status);
+ if (ret != GNUTLS_E_SUCCESS) {
+ DEBUG(1,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
+ tlss->error = EIO;
+ tevent_req_error(req, tlss->error);
+ return;
+ }
+
+ if (status != 0) {
+ DEBUG(1,("TLS %s - check failed for "
+ "verify_peer[%s] and peer_name[%s] "
+ "status 0x%x (%s%s%s%s%s%s%s%s)\n",
+ __location__,
+ tls_verify_peer_string(tlss->verify_peer),
+ tlss->peer_name,
+ status,
+ status & GNUTLS_CERT_INVALID ? "invalid " : "",
+ status & GNUTLS_CERT_REVOKED ? "revoked " : "",
+ status & GNUTLS_CERT_SIGNER_NOT_FOUND ?
+ "signer_not_found " : "",
+ status & GNUTLS_CERT_SIGNER_NOT_CA ?
+ "signer_not_ca " : "",
+ status & GNUTLS_CERT_INSECURE_ALGORITHM ?
+ "insecure_algorithm " : "",
+ status & GNUTLS_CERT_NOT_ACTIVATED ?
+ "not_activated " : "",
+ status & GNUTLS_CERT_EXPIRED ?
+ "expired " : "",
+ status & GNUTLS_CERT_UNEXPECTED_OWNER ?
+ "unexptected_owner " : ""));
+ tlss->error = EINVAL;
+ tevent_req_error(req, tlss->error);
+ return;
+ }
+ }
+
+ tevent_req_done(req);
+}
+
+int tstream_tls_accept_recv(struct tevent_req *req,
+ int *perrno,
+ TALLOC_CTX *mem_ctx,
+ struct tstream_context **tls_stream)
+{
+ struct tstream_tls_accept_state *state =
+ tevent_req_data(req,
+ struct tstream_tls_accept_state);
+
+ if (tevent_req_is_unix_error(req, perrno)) {
+ tevent_req_received(req);
+ return -1;
+ }
+
+ *tls_stream = talloc_move(mem_ctx, &state->tls_stream);
+ tevent_req_received(req);
+ return 0;
+}
diff --git a/source4/lib/tls/tlscert.c b/source4/lib/tls/tlscert.c
new file mode 100644
index 0000000..36482e3
--- /dev/null
+++ b/source4/lib/tls/tlscert.c
@@ -0,0 +1,159 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ auto-generate self signed TLS certificates
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 "lib/tls/tls.h"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#define ORGANISATION_NAME "Samba Administration"
+#define CA_NAME "Samba - temporary autogenerated CA certificate"
+#define UNIT_NAME "Samba - temporary autogenerated HOST certificate"
+#define LIFETIME 700*24*60*60
+
+/* FIPS140-2 only allows 2048 or 3072 prime sizes. */
+#define RSA_BITS gnutls_fips140_mode_enabled() ? 3072 : 4096
+
+/*
+ auto-generate a set of self signed certificates
+*/
+void tls_cert_generate(TALLOC_CTX *mem_ctx,
+ const char *hostname,
+ const char *keyfile, const char *certfile,
+ const char *cafile)
+{
+ gnutls_x509_crt_t cacrt, crt;
+ gnutls_x509_privkey_t key, cakey;
+ uint32_t serial = (uint32_t)time(NULL);
+ unsigned char keyid[100];
+ char buf[4096];
+ size_t bufsize;
+ size_t keyidsize = sizeof(keyid);
+ time_t activation = time(NULL), expiry = activation + LIFETIME;
+ int ret;
+
+ if (file_exist(keyfile) || file_exist(certfile) || file_exist(cafile)) {
+ DEBUG(0,("TLS autogeneration skipped - some TLS files already exist\n"));
+ return;
+ }
+
+#define TLSCHECK(call) do { \
+ ret = call; \
+ if (ret < 0) { \
+ DEBUG(0,("TLS %s - %s\n", #call, gnutls_strerror(ret))); \
+ goto failed; \
+ } \
+} while (0)
+
+ DEBUG(0,("Attempting to autogenerate TLS self-signed keys for https for hostname '%s'\n",
+ hostname));
+
+ DEBUG(3,("Generating private key\n"));
+ TLSCHECK(gnutls_x509_privkey_init(&key));
+ TLSCHECK(gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, RSA_BITS, 0));
+
+ DEBUG(3,("Generating CA private key\n"));
+ TLSCHECK(gnutls_x509_privkey_init(&cakey));
+ TLSCHECK(gnutls_x509_privkey_generate(cakey, GNUTLS_PK_RSA, RSA_BITS, 0));
+
+ DEBUG(3,("Generating CA certificate\n"));
+ TLSCHECK(gnutls_x509_crt_init(&cacrt));
+ TLSCHECK(gnutls_x509_crt_set_dn_by_oid(cacrt,
+ GNUTLS_OID_X520_ORGANIZATION_NAME, 0,
+ ORGANISATION_NAME, strlen(ORGANISATION_NAME)));
+ TLSCHECK(gnutls_x509_crt_set_dn_by_oid(cacrt,
+ GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0,
+ CA_NAME, strlen(CA_NAME)));
+ TLSCHECK(gnutls_x509_crt_set_dn_by_oid(cacrt,
+ GNUTLS_OID_X520_COMMON_NAME, 0,
+ hostname, strlen(hostname)));
+ TLSCHECK(gnutls_x509_crt_set_key(cacrt, cakey));
+ TLSCHECK(gnutls_x509_crt_set_serial(cacrt, &serial, sizeof(serial)));
+ TLSCHECK(gnutls_x509_crt_set_activation_time(cacrt, activation));
+ TLSCHECK(gnutls_x509_crt_set_expiration_time(cacrt, expiry));
+ TLSCHECK(gnutls_x509_crt_set_ca_status(cacrt, 1));
+ TLSCHECK(gnutls_x509_crt_set_key_usage(cacrt, GNUTLS_KEY_KEY_CERT_SIGN | GNUTLS_KEY_CRL_SIGN));
+ TLSCHECK(gnutls_x509_crt_set_version(cacrt, 3));
+ TLSCHECK(gnutls_x509_crt_get_key_id(cacrt, 0, keyid, &keyidsize));
+ TLSCHECK(gnutls_x509_crt_set_subject_key_id(cacrt, keyid, keyidsize));
+ TLSCHECK(gnutls_x509_crt_sign2(cacrt, cacrt, cakey,
+ GNUTLS_DIG_SHA256, 0));
+
+ DEBUG(3,("Generating TLS certificate\n"));
+ TLSCHECK(gnutls_x509_crt_init(&crt));
+ TLSCHECK(gnutls_x509_crt_set_dn_by_oid(crt,
+ GNUTLS_OID_X520_ORGANIZATION_NAME, 0,
+ ORGANISATION_NAME, strlen(ORGANISATION_NAME)));
+ TLSCHECK(gnutls_x509_crt_set_dn_by_oid(crt,
+ GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0,
+ UNIT_NAME, strlen(UNIT_NAME)));
+ TLSCHECK(gnutls_x509_crt_set_dn_by_oid(crt,
+ GNUTLS_OID_X520_COMMON_NAME, 0,
+ hostname, strlen(hostname)));
+ TLSCHECK(gnutls_x509_crt_set_key(crt, key));
+ TLSCHECK(gnutls_x509_crt_set_serial(crt, &serial, sizeof(serial)));
+ TLSCHECK(gnutls_x509_crt_set_activation_time(crt, activation));
+ TLSCHECK(gnutls_x509_crt_set_expiration_time(crt, expiry));
+ TLSCHECK(gnutls_x509_crt_set_ca_status(crt, 0));
+ TLSCHECK(gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_TLS_WWW_SERVER, 0));
+ TLSCHECK(gnutls_x509_crt_set_version(crt, 3));
+ TLSCHECK(gnutls_x509_crt_get_key_id(crt, 0, keyid, &keyidsize));
+ TLSCHECK(gnutls_x509_crt_set_subject_key_id(crt, keyid, keyidsize));
+ TLSCHECK(gnutls_x509_crt_sign2(crt, crt, key,
+ GNUTLS_DIG_SHA256, 0));
+ TLSCHECK(gnutls_x509_crt_sign2(crt, cacrt, cakey,
+ GNUTLS_DIG_SHA256, 0));
+
+ DEBUG(3,("Exporting TLS keys\n"));
+
+ bufsize = sizeof(buf);
+ TLSCHECK(gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, buf, &bufsize));
+ if (!file_save(certfile, buf, bufsize)) {
+ DEBUG(0,("Unable to save certificate in %s parent dir exists ?\n", certfile));
+ goto failed;
+ }
+
+ bufsize = sizeof(buf);
+ TLSCHECK(gnutls_x509_crt_export(cacrt, GNUTLS_X509_FMT_PEM, buf, &bufsize));
+ if (!file_save(cafile, buf, bufsize)) {
+ DEBUG(0,("Unable to save ca cert in %s parent dir exists ?\n", cafile));
+ goto failed;
+ }
+
+ bufsize = sizeof(buf);
+ TLSCHECK(gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM, buf, &bufsize));
+ if (!file_save_mode(keyfile, buf, bufsize, 0600)) {
+ DEBUG(0,("Unable to save privatekey in %s parent dir exists ?\n", keyfile));
+ goto failed;
+ }
+
+ gnutls_x509_privkey_deinit(key);
+ gnutls_x509_privkey_deinit(cakey);
+ gnutls_x509_crt_deinit(cacrt);
+ gnutls_x509_crt_deinit(crt);
+
+ DEBUG(0,("TLS self-signed keys generated OK\n"));
+ return;
+
+failed:
+ DEBUG(0,("TLS certificate generation failed\n"));
+}
diff --git a/source4/lib/tls/wscript_build b/source4/lib/tls/wscript_build
new file mode 100644
index 0000000..7980a63
--- /dev/null
+++ b/source4/lib/tls/wscript_build
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+
+bld.SAMBA_SUBSYSTEM('LIBTLS',
+ source='''
+ tlscert.c
+ tls_tstream.c
+ ''',
+ public_deps='''
+ talloc
+ gnutls
+ samba-hostconfig
+ LIBTSOCKET
+ tevent
+ tevent-util
+ ''')
diff --git a/source4/lib/wmi/tools/wmic.c b/source4/lib/wmi/tools/wmic.c
new file mode 100644
index 0000000..4817dec
--- /dev/null
+++ b/source4/lib/wmi/tools/wmic.c
@@ -0,0 +1,221 @@
+/*
+ WMI Sample client
+ Copyright (C) 2006 Andrzej Hajda <andrzej.hajda@wp.pl>
+ Copyright (C) 2008 Jelmer Vernooij <jelmer@samba.org>
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "lib/cmdline/popt_common.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/gen_ndr/ndr_oxidresolver.h"
+#include "librpc/gen_ndr/ndr_oxidresolver_c.h"
+#include "librpc/gen_ndr/ndr_dcom.h"
+#include "librpc/gen_ndr/ndr_dcom_c.h"
+#include "librpc/gen_ndr/ndr_remact_c.h"
+#include "librpc/gen_ndr/ndr_epmapper_c.h"
+#include "librpc/gen_ndr/com_dcom.h"
+
+#include "lib/com/dcom/dcom.h"
+#include "lib/com/com.h"
+
+#include "lib/wmi/wmi.h"
+
+struct program_args {
+ char *hostname;
+ char *query;
+ char *ns;
+};
+
+static void parse_args(int argc, char *argv[], struct program_args *pmyargs)
+{
+ poptContext pc;
+ int opt, i;
+
+ int argc_new;
+ char **argv_new;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_CREDENTIALS
+ POPT_COMMON_VERSION
+ {"namespace", 0, POPT_ARG_STRING, &pmyargs->ns, 0,
+ "WMI namespace, default to root\\cimv2", 0},
+ POPT_TABLEEND
+ };
+
+ pc = poptGetContext("wmi", argc, (const char **) argv,
+ long_options, POPT_CONTEXT_KEEP_FIRST);
+
+ poptSetOtherOptionHelp(pc, "//host query\n\nExample: wmic -U [domain/]adminuser%password //host \"select * from Win32_ComputerSystem\"");
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ poptPrintUsage(pc, stdout, 0);
+ poptFreeContext(pc);
+ exit(1);
+ }
+
+ argv_new = discard_const_p(char *, poptGetArgs(pc));
+
+ argc_new = argc;
+ for (i = 0; i < argc; i++) {
+ if (argv_new[i] == NULL) {
+ argc_new = i;
+ break;
+ }
+ }
+
+ if (argc_new != 3 || argv_new[1][0] != '/'
+ || argv_new[1][1] != '/') {
+ poptPrintUsage(pc, stdout, 0);
+ poptFreeContext(pc);
+ exit(1);
+ }
+
+ pmyargs->hostname = argv_new[1] + 2;
+ pmyargs->query = argv_new[2];
+ poptFreeContext(pc);
+}
+
+#define WERR_CHECK(msg) if (!W_ERROR_IS_OK(result)) { \
+ DEBUG(0, ("ERROR: %s\n", msg)); \
+ goto error; \
+ } else { \
+ DEBUG(1, ("OK : %s\n", msg)); \
+ }
+
+#define RETURN_CVAR_ARRAY_STR(fmt, arr) {\
+ uint32_t i;\
+ char *r;\
+\
+ if (!arr) {\
+ return talloc_strdup(mem_ctx, "NULL");\
+ }\
+ r = talloc_strdup(mem_ctx, "(");\
+ for (i = 0; i < arr->count; ++i) {\
+ r = talloc_asprintf_append(r, fmt "%s", arr->item[i], (i+1 == arr->count)?"":",");\
+ }\
+ return talloc_asprintf_append(r, ")");\
+}
+
+char *string_CIMVAR(TALLOC_CTX *mem_ctx, union CIMVAR *v, enum CIMTYPE_ENUMERATION cimtype)
+{
+ switch (cimtype) {
+ case CIM_SINT8: return talloc_asprintf(mem_ctx, "%d", v->v_sint8);
+ case CIM_UINT8: return talloc_asprintf(mem_ctx, "%u", v->v_uint8);
+ case CIM_SINT16: return talloc_asprintf(mem_ctx, "%d", v->v_sint16);
+ case CIM_UINT16: return talloc_asprintf(mem_ctx, "%u", v->v_uint16);
+ case CIM_SINT32: return talloc_asprintf(mem_ctx, "%d", v->v_sint32);
+ case CIM_UINT32: return talloc_asprintf(mem_ctx, "%u", v->v_uint32);
+ case CIM_SINT64: return talloc_asprintf(mem_ctx, "%lld", v->v_sint64);
+ case CIM_UINT64: return talloc_asprintf(mem_ctx, "%llu", v->v_sint64);
+ case CIM_REAL32: return talloc_asprintf(mem_ctx, "%f", (double)v->v_uint32);
+ case CIM_REAL64: return talloc_asprintf(mem_ctx, "%f", (double)v->v_uint64);
+ case CIM_BOOLEAN: return talloc_asprintf(mem_ctx, "%s", v->v_boolean?"True":"False");
+ case CIM_STRING:
+ case CIM_DATETIME:
+ case CIM_REFERENCE: return talloc_asprintf(mem_ctx, "%s", v->v_string);
+ case CIM_CHAR16: return talloc_asprintf(mem_ctx, "Unsupported");
+ case CIM_OBJECT: return talloc_asprintf(mem_ctx, "Unsupported");
+ case CIM_ARR_SINT8: RETURN_CVAR_ARRAY_STR("%d", v->a_sint8);
+ case CIM_ARR_UINT8: RETURN_CVAR_ARRAY_STR("%u", v->a_uint8);
+ case CIM_ARR_SINT16: RETURN_CVAR_ARRAY_STR("%d", v->a_sint16);
+ case CIM_ARR_UINT16: RETURN_CVAR_ARRAY_STR("%u", v->a_uint16);
+ case CIM_ARR_SINT32: RETURN_CVAR_ARRAY_STR("%d", v->a_sint32);
+ case CIM_ARR_UINT32: RETURN_CVAR_ARRAY_STR("%u", v->a_uint32);
+ case CIM_ARR_SINT64: RETURN_CVAR_ARRAY_STR("%lld", v->a_sint64);
+ case CIM_ARR_UINT64: RETURN_CVAR_ARRAY_STR("%llu", v->a_uint64);
+ case CIM_ARR_REAL32: RETURN_CVAR_ARRAY_STR("%f", v->a_real32);
+ case CIM_ARR_REAL64: RETURN_CVAR_ARRAY_STR("%f", v->a_real64);
+ case CIM_ARR_BOOLEAN: RETURN_CVAR_ARRAY_STR("%d", v->a_boolean);
+ case CIM_ARR_STRING: RETURN_CVAR_ARRAY_STR("%s", v->a_string);
+ case CIM_ARR_DATETIME: RETURN_CVAR_ARRAY_STR("%s", v->a_datetime);
+ case CIM_ARR_REFERENCE: RETURN_CVAR_ARRAY_STR("%s", v->a_reference);
+ default: return talloc_asprintf(mem_ctx, "Unsupported");
+ }
+}
+
+#undef RETURN_CVAR_ARRAY_STR
+
+int main(int argc, char **argv)
+{
+ struct program_args args = {};
+ uint32_t cnt = 5, ret;
+ char *class_name = NULL;
+ WERROR result;
+ NTSTATUS status;
+ struct IWbemServices *pWS = NULL;
+ struct BSTR queryLanguage, query;
+ struct IEnumWbemClassObject *pEnum = NULL;
+ struct com_context *ctx = NULL;
+
+ parse_args(argc, argv, &args);
+
+ wmi_init(&ctx, popt_get_cmdline_credentials());
+
+ if (!args.ns)
+ args.ns = "root\\cimv2";
+ result = WBEM_ConnectServer(ctx, args.hostname, args.ns, 0, 0, 0, 0, 0, 0, &pWS);
+ WERR_CHECK("Login to remote object.");
+
+ queryLanguage.data = "WQL";
+ query.data = args.query;
+ result = IWbemServices_ExecQuery(pWS, ctx, queryLanguage, query, WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_ENSURE_LOCATABLE, NULL, &pEnum);
+ WERR_CHECK("WMI query execute.");
+
+ IEnumWbemClassObject_Reset(pEnum, ctx);
+ WERR_CHECK("Reset result of WMI query.");
+
+ do {
+ uint32_t i, j;
+ struct WbemClassObject *co[cnt];
+
+ result = IEnumWbemClassObject_SmartNext(pEnum, ctx, 0xFFFFFFFF, cnt, co, &ret);
+ /* WERR_INVALID_FUNCTION is OK, it means only that there is less returned objects than requested */
+ if (!W_ERROR_EQUAL(result, WERR_INVALID_FUNCTION)) {
+ WERR_CHECK("Retrieve result data.");
+ } else {
+ DEBUG(1, ("OK : Retrieved less objects than requested (it is normal).\n"));
+ }
+ if (!ret) break;
+
+ for (i = 0; i < ret; ++i) {
+ if (!class_name || strcmp(co[i]->obj_class->__CLASS, class_name)) {
+ if (class_name) talloc_free(class_name);
+ class_name = talloc_strdup(ctx, co[i]->obj_class->__CLASS);
+ printf("CLASS: %s\n", class_name);
+ for (j = 0; j < co[i]->obj_class->__PROPERTY_COUNT; ++j)
+ printf("%s%s", j?"|":"", co[i]->obj_class->properties[j].property.name);
+ printf("\n");
+ }
+ for (j = 0; j < co[i]->obj_class->__PROPERTY_COUNT; ++j) {
+ char *s;
+ s = string_CIMVAR(ctx, &co[i]->instance->data[j], co[i]->obj_class->properties[j].property.desc->cimtype & CIM_TYPEMASK);
+ printf("%s%s", j?"|":"", s);
+ }
+ printf("\n");
+ }
+ } while (ret == cnt);
+ talloc_free(ctx);
+ return 0;
+error:
+ status = werror_to_ntstatus(result);
+ fprintf(stderr, "NTSTATUS: %s - %s\n", nt_errstr(status), get_friendly_nt_error_msg(status));
+ talloc_free(ctx);
+ return 1;
+}
diff --git a/source4/lib/wmi/tools/wmis.c b/source4/lib/wmi/tools/wmis.c
new file mode 100644
index 0000000..395cec2
--- /dev/null
+++ b/source4/lib/wmi/tools/wmis.c
@@ -0,0 +1,222 @@
+/*
+ WMI Sample client
+ Copyright (C) 2006 Andrzej Hajda <andrzej.hajda@wp.pl>
+ Copyright (C) 2008 Jelmer Vernooij <jelmer@samba.org>
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "lib/cmdline/popt_common.h"
+#include "auth/credentials/credentials.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/gen_ndr/ndr_oxidresolver.h"
+#include "librpc/gen_ndr/ndr_oxidresolver_c.h"
+#include "librpc/gen_ndr/dcom.h"
+#include "librpc/gen_ndr/ndr_dcom.h"
+#include "librpc/gen_ndr/ndr_dcom_c.h"
+#include "librpc/gen_ndr/ndr_remact_c.h"
+#include "librpc/gen_ndr/ndr_epmapper_c.h"
+#include "librpc/gen_ndr/com_dcom.h"
+
+#include "lib/com/dcom/dcom.h"
+#include "librpc/gen_ndr/com_wmi.h"
+#include "librpc/ndr/ndr_table.h"
+
+#include "lib/wmi/wmi.h"
+
+struct program_args {
+ char *hostname;
+ char *query;
+};
+
+static void parse_args(int argc, char *argv[], struct program_args *pmyargs)
+{
+ poptContext pc;
+ int opt, i;
+
+ int argc_new;
+ char **argv_new;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_CREDENTIALS
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ pc = poptGetContext("wmi", argc, (const char **) argv,
+ long_options, POPT_CONTEXT_KEEP_FIRST);
+
+ poptSetOtherOptionHelp(pc, "//host\n\nExample: wmis -U [domain/]adminuser%password //host");
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ poptPrintUsage(pc, stdout, 0);
+ poptFreeContext(pc);
+ exit(1);
+ }
+
+ argv_new = discard_const_p(char *, poptGetArgs(pc));
+
+ argc_new = argc;
+ for (i = 0; i < argc; i++) {
+ if (argv_new[i] == NULL) {
+ argc_new = i;
+ break;
+ }
+ }
+
+ if (argc_new < 2 || argv_new[1][0] != '/'
+ || argv_new[1][1] != '/') {
+ poptPrintUsage(pc, stdout, 0);
+ poptFreeContext(pc);
+ exit(1);
+ }
+
+ pmyargs->hostname = argv_new[1] + 2;
+ poptFreeContext(pc);
+}
+
+#define WERR_CHECK(msg) if (!W_ERROR_IS_OK(result)) { \
+ DEBUG(0, ("ERROR: %s\n", msg)); \
+ goto error; \
+ } else { \
+ DEBUG(1, ("OK : %s\n", msg)); \
+ }
+/*
+WERROR WBEM_ConnectServer(struct com_context *ctx, const char *server, const char *nspace, const char *user, const char *password, const char *locale, uint32_t flags, const char *authority, struct IWbemContext* wbem_ctx, struct IWbemServices** services)
+{
+ struct GUID clsid;
+ struct GUID iid;
+ WERROR result;
+ HRESULT coresult;
+ struct IUnknown **mqi;
+ struct IWbemLevel1Login *pL;
+
+ if (user) {
+ char *cred;
+ struct cli_credentials *cc;
+
+ cred = talloc_asprintf(NULL, "%s%%%s", user, password);
+ cc = cli_credentials_init(ctx);
+ cli_credentials_set_conf(cc);
+ cli_credentials_parse_string(cc, cred, CRED_SPECIFIED);
+ dcom_set_server_credentials(ctx, server, cc);
+ talloc_free(cred);
+ }
+
+ GUID_from_string(CLSID_WBEMLEVEL1LOGIN, &clsid);
+ GUID_from_string(COM_IWBEMLEVEL1LOGIN_UUID, &iid);
+ result = dcom_create_object(ctx, &clsid, server, 1, &iid, &mqi, &coresult);
+ WERR_CHECK("dcom_create_object.");
+ result = coresult;
+ WERR_CHECK("Create remote WMI object.");
+ pL = (struct IWbemLevel1Login *)mqi[0];
+ talloc_free(mqi);
+
+ result = IWbemLevel1Login_NTLMLogin(pL, ctx, nspace, locale, flags, wbem_ctx, services);
+ WERR_CHECK("Login to remote object.");
+error:
+ return result;
+}
+*/
+WERROR WBEM_RemoteExecute(struct IWbemServices *pWS, const char *cmdline, uint32_t *ret_code)
+{
+ struct IWbemClassObject *wco = NULL;
+ struct IWbemClassObject *inc, *outc, *in;
+ struct IWbemClassObject *out = NULL;
+ WERROR result;
+ union CIMVAR v;
+ TALLOC_CTX *ctx;
+ struct BSTR objectPath, methodName;
+
+ ctx = talloc_new(0);
+
+ objectPath.data = "Win32_Process";
+
+ result = IWbemServices_GetObject(pWS, ctx, objectPath,
+ WBEM_FLAG_RETURN_WBEM_COMPLETE, NULL, &wco, NULL);
+ WERR_CHECK("GetObject.");
+
+ result = IWbemClassObject_GetMethod(wco, ctx, "Create", 0, &inc, &outc);
+ WERR_CHECK("IWbemClassObject_GetMethod.");
+
+ result = IWbemClassObject_SpawnInstance(inc, ctx, 0, &in);
+ WERR_CHECK("IWbemClassObject_SpawnInstance.");
+
+ v.v_string = cmdline;
+ result = IWbemClassObject_Put(in, ctx, "CommandLine", 0, &v, 0);
+ WERR_CHECK("IWbemClassObject_Put(CommandLine).");
+
+ methodName.data = "Create";
+ result = IWbemServices_ExecMethod(pWS, ctx, objectPath, methodName, 0, NULL, in, &out,
+ NULL);
+ WERR_CHECK("IWbemServices_ExecMethod.");
+
+ if (ret_code) {
+ result = WbemClassObject_Get(out->object_data, ctx, "ReturnValue", 0, &v, 0, 0);
+ WERR_CHECK("IWbemClassObject_Put(CommandLine).");
+ *ret_code = v.v_uint32;
+ }
+error:
+ talloc_free(ctx);
+ return result;
+}
+
+int main(int argc, char **argv)
+{
+ struct program_args args = {};
+ struct com_context *ctx = NULL;
+ WERROR result;
+ NTSTATUS status;
+ struct IWbemServices *pWS = NULL;
+ struct IEnumWbemClassObject *pEnum = NULL;
+ uint32_t cnt;
+ struct BSTR queryLanguage;
+ struct BSTR query;
+
+ parse_args(argc, argv, &args);
+
+ wmi_init(&ctx, popt_get_cmdline_credentials());
+ result = WBEM_ConnectServer(ctx, args.hostname, "root\\cimv2", 0, 0, 0, 0, 0, 0, &pWS);
+ WERR_CHECK("WBEM_ConnectServer.");
+
+ printf("1: Creating directory C:\\wmi_test_dir_tmp using method Win32_Process.Create\n");
+ WBEM_RemoteExecute(pWS, "cmd.exe /C mkdir C:\\wmi_test_dir_tmp", &cnt);
+ WERR_CHECK("WBEM_RemoteExecute.");
+ printf("2: ReturnCode: %d\n", cnt);
+
+ printf("3: Monitoring directory C:\\wmi_test_dir_tmp. Please create/delete files in that directory to see notifications, after 4 events program quits.\n");
+ query.data = "SELECT * FROM __InstanceOperationEvent WITHIN 1 WHERE Targetinstance ISA 'CIM_DirectoryContainsFile' and TargetInstance.GroupComponent= 'Win32_Directory.Name=\"C:\\\\\\\\wmi_test_dir_tmp\"'";
+ queryLanguage.data = "WQL";
+ result = IWbemServices_ExecNotificationQuery(pWS, ctx, queryLanguage,
+ query, WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, NULL, &pEnum);
+ WERR_CHECK("WMI query execute.");
+ for (cnt = 0; cnt < 4; ++cnt) {
+ struct WbemClassObject *co;
+ uint32_t ret;
+ result = IEnumWbemClassObject_SmartNext(pEnum, ctx, 0xFFFFFFFF, 1, &co, &ret);
+ WERR_CHECK("IEnumWbemClassObject_Next.");
+ printf("%s\n", co->obj_class->__CLASS);
+ }
+
+error:
+ status = werror_to_ntstatus(result);
+ fprintf(stderr, "NTSTATUS: %s - %s\n", nt_errstr(status), get_friendly_nt_error_msg(status));
+ talloc_free(ctx);
+ return 1;
+}
diff --git a/source4/lib/wmi/wbemdata.c b/source4/lib/wmi/wbemdata.c
new file mode 100644
index 0000000..d475067
--- /dev/null
+++ b/source4/lib/wmi/wbemdata.c
@@ -0,0 +1,451 @@
+/*
+ WMI Implementation
+ Copyright (C) 2006 Andrzej Hajda <andrzej.hajda@wp.pl>
+ Copyright (C) 2008 Jelmer Vernooij <jelmer@samba.org>
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include <talloc.h>
+#include "librpc/gen_ndr/dcom.h"
+#include "librpc/gen_ndr/com_dcom.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/ndr/libndr_proto.h"
+#include "lib/com/com.h"
+#include "lib/com/dcom/dcom.h"
+#include "lib/util/dlinklist.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/ndr_dcom.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "libcli/composite/composite.h"
+#include "lib/wmi/wmi.h"
+#include "librpc/gen_ndr/ndr_wmi.h"
+
+enum {
+ DATATYPE_CLASSOBJECT = 2,
+ DATATYPE_OBJECT = 3,
+ COFLAG_IS_CLASS = 4,
+};
+
+static enum ndr_err_code marshal(TALLOC_CTX *mem_ctx, struct IUnknown *pv, struct OBJREF *o)
+{
+ struct ndr_push *ndr;
+ struct IWbemClassObject *wco;
+ struct MInterfacePointer *mp;
+
+ mp = (struct MInterfacePointer *)((char *)o - offsetof(struct MInterfacePointer, obj)); /* FIXME:high remove this Mumbo Jumbo */
+ wco = pv->object_data;
+ ndr = talloc_zero(mem_ctx, struct ndr_push);
+ ndr->flags = 0;
+ ndr->alloc_size = 1024;
+ ndr->data = talloc_array(mp, uint8_t, ndr->alloc_size);
+
+ if (wco) {
+ uint32_t ofs;
+ NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, 0x12345678));
+ NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, 0));
+ NDR_CHECK(ndr_push_IWbemClassObject(ndr, NDR_SCALARS | NDR_BUFFERS, wco));
+ ofs = ndr->offset;
+ ndr->offset = 4;
+ NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, ofs - 8));
+ ndr->offset = ofs;
+ } else {
+ NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, 0));
+ }
+ o->u_objref.u_custom.pData = talloc_realloc(mp, ndr->data, uint8_t, ndr->offset);
+ o->u_objref.u_custom.size = ndr->offset;
+ mp->size = sizeof(struct OBJREF) - sizeof(union OBJREF_Types) + sizeof(struct u_custom) + o->u_objref.u_custom.size - 4;
+ if (DEBUGLVL(9)) {
+ NDR_PRINT_DEBUG(IWbemClassObject, wco);
+ }
+ return NDR_ERR_SUCCESS;
+}
+
+static enum ndr_err_code unmarshal(TALLOC_CTX *mem_ctx, struct OBJREF *o, struct IUnknown **pv)
+{
+ struct ndr_pull *ndr;
+ struct IWbemClassObject *wco;
+ enum ndr_err_code ndr_err;
+ uint32_t u;
+
+ mem_ctx = talloc_new(0);
+ ndr = talloc_zero(mem_ctx, struct ndr_pull);
+ ndr->current_mem_ctx = mem_ctx;
+ ndr->data = o->u_objref.u_custom.pData;
+ ndr->data_size = o->u_objref.u_custom.size;
+
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ if (!u) {
+ talloc_free(*pv);
+ *pv = NULL;
+ return NDR_ERR_SUCCESS;
+ }
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ if (u + 8 > ndr->data_size) {
+ DEBUG(1, ("unmarshall_IWbemClassObject: Incorrect data_size"));
+ return NDR_ERR_BUFSIZE;
+ }
+ wco = talloc_zero(*pv, struct IWbemClassObject);
+ ndr->current_mem_ctx = wco;
+ ndr_err = ndr_pull_IWbemClassObject(ndr, NDR_SCALARS | NDR_BUFFERS, wco);
+
+ if (NDR_ERR_CODE_IS_SUCCESS(ndr_err) && (DEBUGLVL(9))) {
+ NDR_PRINT_DEBUG(IWbemClassObject, wco);
+ }
+
+ if (NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ (*pv)->object_data = wco;
+ } else {
+ talloc_free(wco);
+ }
+ return NDR_ERR_SUCCESS;
+}
+
+WERROR dcom_IWbemClassObject_from_WbemClassObject(struct com_context *ctx, struct IWbemClassObject **_p, struct IWbemClassObject *wco)
+{
+ struct IWbemClassObject *p;
+
+ p = talloc_zero(ctx, struct IWbemClassObject);
+ p->ctx = ctx;
+ p->obj.signature = 0x574f454d;
+ p->obj.flags = OBJREF_CUSTOM;
+ GUID_from_string("dc12a681-737f-11cf-884d-00aa004b2e24", &p->obj.iid);
+ GUID_from_string("4590f812-1d3a-11d0-891f-00aa004b2e24", &p->obj.u_objref.u_custom.clsid);
+ p->object_data = (void *)wco;
+ talloc_steal(p, p->object_data);
+ *_p = p;
+ return WERR_OK;
+}
+
+WERROR IWbemClassObject_GetMethod(struct IWbemClassObject *d, TALLOC_CTX *mem_ctx, const char *name, uint32_t flags, struct IWbemClassObject **in, struct IWbemClassObject **out)
+{
+ uint32_t i;
+ struct IWbemClassObject *wco;
+
+ wco = (struct IWbemClassObject *)d->object_data;
+ for (i = 0; i < wco->obj_methods->count; ++i)
+ if (!strcmp(wco->obj_methods->method[i].name, name)) {
+ if (in) dcom_IWbemClassObject_from_WbemClassObject(d->ctx, in, wco->obj_methods->method[i].in);
+ if (out) dcom_IWbemClassObject_from_WbemClassObject(d->ctx, out, wco->obj_methods->method[i].out);
+ return WERR_OK;
+ }
+ return WERR_NOT_FOUND;
+}
+
+void IWbemClassObject_CreateInstance(struct IWbemClassObject *wco)
+{
+ uint32_t i;
+
+ wco->instance = talloc_zero(wco, struct WbemInstance);
+ wco->instance->default_flags = talloc_array(wco->instance, uint8_t, wco->obj_class->__PROPERTY_COUNT);
+ wco->instance->data = talloc_array(wco->instance, union CIMVAR, wco->obj_class->__PROPERTY_COUNT);
+ memset(wco->instance->data, 0, sizeof(union CIMVAR) * wco->obj_class->__PROPERTY_COUNT);
+ for (i = 0; i < wco->obj_class->__PROPERTY_COUNT; ++i) {
+ wco->instance->default_flags[i] = 1; /* FIXME:high resolve this magic */
+ }
+ wco->instance->__CLASS = wco->obj_class->__CLASS;
+ wco->instance->u2_4 = 4;
+ wco->instance->u3_1 = 1;
+}
+
+WERROR IWbemClassObject_Clone(struct IWbemClassObject *d, TALLOC_CTX *mem_ctx, struct IWbemClassObject **copy)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR IWbemClassObject_SpawnInstance(struct IWbemClassObject *d, TALLOC_CTX *mem_ctx, uint32_t flags, struct IWbemClassObject **instance)
+{
+ struct IWbemClassObject *wco, *nwco;
+
+ wco = (struct IWbemClassObject *)d->object_data;
+ nwco = talloc_zero(mem_ctx, struct IWbemClassObject);
+ nwco->flags = WCF_INSTANCE;
+ nwco->obj_class = wco->obj_class;
+ IWbemClassObject_CreateInstance(nwco);
+ dcom_IWbemClassObject_from_WbemClassObject(d->ctx, instance, nwco);
+ return WERR_OK;
+}
+
+WERROR IWbemClassObject_Get(struct IWbemClassObject *d, TALLOC_CTX *mem_ctx, const char *name, uint32_t flags, union CIMVAR *val, enum CIMTYPE_ENUMERATION *cimtype, uint32_t *flavor)
+{
+ uint32_t i;
+ for (i = 0; i < d->obj_class->__PROPERTY_COUNT; ++i) {
+ if (!strcmp(d->obj_class->properties[i].property.name, name)) {
+ duplicate_CIMVAR(mem_ctx, &d->instance->data[i], val, d->obj_class->properties[i].property.desc->cimtype);
+ if (cimtype != NULL)
+ *cimtype = d->obj_class->properties[i].property.desc->cimtype;
+ if (flavor != NULL)
+ *flavor = 0; /* FIXME:avg implement flavor */
+ return WERR_OK;
+ }
+ }
+ return WERR_NOT_FOUND;
+}
+
+WERROR IWbemClassObject_Put(struct IWbemClassObject *d, TALLOC_CTX *mem_ctx, const char *name, uint32_t flags, union CIMVAR *val, enum CIMTYPE_ENUMERATION cimtype)
+{
+ struct IWbemClassObject *wco;
+ uint32_t i;
+
+ wco = (struct IWbemClassObject *)d->object_data;
+ for (i = 0; i < wco->obj_class->__PROPERTY_COUNT; ++i) {
+ if (!strcmp(wco->obj_class->properties[i].property.name, name)) {
+ if (cimtype && cimtype != wco->obj_class->properties[i].property.desc->cimtype) return WERR_INVALID_PARAMETER;
+ wco->instance->default_flags[i] = 0;
+ duplicate_CIMVAR(wco->instance, val, &wco->instance->data[i], wco->obj_class->properties[i].property.desc->cimtype);
+ return WERR_OK;
+ }
+ }
+ return WERR_NOT_FOUND;
+}
+
+#define WERR_CHECK(msg) if (!W_ERROR_IS_OK(result)) { \
+ DEBUG(1, ("ERROR: %s - %s\n", msg, wmi_errstr(result))); \
+ return result; \
+ } else { \
+ DEBUG(1, ("OK : %s\n", msg)); \
+ }
+
+struct pair_guid_ptr {
+ struct GUID guid;
+ void *ptr;
+ struct pair_guid_ptr *next, *prev;
+};
+
+static void *get_ptr_by_guid(struct pair_guid_ptr *list, struct GUID *uuid)
+{
+ for (; list; list = list->next) {
+ if (GUID_equal(&list->guid, uuid))
+ return list->ptr;
+ }
+ return NULL;
+}
+
+static void add_pair_guid_ptr(TALLOC_CTX *mem_ctx, struct pair_guid_ptr **list, struct GUID *uuid, void *ptr)
+{
+ struct pair_guid_ptr *e;
+
+ e = talloc(mem_ctx, struct pair_guid_ptr);
+ e->guid = *uuid;
+ e->ptr = ptr;
+ talloc_steal(e, ptr);
+ DLIST_ADD(*list, e);
+}
+
+struct IEnumWbemClassObject_data {
+ struct GUID guid;
+ struct IWbemFetchSmartEnum *pFSE;
+ struct IWbemWCOSmartEnum *pSE;
+ struct pair_guid_ptr *cache;
+};
+#define NDR_CHECK_EXPR(expr) do { if (!(expr)) {\
+ DEBUG(0, ("%s(%d): WBEMDATA_ERR(0x%08X): Error parsing(%s)\n", __FILE__, __LINE__, ndr->offset, #expr)); \
+ return NDR_ERR_VALIDATE; \
+ } \
+ } while(0)
+
+#define NDR_CHECK_CONST(val, exp) NDR_CHECK_EXPR((val) == (exp))
+
+
+static enum ndr_err_code WBEMDATA_Parse(TALLOC_CTX *mem_ctx, uint8_t *data, uint32_t size, struct IEnumWbemClassObject *d, uint32_t uCount, struct IWbemClassObject **apObjects)
+{
+ struct ndr_pull *ndr;
+ uint32_t u, i, ofs_next;
+ uint8_t u8, datatype;
+ struct GUID guid;
+ struct IEnumWbemClassObject_data *ecod;
+
+ if (!uCount)
+ return NDR_ERR_BAD_SWITCH;
+
+ ecod = d->object_data;
+
+ ndr = talloc_zero(mem_ctx, struct ndr_pull);
+ ndr->current_mem_ctx = d->ctx;
+ ndr->data = data;
+ ndr->data_size = size;
+ ndr_set_flags(&ndr->flags, LIBNDR_FLAG_NOALIGN);
+
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_CHECK_CONST(u, 0x0);
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_CHECK_CONST(u, *(const uint32_t *)"WBEM");
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_CHECK_CONST(u, *(const uint32_t *)"DATA");
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_CHECK_CONST(u, 0x1A); /* Length of header */
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_PULL_NEED_BYTES(ndr, u + 6);
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_CHECK_CONST(u, 0x0);
+ NDR_CHECK(ndr_pull_uint8(ndr, NDR_SCALARS, &u8));
+ NDR_CHECK_CONST(u8, 0x01); /* Major Version */
+ NDR_CHECK(ndr_pull_uint8(ndr, NDR_SCALARS, &u8));
+ NDR_CHECK_EXPR(u8 <= 1); /* Minor Version 0 - Win2000, 1 - XP/2003 */
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_CHECK_CONST(u, 0x8); /* Length of header */
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_PULL_NEED_BYTES(ndr, u);
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_CHECK_CONST(u, 0xC); /* Length of header */
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_PULL_NEED_BYTES(ndr, u + 4);
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_CHECK_CONST(u, uCount);
+ for (i = 0; i < uCount; ++i) {
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_CHECK_CONST(u, 0x9); /* Length of header */
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_PULL_NEED_BYTES(ndr, u + 1);
+ NDR_CHECK(ndr_pull_uint8(ndr, NDR_SCALARS, &datatype));
+ ofs_next = ndr->offset + u;
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_CHECK_CONST(u, 0x18); /* Length of header */
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_PULL_NEED_BYTES(ndr, u + 16);
+ NDR_CHECK(ndr_pull_GUID(ndr, NDR_SCALARS, &guid));
+ switch (datatype) {
+ case DATATYPE_CLASSOBJECT:
+ apObjects[i] = talloc_zero(d->ctx, struct IWbemClassObject);
+ ndr->current_mem_ctx = apObjects[i];
+ NDR_CHECK(ndr_pull_WbemClassObject(ndr, NDR_SCALARS|NDR_BUFFERS, apObjects[i]));
+ ndr->current_mem_ctx = d->ctx;
+ add_pair_guid_ptr(ecod, &ecod->cache, &guid, apObjects[i]->obj_class);
+ break;
+ case DATATYPE_OBJECT:
+ apObjects[i] = talloc_zero(d->ctx, struct IWbemClassObject);
+ apObjects[i]->obj_class = get_ptr_by_guid(ecod->cache, &guid);
+ (void)talloc_reference(apObjects[i], apObjects[i]->obj_class);
+ ndr->current_mem_ctx = apObjects[i];
+ NDR_CHECK(ndr_pull_WbemClassObject_Object(ndr, NDR_SCALARS|NDR_BUFFERS, apObjects[i]));
+ ndr->current_mem_ctx = d->ctx;
+ break;
+ default:
+ DEBUG(0, ("WBEMDATA_Parse: Data type %d not supported\n", datatype));
+ return NDR_ERR_BAD_SWITCH;
+ }
+ ndr->offset = ofs_next;
+ if (DEBUGLVL(9)) {
+ NDR_PRINT_DEBUG(IWbemClassObject, apObjects[i]);
+ }
+ }
+ return NDR_ERR_SUCCESS;
+}
+
+WERROR IEnumWbemClassObject_SmartNext(struct IEnumWbemClassObject *d, TALLOC_CTX *mem_ctx, int32_t lTimeout, uint32_t uCount, struct IWbemClassObject **apObjects, uint32_t *puReturned)
+{
+ WERROR result;
+ NTSTATUS status;
+ struct IEnumWbemClassObject_data *ecod;
+ TALLOC_CTX *loc_ctx;
+ uint32_t size;
+ uint8_t *data;
+
+ loc_ctx = talloc_new(0);
+ ecod = d->object_data;
+ if (!ecod) {
+ struct GUID iid;
+ WERROR coresult;
+
+ d->object_data = ecod = talloc_zero(d, struct IEnumWbemClassObject_data);
+ GUID_from_string(COM_IWBEMFETCHSMARTENUM_UUID, &iid);
+ result = dcom_query_interface((struct IUnknown *)d, 5, 1, &iid, (struct IUnknown **)&ecod->pFSE, &coresult);
+ WERR_CHECK("dcom_query_interface.");
+ result = coresult;
+ WERR_CHECK("Retrieve enumerator of result(IWbemFetchSmartEnum).");
+
+ result = IWbemFetchSmartEnum_Fetch(ecod->pFSE, mem_ctx, &ecod->pSE);
+ WERR_CHECK("Retrieve enumerator of result(IWbemWCOSmartEnum).");
+
+ ecod->guid = GUID_random();
+ d->vtable->Release_send = dcom_proxy_IEnumWbemClassObject_Release_send;
+ }
+
+ result = IWbemWCOSmartEnum_Next(ecod->pSE, loc_ctx, &ecod->guid, lTimeout, uCount, puReturned, &size, &data);
+ if (!W_ERROR_EQUAL(result, WERR_INVALID_FUNCTION)) {
+ WERR_CHECK("IWbemWCOSmartEnum_Next.");
+ }
+
+ if (data) {
+ NDR_CHECK(WBEMDATA_Parse(mem_ctx, data, size, d, *puReturned, apObjects));
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ status = werror_to_ntstatus(result);
+ DEBUG(9, ("dcom_proxy_IEnumWbemClassObject_Next: %s - %s\n", nt_errstr(status), get_friendly_nt_error_msg(status)));
+ }
+ talloc_free(loc_ctx);
+ return result;
+}
+
+struct composite_context *dcom_proxy_IEnumWbemClassObject_Release_send(struct IUnknown *d, TALLOC_CTX *mem_ctx)
+{
+ struct composite_context *c, *cr;
+ struct REMINTERFACEREF iref[3];
+ struct dcom_object_exporter *ox;
+ struct IEnumWbemClassObject_data *ecod;
+ int n;
+
+ c = composite_create(d->ctx, d->ctx->event_ctx);
+ if (c == NULL) return NULL;
+ c->private_data = d;
+
+ ox = object_exporter_by_ip(d->ctx, d);
+ iref[0].ipid = IUnknown_ipid(d);
+ iref[0].cPublicRefs = 5;
+ iref[0].cPrivateRefs = 0;
+ n = 1;
+
+ ecod = d->object_data;
+ if (ecod) {
+ if (ecod->pFSE) {
+ talloc_steal(d, ecod->pFSE);
+ iref[n].ipid = IUnknown_ipid(ecod->pFSE);
+ iref[n].cPublicRefs = 5;
+ iref[n].cPrivateRefs = 0;
+ ++n;
+ }
+ if (ecod->pSE) {
+ talloc_steal(d, ecod->pSE);
+ iref[n].ipid = IUnknown_ipid(ecod->pSE);
+ iref[n].cPublicRefs = 5;
+ iref[n].cPrivateRefs = 0;
+ ++n;
+ }
+ }
+ cr = IRemUnknown_RemRelease_send(ox->rem_unknown, mem_ctx, n, iref);
+
+ composite_continue(c, cr, dcom_release_continue, c);
+ return c;
+}
+
+NTSTATUS dcom_proxy_IWbemClassObject_init(TALLOC_CTX *ctx)
+{
+ struct GUID clsid;
+ GUID_from_string("4590f812-1d3a-11d0-891f-00aa004b2e24", &clsid);
+ dcom_register_marshal(ctx, &clsid, marshal, unmarshal);
+
+#if 0
+ struct IEnumWbemClassObject_vtable *proxy_vtable;
+ proxy_vtable = (struct IEnumWbemClassObject_vtable *)dcom_proxy_vtable_by_iid((struct GUID *)&dcerpc_table_IEnumWbemClassObject.syntax_id.uuid);
+ if (proxy_vtable)
+ proxy_vtable->Release_send = dcom_proxy_IEnumWbemClassObject_Release_send;
+ else
+ DEBUG(0, ("WARNING: IEnumWbemClassObject should be initialized before IWbemClassObject."));
+#endif
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/lib/wmi/wmi.h b/source4/lib/wmi/wmi.h
new file mode 100644
index 0000000..46c9b70
--- /dev/null
+++ b/source4/lib/wmi/wmi.h
@@ -0,0 +1,48 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 _WMI_H_
+#define _WMI_H_
+
+#include "librpc/gen_ndr/com_wmi.h"
+
+/* The following definitions come from lib/wmi/wmicore.c */
+
+
+/** FIXME: Use credentials struct rather than user/password here */
+WERROR WBEM_ConnectServer(struct com_context *ctx, const char *server, const char *nspace,
+ struct cli_credentials *credentials,
+ const char *locale, uint32_t flags, const char *authority,
+ struct IWbemContext* wbem_ctx, struct IWbemServices** services);
+const char *wmi_errstr(WERROR werror);
+
+/* The following definitions come from lib/wmi/wbemdata.c */
+
+WERROR IWbemClassObject_GetMethod(struct IWbemClassObject *d, TALLOC_CTX *mem_ctx, const char *name, uint32_t flags, struct IWbemClassObject **in, struct IWbemClassObject **out);
+void WbemClassObject_CreateInstance(struct IWbemClassObject *wco);
+WERROR IWbemClassObject_Clone(struct IWbemClassObject *d, TALLOC_CTX *mem_ctx, struct IWbemClassObject **copy);
+WERROR IWbemClassObject_SpawnInstance(struct IWbemClassObject *d, TALLOC_CTX *mem_ctx, uint32_t flags, struct IWbemClassObject **instance);
+WERROR IWbemClassObject_Get(struct IWbemClassObject *d, TALLOC_CTX *mem_ctx, const char *name, uint32_t flags, union CIMVAR *val, enum CIMTYPE_ENUMERATION *cimtype, uint32_t *flavor);
+WERROR IWbemClassObject_Put(struct IWbemClassObject *d, TALLOC_CTX *mem_ctx, const char *name, uint32_t flags, union CIMVAR *val, enum CIMTYPE_ENUMERATION cimtype);
+WERROR IEnumWbemClassObject_SmartNext(struct IEnumWbemClassObject *d, TALLOC_CTX *mem_ctx, int32_t lTimeout, uint32_t uCount, struct IWbemClassObject **apObjects, uint32_t *puReturned);
+struct composite_context *dcom_proxy_IEnumWbemClassObject_Release_send(struct IUnknown *d, TALLOC_CTX *mem_ctx);
+
+void wmi_init(struct com_context **ctx, struct cli_credentials *credentials);
+
+#endif
diff --git a/source4/lib/wmi/wmi.i b/source4/lib/wmi/wmi.i
new file mode 100644
index 0000000..8f217e0
--- /dev/null
+++ b/source4/lib/wmi/wmi.i
@@ -0,0 +1,352 @@
+/*
+ WMI Implementation
+ Copyright (C) 2006 Andrzej Hajda <andrzej.hajda@wp.pl>
+ Copyright (C) 2008 Jelmer Vernooij <jelmer@samba.org>
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+%module wmi
+
+%include "typemaps.i"
+%include "libcli/util/errors.i"
+%import "stdint.i"
+%import "lib/talloc/talloc.i"
+
+%runtime %{
+void push_object(PyObject **stack, PyObject *o)
+{
+ if ((!*stack) || (*stack == Py_None)) {
+ *stack = o;
+ } else {
+ PyObject *o2, *o3;
+ if (!PyTuple_Check(*stack)) {
+ o2 = *stack;
+ *stack = PyTuple_New(1);
+ PyTuple_SetItem(*stack,0,o2);
+ }
+ o3 = PyTuple_New(1);
+ PyTuple_SetItem(o3,0,o);
+ o2 = *stack;
+ *stack = PySequence_Concat(o2,o3);
+ Py_DECREF(o2);
+ Py_DECREF(o3);
+ }
+}
+%}
+
+%{
+#include "includes.h"
+#include "librpc/gen_ndr/misc.h"
+#include "librpc/rpc/dcerpc.h"
+#include "lib/com/dcom/dcom.h"
+#include "librpc/gen_ndr/com_dcom.h"
+#include "lib/wmi/wmi.h"
+
+
+WERROR WBEM_ConnectServer(struct com_context *ctx, const char *server, const char *nspace, const char *user, const char *password,
+ const char *locale, uint32_t flags, const char *authority, struct IWbemContext* wbem_ctx, struct IWbemServices** services);
+WERROR IEnumWbemClassObject_SmartNext(struct IEnumWbemClassObject *d, TALLOC_CTX *mem_ctx, int32_t lTimeout,uint32_t uCount,
+ struct WbemClassObject **apObjects, uint32_t *puReturned);
+
+static PyObject *PyObject_FromCVAR(uint32_t cimtype, union CIMVAR *cvar);
+static PyObject *PySWbemObject_FromWbemClassObject(struct WbemClassObject *wco);
+
+static struct com_context *com_ctx;
+static PyObject *ComError;
+static PyObject *mod_win32_client;
+static PyObject *mod_pywintypes;
+
+typedef struct IUnknown IUnknown;
+typedef struct IWbemServices IWbemServices;
+typedef struct IWbemClassObject IWbemClassObject;
+typedef struct IEnumWbemClassObject IEnumWbemClassObject;
+%}
+
+%wrapper %{
+
+#define RETURN_CVAR_ARRAY(fmt, arr) {\
+ PyObject *l, *o;\
+ uint32_t i;\
+\
+ if (!arr) {\
+ Py_INCREF(Py_None);\
+ return Py_None;\
+ }\
+ l = PyList_New(arr->count);\
+ if (!l) return NULL;\
+ for (i = 0; i < arr->count; ++i) {\
+ o = _Py_BuildValue(fmt, arr->item[i]);\
+ if (!o) {\
+ Py_DECREF(l);\
+ return NULL;\
+ }\
+ PyList_SET_ITEM(l, i, o);\
+ }\
+ return l;\
+}
+
+static PyObject *_Py_BuildValue(char *str, ...)
+{
+ PyObject * result = NULL;
+ va_list lst;
+ va_start(lst, str);
+ if (str && *str == 'I') {
+ uint32_t value = va_arg(lst, uint32_t);
+ if (value & 0x80000000) {
+ result = Py_BuildValue("L", (long)value);
+ } else {
+ result = Py_BuildValue("i", value);
+ }
+ } else {
+ result = Py_VaBuildValue(str, lst);
+ }
+ va_end(lst);
+ return result;
+}
+
+
+static PyObject *PyObject_FromCVAR(uint32_t cimtype, union CIMVAR *cvar)
+{
+ switch (cimtype) {
+ case CIM_SINT8: return Py_BuildValue("b", cvar->v_sint8);
+ case CIM_UINT8: return Py_BuildValue("B", cvar->v_uint8);
+ case CIM_SINT16: return Py_BuildValue("h", cvar->v_sint16);
+ case CIM_UINT16: return Py_BuildValue("H", cvar->v_uint16);
+ case CIM_SINT32: return Py_BuildValue("i", cvar->v_sint32);
+ case CIM_UINT32: return _Py_BuildValue("I", cvar->v_uint32);
+ case CIM_SINT64: return Py_BuildValue("L", cvar->v_sint64);
+ case CIM_UINT64: return Py_BuildValue("K", cvar->v_uint64);
+ case CIM_REAL32: return Py_BuildValue("f", cvar->v_real32);
+ case CIM_REAL64: return Py_BuildValue("d", cvar->v_real64);
+ case CIM_BOOLEAN: return Py_BuildValue("h", cvar->v_boolean);
+ case CIM_STRING: return Py_BuildValue("s", cvar->v_string);
+ case CIM_DATETIME: return Py_BuildValue("s", cvar->v_datetime);
+ case CIM_REFERENCE: return Py_BuildValue("s", cvar->v_reference);
+ case CIM_OBJECT: return PySWbemObject_FromWbemClassObject(cvar->v_object);
+ case CIM_ARR_SINT8: RETURN_CVAR_ARRAY("b", cvar->a_sint8);
+ case CIM_ARR_UINT8: RETURN_CVAR_ARRAY("B", cvar->a_uint8);
+ case CIM_ARR_SINT16: RETURN_CVAR_ARRAY("h", cvar->a_sint16);
+ case CIM_ARR_UINT16: RETURN_CVAR_ARRAY("H", cvar->a_uint16);
+ case CIM_ARR_SINT32: RETURN_CVAR_ARRAY("i", cvar->a_sint32);
+ case CIM_ARR_UINT32: RETURN_CVAR_ARRAY("I", cvar->a_uint32);
+ case CIM_ARR_SINT64: RETURN_CVAR_ARRAY("L", cvar->a_sint64);
+ case CIM_ARR_UINT64: RETURN_CVAR_ARRAY("K", cvar->a_uint64);
+ case CIM_ARR_REAL32: RETURN_CVAR_ARRAY("f", cvar->a_real32);
+ case CIM_ARR_REAL64: RETURN_CVAR_ARRAY("d", cvar->a_real64);
+ case CIM_ARR_BOOLEAN: RETURN_CVAR_ARRAY("h", cvar->a_boolean);
+ case CIM_ARR_STRING: RETURN_CVAR_ARRAY("s", cvar->a_string);
+ case CIM_ARR_DATETIME: RETURN_CVAR_ARRAY("s", cvar->a_datetime);
+ case CIM_ARR_REFERENCE: RETURN_CVAR_ARRAY("s", cvar->a_reference);
+ default:
+ {
+ char *str;
+ str = talloc_asprintf(NULL, "Unsupported CIMTYPE(0x%04X)", cimtype);
+ PyErr_SetString(PyExc_RuntimeError, str);
+ talloc_free(str);
+ return NULL;
+ }
+ }
+}
+
+#undef RETURN_CVAR_ARRAY
+
+PyObject *PySWbemObject_InitProperites(PyObject *o, struct WbemClassObject *wco)
+{
+ PyObject *properties;
+ PyObject *addProp;
+ uint32_t i;
+ int32_t r;
+ PyObject *result;
+
+ result = NULL;
+ properties = PyObject_GetAttrString(o, "Properties_");
+ if (!properties) return NULL;
+ addProp = PyObject_GetAttrString(properties, "Add");
+ if (!addProp) {
+ Py_DECREF(properties);
+ return NULL;
+ }
+
+ for (i = 0; i < wco->obj_class->__PROPERTY_COUNT; ++i) {
+ PyObject *args, *property;
+
+ args = Py_BuildValue("(si)", wco->obj_class->properties[i].property.name, wco->obj_class->properties[i].property.desc->cimtype & CIM_TYPEMASK);
+ if (!args) goto finish;
+ property = PyObject_CallObject(addProp, args);
+ Py_DECREF(args);
+ if (!property) goto finish;
+ if (wco->flags & WCF_INSTANCE) {
+ PyObject *value;
+
+ if (wco->instance->default_flags[i] & 1) {
+ value = Py_None;
+ Py_INCREF(Py_None);
+ } else
+ value = PyObject_FromCVAR(wco->obj_class->properties[i].property.desc->cimtype & CIM_TYPEMASK, &wco->instance->data[i]);
+ if (!value) {
+ Py_DECREF(property);
+ goto finish;
+ }
+ r = PyObject_SetAttrString(property, "Value", value);
+ Py_DECREF(value);
+ if (r == -1) {
+ PyErr_SetString(PyExc_RuntimeError, "Error setting value of property");
+ goto finish;
+ }
+ }
+ Py_DECREF(property);
+ }
+
+ Py_INCREF(Py_None);
+ result = Py_None;
+finish:
+ Py_DECREF(addProp);
+ Py_DECREF(properties);
+ return result;
+}
+
+static PyObject *PySWbemObject_FromWbemClassObject(struct WbemClassObject *wco)
+{
+ PyObject *swo_class, *swo, *args, *result;
+
+ swo_class = PyObject_GetAttrString(mod_win32_client, "SWbemObject");
+ if (!swo_class) return NULL;
+ args = PyTuple_New(0);
+ if (!args) {
+ Py_DECREF(swo_class);
+ return NULL;
+ }
+ swo = PyObject_CallObject(swo_class, args);
+ Py_DECREF(args);
+ Py_DECREF(swo_class);
+ if (!swo) return NULL;
+
+ result = PySWbemObject_InitProperites(swo, wco);
+ if (!result) {
+ Py_DECREF(swo);
+ return NULL;
+ }
+ Py_DECREF(result);
+
+ return swo;
+}
+
+%}
+
+%typemap(in, numinputs=0) struct com_context *ctx {
+ $1 = com_ctx;
+}
+
+%typemap(in, numinputs=0) struct IWbemServices **services (struct IWbemServices *temp) {
+ $1 = &temp;
+}
+
+%typemap(argout) struct IWbemServices **services {
+ PyObject *o;
+ o = SWIG_NewPointerObj(*$1, SWIGTYPE_p_IWbemServices, 0);
+ push_object(&$result, o);
+}
+
+WERROR WBEM_ConnectServer(struct com_context *ctx, const char *server, const char *nspace, const char *user, const char *password,
+ const char *locale, uint32_t flags, const char *authority, struct IWbemContext* wbem_ctx, struct IWbemServices** services);
+
+%typemap(in, numinputs=0) struct IEnumWbemClassObject **ppEnum (struct IEnumWbemClassObject *temp) {
+ $1 = &temp;
+}
+
+%typemap(argout) struct IEnumWbemClassObject **ppEnum {
+ PyObject *o;
+ o = SWIG_NewPointerObj(*$1, SWIGTYPE_p_IEnumWbemClassObject, 0);
+ push_object(&$result, o);
+}
+
+typedef struct IUnknown {
+ %extend {
+ uint32_t Release(TALLOC_CTX *mem_ctx);
+ }
+} IUnknown;
+
+%typemap(in) struct BSTR {
+ $1.data = PyString_AsString($input);
+}
+
+
+typedef struct IWbemServices {
+ %extend {
+ WERROR ExecQuery(TALLOC_CTX *mem_ctx, struct BSTR strQueryLanguage, struct BSTR strQuery, int32_t lFlags, struct IWbemContext *pCtx, struct IEnumWbemClassObject **ppEnum);
+ WERROR ExecNotificationQuery(TALLOC_CTX *mem_ctx, struct BSTR strQueryLanguage, struct BSTR strQuery, int32_t lFlags, struct IWbemContext *pCtx, struct IEnumWbemClassObject **ppEnum);
+ WERROR CreateInstanceEnum(TALLOC_CTX *mem_ctx, struct BSTR strClass,
+ int32_t lFlags, struct IWbemContext *pCtx, struct IEnumWbemClassObject **ppEnum);
+ }
+} IWbemServices;
+
+typedef struct IEnumWbemClassObject {
+ %extend {
+ WERROR Reset(TALLOC_CTX *mem_ctx);
+ }
+} IEnumWbemClassObject;
+
+%typemap(in, numinputs=1) (uint32_t uCount, struct WbemClassObject **apObjects, uint32_t *puReturned) (uint32_t uReturned) {
+ if (PyLong_Check($input))
+ $1 = PyLong_AsUnsignedLong($input);
+ else if (PyLong_Check($input))
+ $1 = PyLong_AsLong($input);
+ else {
+ PyErr_SetString(PyExc_TypeError,"Expected a long or an int");
+ return NULL;
+ }
+ $2 = talloc_array(NULL, struct WbemClassObject *, $1);
+ $3 = &uReturned;
+}
+
+%typemap(argout) (struct WbemClassObject **apObjects, uint32_t *puReturned) {
+ uint32_t i;
+ PyObject *o;
+ int32_t error;
+
+ error = 0;
+
+ $result = PyTuple_New(*$2);
+ for (i = 0; i < *$2; ++i) {
+ if (!error) {
+ o = PySWbemObject_FromWbemClassObject($1[i]);
+ if (!o)
+ --error;
+ else
+ error = PyTuple_SetItem($result, i, o);
+ }
+ talloc_free($1[i]);
+ }
+ talloc_free($1);
+ if (error) return NULL;
+}
+
+WERROR IEnumWbemClassObject_SmartNext(struct IEnumWbemClassObject *d, TALLOC_CTX *mem_ctx, int32_t lTimeout, uint32_t uCount,
+ struct WbemClassObject **apObjects, uint32_t *puReturned);
+
+%init %{
+
+ mod_win32_client = PyImport_ImportModule("win32com.client");
+ mod_pywintypes = PyImport_ImportModule("pywintypes");
+ ComError = PyObject_GetAttrString(mod_pywintypes, "com_error");
+
+ wmi_init(&com_ctx, NULL);
+ {
+ PyObject *pModule;
+
+ pModule = PyImport_ImportModule( "win32com.client" );
+ }
+%}
diff --git a/source4/lib/wmi/wmi.py b/source4/lib/wmi/wmi.py
new file mode 100644
index 0000000..5f68ba9
--- /dev/null
+++ b/source4/lib/wmi/wmi.py
@@ -0,0 +1,120 @@
+# This file was automatically generated by SWIG (http://www.swig.org).
+# Version 1.3.36
+#
+# Don't modify this file, modify the SWIG interface instead.
+
+import _wmi
+import new
+new_instancemethod = new.instancemethod
+try:
+ _swig_property = property
+except NameError:
+ pass # Python < 2.2 doesn't have 'property'.
+
+
+def _swig_setattr_nondynamic(self, class_type, name, value, static=1):
+ if (name == "thisown"):
+ return self.this.own(value)
+ if (name == "this"):
+ if type(value).__name__ == 'PySwigObject':
+ self.__dict__[name] = value
+ return
+ method = class_type.__swig_setmethods__.get(name, None)
+ if method:
+ return method(self, value)
+ if (not static) or hasattr(self, name):
+ self.__dict__[name] = value
+ else:
+ raise AttributeError("You cannot add attributes to %s" % self)
+
+
+def _swig_setattr(self, class_type, name, value):
+ return _swig_setattr_nondynamic(self, class_type, name, value, 0)
+
+
+def _swig_getattr(self, class_type, name):
+ if (name == "thisown"):
+ return self.this.own()
+ method = class_type.__swig_getmethods__.get(name, None)
+ if method:
+ return method(self)
+ raise AttributeError(name)
+
+
+def _swig_repr(self):
+ try:
+ strthis = "proxy of " + self.this.__repr__()
+ except:
+ strthis = ""
+ return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,)
+
+
+import types
+try:
+ _object = types.ObjectType
+ _newclass = 1
+except AttributeError:
+ class _object:
+ pass
+ _newclass = 0
+del types
+
+
+def _swig_setattr_nondynamic_method(set):
+ def set_attr(self, name, value):
+ if (name == "thisown"):
+ return self.this.own(value)
+ if hasattr(self, name) or (name == "this"):
+ set(self, name, value)
+ else:
+ raise AttributeError("You cannot add attributes to %s" % self)
+ return set_attr
+
+
+WBEM_ConnectServer = _wmi.WBEM_ConnectServer
+
+
+class IUnknown(object):
+ thisown = _swig_property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc='The membership flag')
+ __repr__ = _swig_repr
+
+ def __init__(self, *args, **kwargs):
+ _wmi.IUnknown_swiginit(self, _wmi.new_IUnknown(*args, **kwargs))
+ __swig_destroy__ = _wmi.delete_IUnknown
+
+
+IUnknown.Release = new_instancemethod(_wmi.IUnknown_Release, None, IUnknown)
+IUnknown_swigregister = _wmi.IUnknown_swigregister
+IUnknown_swigregister(IUnknown)
+
+
+class IWbemServices(object):
+ thisown = _swig_property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc='The membership flag')
+ __repr__ = _swig_repr
+
+ def __init__(self, *args, **kwargs):
+ _wmi.IWbemServices_swiginit(self, _wmi.new_IWbemServices(*args, **kwargs))
+ __swig_destroy__ = _wmi.delete_IWbemServices
+
+
+IWbemServices.ExecQuery = new_instancemethod(_wmi.IWbemServices_ExecQuery, None, IWbemServices)
+IWbemServices.ExecNotificationQuery = new_instancemethod(_wmi.IWbemServices_ExecNotificationQuery, None, IWbemServices)
+IWbemServices.CreateInstanceEnum = new_instancemethod(_wmi.IWbemServices_CreateInstanceEnum, None, IWbemServices)
+IWbemServices_swigregister = _wmi.IWbemServices_swigregister
+IWbemServices_swigregister(IWbemServices)
+
+
+class IEnumWbemClassObject(object):
+ thisown = _swig_property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc='The membership flag')
+ __repr__ = _swig_repr
+
+ def __init__(self, *args, **kwargs):
+ _wmi.IEnumWbemClassObject_swiginit(self, _wmi.new_IEnumWbemClassObject(*args, **kwargs))
+ __swig_destroy__ = _wmi.delete_IEnumWbemClassObject
+
+
+IEnumWbemClassObject.Reset = new_instancemethod(_wmi.IEnumWbemClassObject_Reset, None, IEnumWbemClassObject)
+IEnumWbemClassObject_swigregister = _wmi.IEnumWbemClassObject_swigregister
+IEnumWbemClassObject_swigregister(IEnumWbemClassObject)
+
+IEnumWbemClassObject_SmartNext = _wmi.IEnumWbemClassObject_SmartNext
diff --git a/source4/lib/wmi/wmi_wrap.c b/source4/lib/wmi/wmi_wrap.c
new file mode 100644
index 0000000..f8200ba
--- /dev/null
+++ b/source4/lib/wmi/wmi_wrap.c
@@ -0,0 +1,4304 @@
+/* ----------------------------------------------------------------------------
+ * This file was automatically generated by SWIG (http://www.swig.org).
+ * Version 1.3.36
+ *
+ * This file is not intended to be easily readable and contains a number of
+ * coding conventions designed to improve portability and efficiency. Do not make
+ * changes to this file unless you know what you are doing--modify the SWIG
+ * interface file instead.
+ * ----------------------------------------------------------------------------- */
+
+#define SWIGPYTHON
+#define SWIG_PYTHON_NO_BUILD_NONE
+/* -----------------------------------------------------------------------------
+ * This section contains generic SWIG labels for method/variable
+ * declarations/attributes, and other compiler dependent labels.
+ * ----------------------------------------------------------------------------- */
+
+/* template workaround for compilers that cannot correctly implement the C++ standard */
+#ifndef SWIGTEMPLATEDISAMBIGUATOR
+# if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560)
+# define SWIGTEMPLATEDISAMBIGUATOR template
+# elif defined(__HP_aCC)
+/* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */
+/* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */
+# define SWIGTEMPLATEDISAMBIGUATOR template
+# else
+# define SWIGTEMPLATEDISAMBIGUATOR
+# endif
+#endif
+
+/* inline attribute */
+#ifndef SWIGINLINE
+# if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__))
+# define SWIGINLINE inline
+# else
+# define SWIGINLINE
+# endif
+#endif
+
+/* attribute recognised by some compilers to avoid 'unused' warnings */
+#ifndef SWIGUNUSED
+# ifdef HAVE___ATTRIBUTE__
+# define SWIGUNUSED __attribute__ ((__unused__))
+# else
+# define SWIGUNUSED
+# endif
+#endif
+
+#ifndef SWIG_MSC_UNSUPPRESS_4505
+# if defined(_MSC_VER)
+# pragma warning(disable : 4505) /* unreferenced local function has been removed */
+# endif
+#endif
+
+#ifndef SWIGUNUSEDPARM
+# ifdef __cplusplus
+# define SWIGUNUSEDPARM(p)
+# else
+# define SWIGUNUSEDPARM(p) p SWIGUNUSED
+# endif
+#endif
+
+/* internal SWIG method */
+#ifndef SWIGINTERN
+# define SWIGINTERN static SWIGUNUSED
+#endif
+
+/* internal inline SWIG method */
+#ifndef SWIGINTERNINLINE
+# define SWIGINTERNINLINE SWIGINTERN SWIGINLINE
+#endif
+
+/* exporting methods */
+#if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+# ifndef GCC_HASCLASSVISIBILITY
+# define GCC_HASCLASSVISIBILITY
+# endif
+#endif
+
+#ifndef SWIGEXPORT
+# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+# if defined(STATIC_LINKED)
+# define SWIGEXPORT
+# else
+# define SWIGEXPORT __declspec(dllexport)
+# endif
+# else
+# if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY)
+# define SWIGEXPORT __attribute__ ((visibility("default")))
+# else
+# define SWIGEXPORT
+# endif
+# endif
+#endif
+
+/* calling conventions for Windows */
+#ifndef SWIGSTDCALL
+# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+# define SWIGSTDCALL __stdcall
+# else
+# define SWIGSTDCALL
+# endif
+#endif
+
+/* Deal with Microsoft's attempt at deprecating C standard runtime functions */
+#if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE)
+# define _CRT_SECURE_NO_DEPRECATE
+#endif
+
+/* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */
+#if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE)
+# define _SCL_SECURE_NO_DEPRECATE
+#endif
+
+
+
+/* Python.h has to appear first */
+#include <Python.h>
+
+/* -----------------------------------------------------------------------------
+ * swigrun.swg
+ *
+ * This file contains generic CAPI SWIG runtime support for pointer
+ * type checking.
+ * ----------------------------------------------------------------------------- */
+
+/* This should only be incremented when either the layout of swig_type_info changes,
+ or for whatever reason, the runtime changes incompatibly */
+#define SWIG_RUNTIME_VERSION "4"
+
+/* define SWIG_TYPE_TABLE_NAME as "SWIG_TYPE_TABLE" */
+#ifdef SWIG_TYPE_TABLE
+# define SWIG_QUOTE_STRING(x) #x
+# define SWIG_EXPAND_AND_QUOTE_STRING(x) SWIG_QUOTE_STRING(x)
+# define SWIG_TYPE_TABLE_NAME SWIG_EXPAND_AND_QUOTE_STRING(SWIG_TYPE_TABLE)
+#else
+# define SWIG_TYPE_TABLE_NAME
+#endif
+
+/*
+ You can use the SWIGRUNTIME and SWIGRUNTIMEINLINE macros for
+ creating a static or dynamic library from the swig runtime code.
+ In 99.9% of the cases, swig just needs to declare them as 'static'.
+
+ But only do this if is strictly necessary, ie, if you have problems
+ with your compiler or so.
+*/
+
+#ifndef SWIGRUNTIME
+# define SWIGRUNTIME SWIGINTERN
+#endif
+
+#ifndef SWIGRUNTIMEINLINE
+# define SWIGRUNTIMEINLINE SWIGRUNTIME SWIGINLINE
+#endif
+
+/* Generic buffer size */
+#ifndef SWIG_BUFFER_SIZE
+# define SWIG_BUFFER_SIZE 1024
+#endif
+
+/* Flags for pointer conversions */
+#define SWIG_POINTER_DISOWN 0x1
+#define SWIG_CAST_NEW_MEMORY 0x2
+
+/* Flags for new pointer objects */
+#define SWIG_POINTER_OWN 0x1
+
+
+/*
+ Flags/methods for returning states.
+
+ The swig conversion methods, as ConvertPtr, return and integer
+ that tells if the conversion was successful or not. And if not,
+ an error code can be returned (see swigerrors.swg for the codes).
+
+ Use the following macros/flags to set or process the returning
+ states.
+
+ In old swig versions, you usually write code as:
+
+ if (SWIG_ConvertPtr(obj,vptr,ty.flags) != -1) {
+ /* success code */
+ } else {
+ /*fail code */
+ }
+
+ Now you can be more explicit as:
+
+ int res = SWIG_ConvertPtr(obj,vptr,ty.flags);
+ if (SWIG_IsOK(res)) {
+ /* success code */
+ } else {
+ /* fail code */
+ }
+
+ that seems to be the same, but now you can also do
+
+ Type *ptr;
+ int res = SWIG_ConvertPtr(obj,(void **)(&ptr),ty.flags);
+ if (SWIG_IsOK(res)) {
+ /* success code */
+ if (SWIG_IsNewObj(res) {
+ ...
+ delete *ptr;
+ } else {
+ ...
+ }
+ } else {
+ /* fail code */
+ }
+
+ I.e., now SWIG_ConvertPtr can return new objects and you can
+ identify the case and take care of the deallocation. Of course that
+ requires also to SWIG_ConvertPtr to return new result values, as
+
+ int SWIG_ConvertPtr(obj, ptr,...) {
+ if (<obj is ok>) {
+ if (<need new object>) {
+ *ptr = <ptr to new allocated object>;
+ return SWIG_NEWOBJ;
+ } else {
+ *ptr = <ptr to old object>;
+ return SWIG_OLDOBJ;
+ }
+ } else {
+ return SWIG_BADOBJ;
+ }
+ }
+
+ Of course, returning the plain '0(success)/-1(fail)' still works, but you can be
+ more explicit by returning SWIG_BADOBJ, SWIG_ERROR or any of the
+ swig errors code.
+
+ Finally, if the SWIG_CASTRANK_MODE is enabled, the result code
+ allows one to return the 'cast rank', for example, if you have this
+
+ int food(double)
+ int fooi(int);
+
+ and you call
+
+ food(1) /* cast rank '1' (1 -> 1.0) */
+ fooi(1) /* cast rank '0' */
+
+ just use the SWIG_AddCast()/SWIG_CheckState()
+
+
+ */
+#define SWIG_OK (0)
+#define SWIG_ERROR (-1)
+#define SWIG_IsOK(r) (r >= 0)
+#define SWIG_ArgError(r) ((r != SWIG_ERROR) ? r : SWIG_TypeError)
+
+/* The CastRankLimit says how many bits are used for the cast rank */
+#define SWIG_CASTRANKLIMIT (1 << 8)
+/* The NewMask denotes the object was created (using new/malloc) */
+#define SWIG_NEWOBJMASK (SWIG_CASTRANKLIMIT << 1)
+/* The TmpMask is for in/out typemaps that use temporal objects */
+#define SWIG_TMPOBJMASK (SWIG_NEWOBJMASK << 1)
+/* Simple returning values */
+#define SWIG_BADOBJ (SWIG_ERROR)
+#define SWIG_OLDOBJ (SWIG_OK)
+#define SWIG_NEWOBJ (SWIG_OK | SWIG_NEWOBJMASK)
+#define SWIG_TMPOBJ (SWIG_OK | SWIG_TMPOBJMASK)
+/* Check, add and del mask methods */
+#define SWIG_AddNewMask(r) (SWIG_IsOK(r) ? (r | SWIG_NEWOBJMASK) : r)
+#define SWIG_DelNewMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_NEWOBJMASK) : r)
+#define SWIG_IsNewObj(r) (SWIG_IsOK(r) && (r & SWIG_NEWOBJMASK))
+#define SWIG_AddTmpMask(r) (SWIG_IsOK(r) ? (r | SWIG_TMPOBJMASK) : r)
+#define SWIG_DelTmpMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_TMPOBJMASK) : r)
+#define SWIG_IsTmpObj(r) (SWIG_IsOK(r) && (r & SWIG_TMPOBJMASK))
+
+
+/* Cast-Rank Mode */
+#if defined(SWIG_CASTRANK_MODE)
+# ifndef SWIG_TypeRank
+# define SWIG_TypeRank unsigned long
+# endif
+# ifndef SWIG_MAXCASTRANK /* Default cast allowed */
+# define SWIG_MAXCASTRANK (2)
+# endif
+# define SWIG_CASTRANKMASK ((SWIG_CASTRANKLIMIT) -1)
+# define SWIG_CastRank(r) (r & SWIG_CASTRANKMASK)
+SWIGINTERNINLINE int SWIG_AddCast(int r) {
+ return SWIG_IsOK(r) ? ((SWIG_CastRank(r) < SWIG_MAXCASTRANK) ? (r + 1) : SWIG_ERROR) : r;
+}
+SWIGINTERNINLINE int SWIG_CheckState(int r) {
+ return SWIG_IsOK(r) ? SWIG_CastRank(r) + 1 : 0;
+}
+#else /* no cast-rank mode */
+# define SWIG_AddCast
+# define SWIG_CheckState(r) (SWIG_IsOK(r) ? 1 : 0)
+#endif
+
+
+
+
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void *(*swig_converter_func)(void *, int *);
+typedef struct swig_type_info *(*swig_dycast_func)(void **);
+
+/* Structure to store information on one type */
+typedef struct swig_type_info {
+ const char *name; /* mangled name of this type */
+ const char *str; /* human readable name of this type */
+ swig_dycast_func dcast; /* dynamic cast function down a hierarchy */
+ struct swig_cast_info *cast; /* linked list of types that can cast into this type */
+ void *clientdata; /* language specific type data */
+ int owndata; /* flag if the structure owns the clientdata */
+} swig_type_info;
+
+/* Structure to store a type and conversion function used for casting */
+typedef struct swig_cast_info {
+ swig_type_info *type; /* pointer to type that is equivalent to this type */
+ swig_converter_func converter; /* function to cast the void pointers */
+ struct swig_cast_info *next; /* pointer to next cast in linked list */
+ struct swig_cast_info *prev; /* pointer to the previous cast */
+} swig_cast_info;
+
+/* Structure used to store module information
+ * Each module generates one structure like this, and the runtime collects
+ * all of these structures and stores them in a circularly linked list.*/
+typedef struct swig_module_info {
+ swig_type_info **types; /* Array of pointers to swig_type_info structures that are in this module */
+ size_t size; /* Number of types in this module */
+ struct swig_module_info *next; /* Pointer to next element in circularly linked list */
+ swig_type_info **type_initial; /* Array of initially generated type structures */
+ swig_cast_info **cast_initial; /* Array of initially generated casting structures */
+ void *clientdata; /* Language specific module data */
+} swig_module_info;
+
+/*
+ Compare two type names skipping the space characters, therefore
+ "char*" == "char *" and "Class<int>" == "Class<int >", etc.
+
+ Return 0 when the two name types are equivalent, as in
+ strncmp, but skipping ' '.
+*/
+SWIGRUNTIME int
+SWIG_TypeNameComp(const char *f1, const char *l1,
+ const char *f2, const char *l2) {
+ for (;(f1 != l1) && (f2 != l2); ++f1, ++f2) {
+ while ((*f1 == ' ') && (f1 != l1)) ++f1;
+ while ((*f2 == ' ') && (f2 != l2)) ++f2;
+ if (*f1 != *f2) return (*f1 > *f2) ? 1 : -1;
+ }
+ return (int)((l1 - f1) - (l2 - f2));
+}
+
+/*
+ Check type equivalence in a name list like <name1>|<name2>|...
+ Return 0 if not equal, 1 if equal
+*/
+SWIGRUNTIME int
+SWIG_TypeEquiv(const char *nb, const char *tb) {
+ int equiv = 0;
+ const char* te = tb + strlen(tb);
+ const char* ne = nb;
+ while (!equiv && *ne) {
+ for (nb = ne; *ne; ++ne) {
+ if (*ne == '|') break;
+ }
+ equiv = (SWIG_TypeNameComp(nb, ne, tb, te) == 0) ? 1 : 0;
+ if (*ne) ++ne;
+ }
+ return equiv;
+}
+
+/*
+ Check type equivalence in a name list like <name1>|<name2>|...
+ Return 0 if equal, -1 if nb < tb, 1 if nb > tb
+*/
+SWIGRUNTIME int
+SWIG_TypeCompare(const char *nb, const char *tb) {
+ int equiv = 0;
+ const char* te = tb + strlen(tb);
+ const char* ne = nb;
+ while (!equiv && *ne) {
+ for (nb = ne; *ne; ++ne) {
+ if (*ne == '|') break;
+ }
+ equiv = (SWIG_TypeNameComp(nb, ne, tb, te) == 0) ? 1 : 0;
+ if (*ne) ++ne;
+ }
+ return equiv;
+}
+
+
+/* think of this as a c++ template<> or a scheme macro */
+#define SWIG_TypeCheck_Template(comparison, ty) \
+ if (ty) { \
+ swig_cast_info *iter = ty->cast; \
+ while (iter) { \
+ if (comparison) { \
+ if (iter == ty->cast) return iter; \
+ /* Move iter to the top of the linked list */ \
+ iter->prev->next = iter->next; \
+ if (iter->next) \
+ iter->next->prev = iter->prev; \
+ iter->next = ty->cast; \
+ iter->prev = 0; \
+ if (ty->cast) ty->cast->prev = iter; \
+ ty->cast = iter; \
+ return iter; \
+ } \
+ iter = iter->next; \
+ } \
+ } \
+ return 0
+
+/*
+ Check the typename
+*/
+SWIGRUNTIME swig_cast_info *
+SWIG_TypeCheck(const char *c, swig_type_info *ty) {
+ SWIG_TypeCheck_Template(strcmp(iter->type->name, c) == 0, ty);
+}
+
+/* Same as previous function, except strcmp is replaced with a pointer comparison */
+SWIGRUNTIME swig_cast_info *
+SWIG_TypeCheckStruct(swig_type_info *from, swig_type_info *into) {
+ SWIG_TypeCheck_Template(iter->type == from, into);
+}
+
+/*
+ Cast a pointer up an inheritance hierarchy
+*/
+SWIGRUNTIMEINLINE void *
+SWIG_TypeCast(swig_cast_info *ty, void *ptr, int *newmemory) {
+ return ((!ty) || (!ty->converter)) ? ptr : (*ty->converter)(ptr, newmemory);
+}
+
+/*
+ Dynamic pointer casting. Down an inheritance hierarchy
+*/
+SWIGRUNTIME swig_type_info *
+SWIG_TypeDynamicCast(swig_type_info *ty, void **ptr) {
+ swig_type_info *lastty = ty;
+ if (!ty || !ty->dcast) return ty;
+ while (ty && (ty->dcast)) {
+ ty = (*ty->dcast)(ptr);
+ if (ty) lastty = ty;
+ }
+ return lastty;
+}
+
+/*
+ Return the name associated with this type
+*/
+SWIGRUNTIMEINLINE const char *
+SWIG_TypeName(const swig_type_info *ty) {
+ return ty->name;
+}
+
+/*
+ Return the pretty name associated with this type,
+ that is an unmangled type name in a form presentable to the user.
+*/
+SWIGRUNTIME const char *
+SWIG_TypePrettyName(const swig_type_info *type) {
+ /* The "str" field contains the equivalent pretty names of the
+ type, separated by vertical-bar characters. We choose
+ to print the last name, as it is often (?) the most
+ specific. */
+ if (!type) return NULL;
+ if (type->str != NULL) {
+ const char *last_name = type->str;
+ const char *s;
+ for (s = type->str; *s; s++)
+ if (*s == '|') last_name = s+1;
+ return last_name;
+ }
+ else
+ return type->name;
+}
+
+/*
+ Set the clientdata field for a type
+*/
+SWIGRUNTIME void
+SWIG_TypeClientData(swig_type_info *ti, void *clientdata) {
+ swig_cast_info *cast = ti->cast;
+ /* if (ti->clientdata == clientdata) return; */
+ ti->clientdata = clientdata;
+
+ while (cast) {
+ if (!cast->converter) {
+ swig_type_info *tc = cast->type;
+ if (!tc->clientdata) {
+ SWIG_TypeClientData(tc, clientdata);
+ }
+ }
+ cast = cast->next;
+ }
+}
+SWIGRUNTIME void
+SWIG_TypeNewClientData(swig_type_info *ti, void *clientdata) {
+ SWIG_TypeClientData(ti, clientdata);
+ ti->owndata = 1;
+}
+
+/*
+ Search for a swig_type_info structure only by mangled name
+ Search is a O(log #types)
+
+ We start searching at module start, and finish searching when start == end.
+ Note: if start == end at the beginning of the function, we go all the way around
+ the circular list.
+*/
+SWIGRUNTIME swig_type_info *
+SWIG_MangledTypeQueryModule(swig_module_info *start,
+ swig_module_info *end,
+ const char *name) {
+ swig_module_info *iter = start;
+ do {
+ if (iter->size) {
+ register size_t l = 0;
+ register size_t r = iter->size - 1;
+ do {
+ /* since l+r >= 0, we can (>> 1) instead (/ 2) */
+ register size_t i = (l + r) >> 1;
+ const char *iname = iter->types[i]->name;
+ if (iname) {
+ register int compare = strcmp(name, iname);
+ if (compare == 0) {
+ return iter->types[i];
+ } else if (compare < 0) {
+ if (i) {
+ r = i - 1;
+ } else {
+ break;
+ }
+ } else if (compare > 0) {
+ l = i + 1;
+ }
+ } else {
+ break; /* should never happen */
+ }
+ } while (l <= r);
+ }
+ iter = iter->next;
+ } while (iter != end);
+ return 0;
+}
+
+/*
+ Search for a swig_type_info structure for either a mangled name or a human readable name.
+ It first searches the mangled names of the types, which is a O(log #types)
+ If a type is not found it then searches the human readable names, which is O(#types).
+
+ We start searching at module start, and finish searching when start == end.
+ Note: if start == end at the beginning of the function, we go all the way around
+ the circular list.
+*/
+SWIGRUNTIME swig_type_info *
+SWIG_TypeQueryModule(swig_module_info *start,
+ swig_module_info *end,
+ const char *name) {
+ /* STEP 1: Search the name field using binary search */
+ swig_type_info *ret = SWIG_MangledTypeQueryModule(start, end, name);
+ if (ret) {
+ return ret;
+ } else {
+ /* STEP 2: If the type hasn't been found, do a complete search
+ of the str field (the human readable name) */
+ swig_module_info *iter = start;
+ do {
+ register size_t i = 0;
+ for (; i < iter->size; ++i) {
+ if (iter->types[i]->str && (SWIG_TypeEquiv(iter->types[i]->str, name)))
+ return iter->types[i];
+ }
+ iter = iter->next;
+ } while (iter != end);
+ }
+
+ /* neither found a match */
+ return 0;
+}
+
+/*
+ Pack binary data into a string
+*/
+SWIGRUNTIME char *
+SWIG_PackData(char *c, void *ptr, size_t sz) {
+ static const char hex[17] = "0123456789abcdef";
+ register const unsigned char *u = (unsigned char *) ptr;
+ register const unsigned char *eu = u + sz;
+ for (; u != eu; ++u) {
+ register unsigned char uu = *u;
+ *(c++) = hex[(uu & 0xf0) >> 4];
+ *(c++) = hex[uu & 0xf];
+ }
+ return c;
+}
+
+/*
+ Unpack binary data from a string
+*/
+SWIGRUNTIME const char *
+SWIG_UnpackData(const char *c, void *ptr, size_t sz) {
+ register unsigned char *u = (unsigned char *) ptr;
+ register const unsigned char *eu = u + sz;
+ for (; u != eu; ++u) {
+ register char d = *(c++);
+ register unsigned char uu;
+ if ((d >= '0') && (d <= '9'))
+ uu = ((d - '0') << 4);
+ else if ((d >= 'a') && (d <= 'f'))
+ uu = ((d - ('a'-10)) << 4);
+ else
+ return (char *) 0;
+ d = *(c++);
+ if ((d >= '0') && (d <= '9'))
+ uu |= (d - '0');
+ else if ((d >= 'a') && (d <= 'f'))
+ uu |= (d - ('a'-10));
+ else
+ return (char *) 0;
+ *u = uu;
+ }
+ return c;
+}
+
+/*
+ Pack 'void *' into a string buffer.
+*/
+SWIGRUNTIME char *
+SWIG_PackVoidPtr(char *buff, void *ptr, const char *name, size_t bsz) {
+ char *r = buff;
+ if ((2*sizeof(void *) + 2) > bsz) return 0;
+ *(r++) = '_';
+ r = SWIG_PackData(r,&ptr,sizeof(void *));
+ if (strlen(name) + 1 > (bsz - (r - buff))) return 0;
+ strcpy(r,name);
+ return buff;
+}
+
+SWIGRUNTIME const char *
+SWIG_UnpackVoidPtr(const char *c, void **ptr, const char *name) {
+ if (*c != '_') {
+ if (strcmp(c,"NULL") == 0) {
+ *ptr = (void *) 0;
+ return name;
+ } else {
+ return 0;
+ }
+ }
+ return SWIG_UnpackData(++c,ptr,sizeof(void *));
+}
+
+SWIGRUNTIME char *
+SWIG_PackDataName(char *buff, void *ptr, size_t sz, const char *name, size_t bsz) {
+ char *r = buff;
+ size_t lname = (name ? strlen(name) : 0);
+ if ((2*sz + 2 + lname) > bsz) return 0;
+ *(r++) = '_';
+ r = SWIG_PackData(r,ptr,sz);
+ if (lname) {
+ strncpy(r,name,lname+1);
+ } else {
+ *r = 0;
+ }
+ return buff;
+}
+
+SWIGRUNTIME const char *
+SWIG_UnpackDataName(const char *c, void *ptr, size_t sz, const char *name) {
+ if (*c != '_') {
+ if (strcmp(c,"NULL") == 0) {
+ memset(ptr,0,sz);
+ return name;
+ } else {
+ return 0;
+ }
+ }
+ return SWIG_UnpackData(++c,ptr,sz);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+/* Errors in SWIG */
+#define SWIG_UnknownError -1
+#define SWIG_IOError -2
+#define SWIG_RuntimeError -3
+#define SWIG_IndexError -4
+#define SWIG_TypeError -5
+#define SWIG_DivisionByZero -6
+#define SWIG_OverflowError -7
+#define SWIG_SyntaxError -8
+#define SWIG_ValueError -9
+#define SWIG_SystemError -10
+#define SWIG_AttributeError -11
+#define SWIG_MemoryError -12
+#define SWIG_NullReferenceError -13
+
+
+
+
+/* Add PyOS_snprintf for old Pythons */
+#if PY_VERSION_HEX < 0x02020000
+# if defined(_MSC_VER) || defined(__BORLANDC__) || defined(_WATCOM)
+# define PyOS_snprintf _snprintf
+# else
+# define PyOS_snprintf snprintf
+# endif
+#endif
+
+/* A crude PyString_FromFormat implementation for old Pythons */
+#if PY_VERSION_HEX < 0x02020000
+
+#ifndef SWIG_PYBUFFER_SIZE
+# define SWIG_PYBUFFER_SIZE 1024
+#endif
+
+static PyObject *
+PyString_FromFormat(const char *fmt, ...) {
+ va_list ap;
+ char buf[SWIG_PYBUFFER_SIZE * 2];
+ int res;
+ va_start(ap, fmt);
+ res = vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ return (res < 0 || res >= (int)sizeof(buf)) ? 0 : PyString_FromString(buf);
+}
+#endif
+
+/* Add PyObject_Del for old Pythons */
+#if PY_VERSION_HEX < 0x01060000
+# define PyObject_Del(op) PyMem_DEL((op))
+#endif
+#ifndef PyObject_DEL
+# define PyObject_DEL PyObject_Del
+#endif
+
+/* A crude PyExc_StopIteration exception for old Pythons */
+#if PY_VERSION_HEX < 0x02020000
+# ifndef PyExc_StopIteration
+# define PyExc_StopIteration PyExc_RuntimeError
+# endif
+# ifndef PyObject_GenericGetAttr
+# define PyObject_GenericGetAttr 0
+# endif
+#endif
+/* Py_NotImplemented is defined in 2.1 and up. */
+#if PY_VERSION_HEX < 0x02010000
+# ifndef Py_NotImplemented
+# define Py_NotImplemented PyExc_RuntimeError
+# endif
+#endif
+
+
+/* A crude PyString_AsStringAndSize implementation for old Pythons */
+#if PY_VERSION_HEX < 0x02010000
+# ifndef PyString_AsStringAndSize
+# define PyString_AsStringAndSize(obj, s, len) {*s = PyString_AsString(obj); *len = *s ? strlen(*s) : 0;}
+# endif
+#endif
+
+/* PySequence_Size for old Pythons */
+#if PY_VERSION_HEX < 0x02000000
+# ifndef PySequence_Size
+# define PySequence_Size PySequence_Length
+# endif
+#endif
+
+
+/* PyBool_FromLong for old Pythons */
+#if PY_VERSION_HEX < 0x02030000
+static
+PyObject *PyBool_FromLong(long ok)
+{
+ PyObject *result = ok ? Py_True : Py_False;
+ Py_INCREF(result);
+ return result;
+}
+#endif
+
+/* Py_ssize_t for old Pythons */
+/* This code is as recommended by: */
+/* http://www.python.org/dev/peps/pep-0353/#conversion-guidelines */
+#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
+typedef int Py_ssize_t;
+# define PY_SSIZE_T_MAX INT_MAX
+# define PY_SSIZE_T_MIN INT_MIN
+#endif
+
+/* -----------------------------------------------------------------------------
+ * error manipulation
+ * ----------------------------------------------------------------------------- */
+
+SWIGRUNTIME PyObject*
+SWIG_Python_ErrorType(int code) {
+ PyObject* type = 0;
+ switch(code) {
+ case SWIG_MemoryError:
+ type = PyExc_MemoryError;
+ break;
+ case SWIG_IOError:
+ type = PyExc_IOError;
+ break;
+ case SWIG_RuntimeError:
+ type = PyExc_RuntimeError;
+ break;
+ case SWIG_IndexError:
+ type = PyExc_IndexError;
+ break;
+ case SWIG_TypeError:
+ type = PyExc_TypeError;
+ break;
+ case SWIG_DivisionByZero:
+ type = PyExc_ZeroDivisionError;
+ break;
+ case SWIG_OverflowError:
+ type = PyExc_OverflowError;
+ break;
+ case SWIG_SyntaxError:
+ type = PyExc_SyntaxError;
+ break;
+ case SWIG_ValueError:
+ type = PyExc_ValueError;
+ break;
+ case SWIG_SystemError:
+ type = PyExc_SystemError;
+ break;
+ case SWIG_AttributeError:
+ type = PyExc_AttributeError;
+ break;
+ default:
+ type = PyExc_RuntimeError;
+ }
+ return type;
+}
+
+
+SWIGRUNTIME void
+SWIG_Python_AddErrorMsg(const char* mesg)
+{
+ PyObject *type = 0;
+ PyObject *value = 0;
+ PyObject *traceback = 0;
+
+ if (PyErr_Occurred()) PyErr_Fetch(&type, &value, &traceback);
+ if (value) {
+ PyObject *old_str = PyObject_Str(value);
+ PyErr_Clear();
+ Py_XINCREF(type);
+ PyErr_Format(type, "%s %s", PyString_AsString(old_str), mesg);
+ Py_DECREF(old_str);
+ Py_DECREF(value);
+ } else {
+ PyErr_SetString(PyExc_RuntimeError, mesg);
+ }
+}
+
+
+
+#if defined(SWIG_PYTHON_NO_THREADS)
+# if defined(SWIG_PYTHON_THREADS)
+# undef SWIG_PYTHON_THREADS
+# endif
+#endif
+#if defined(SWIG_PYTHON_THREADS) /* Threading support is enabled */
+# if !defined(SWIG_PYTHON_USE_GIL) && !defined(SWIG_PYTHON_NO_USE_GIL)
+# if (PY_VERSION_HEX >= 0x02030000) /* For 2.3 or later, use the PyGILState calls */
+# define SWIG_PYTHON_USE_GIL
+# endif
+# endif
+# if defined(SWIG_PYTHON_USE_GIL) /* Use PyGILState threads calls */
+# ifndef SWIG_PYTHON_INITIALIZE_THREADS
+# define SWIG_PYTHON_INITIALIZE_THREADS PyEval_InitThreads()
+# endif
+# ifdef __cplusplus /* C++ code */
+ class SWIG_Python_Thread_Block {
+ bool status;
+ PyGILState_STATE state;
+ public:
+ void end() { if (status) { PyGILState_Release(state); status = false;} }
+ SWIG_Python_Thread_Block() : status(true), state(PyGILState_Ensure()) {}
+ ~SWIG_Python_Thread_Block() { end(); }
+ };
+ class SWIG_Python_Thread_Allow {
+ bool status;
+ PyThreadState *save;
+ public:
+ void end() { if (status) { PyEval_RestoreThread(save); status = false; }}
+ SWIG_Python_Thread_Allow() : status(true), save(PyEval_SaveThread()) {}
+ ~SWIG_Python_Thread_Allow() { end(); }
+ };
+# define SWIG_PYTHON_THREAD_BEGIN_BLOCK SWIG_Python_Thread_Block _swig_thread_block
+# define SWIG_PYTHON_THREAD_END_BLOCK _swig_thread_block.end()
+# define SWIG_PYTHON_THREAD_BEGIN_ALLOW SWIG_Python_Thread_Allow _swig_thread_allow
+# define SWIG_PYTHON_THREAD_END_ALLOW _swig_thread_allow.end()
+# else /* C code */
+# define SWIG_PYTHON_THREAD_BEGIN_BLOCK PyGILState_STATE _swig_thread_block = PyGILState_Ensure()
+# define SWIG_PYTHON_THREAD_END_BLOCK PyGILState_Release(_swig_thread_block)
+# define SWIG_PYTHON_THREAD_BEGIN_ALLOW PyThreadState *_swig_thread_allow = PyEval_SaveThread()
+# define SWIG_PYTHON_THREAD_END_ALLOW PyEval_RestoreThread(_swig_thread_allow)
+# endif
+# else /* Old thread way, not implemented, user must provide it */
+# if !defined(SWIG_PYTHON_INITIALIZE_THREADS)
+# define SWIG_PYTHON_INITIALIZE_THREADS
+# endif
+# if !defined(SWIG_PYTHON_THREAD_BEGIN_BLOCK)
+# define SWIG_PYTHON_THREAD_BEGIN_BLOCK
+# endif
+# if !defined(SWIG_PYTHON_THREAD_END_BLOCK)
+# define SWIG_PYTHON_THREAD_END_BLOCK
+# endif
+# if !defined(SWIG_PYTHON_THREAD_BEGIN_ALLOW)
+# define SWIG_PYTHON_THREAD_BEGIN_ALLOW
+# endif
+# if !defined(SWIG_PYTHON_THREAD_END_ALLOW)
+# define SWIG_PYTHON_THREAD_END_ALLOW
+# endif
+# endif
+#else /* No thread support */
+# define SWIG_PYTHON_INITIALIZE_THREADS
+# define SWIG_PYTHON_THREAD_BEGIN_BLOCK
+# define SWIG_PYTHON_THREAD_END_BLOCK
+# define SWIG_PYTHON_THREAD_BEGIN_ALLOW
+# define SWIG_PYTHON_THREAD_END_ALLOW
+#endif
+
+/* -----------------------------------------------------------------------------
+ * Python API portion that goes into the runtime
+ * ----------------------------------------------------------------------------- */
+
+#ifdef __cplusplus
+extern "C" {
+#if 0
+} /* cc-mode */
+#endif
+#endif
+
+/* -----------------------------------------------------------------------------
+ * Constant declarations
+ * ----------------------------------------------------------------------------- */
+
+/* Constant Types */
+#define SWIG_PY_POINTER 4
+#define SWIG_PY_BINARY 5
+
+/* Constant information structure */
+typedef struct swig_const_info {
+ int type;
+ char *name;
+ long lvalue;
+ double dvalue;
+ void *pvalue;
+ swig_type_info **ptype;
+} swig_const_info;
+
+#ifdef __cplusplus
+#if 0
+{ /* cc-mode */
+#endif
+}
+#endif
+
+
+/* -----------------------------------------------------------------------------
+ * See the LICENSE file for information on copyright, usage and redistribution
+ * of SWIG, and the README file for authors - http://www.swig.org/release.html.
+ *
+ * pyrun.swg
+ *
+ * This file contains the runtime support for Python modules
+ * and includes code for managing global variables and pointer
+ * type checking.
+ *
+ * ----------------------------------------------------------------------------- */
+
+/* Common SWIG API */
+
+/* for raw pointers */
+#define SWIG_Python_ConvertPtr(obj, pptr, type, flags) SWIG_Python_ConvertPtrAndOwn(obj, pptr, type, flags, 0)
+#define SWIG_ConvertPtr(obj, pptr, type, flags) SWIG_Python_ConvertPtr(obj, pptr, type, flags)
+#define SWIG_ConvertPtrAndOwn(obj,pptr,type,flags,own) SWIG_Python_ConvertPtrAndOwn(obj, pptr, type, flags, own)
+#define SWIG_NewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(ptr, type, flags)
+#define SWIG_CheckImplicit(ty) SWIG_Python_CheckImplicit(ty)
+#define SWIG_AcquirePtr(ptr, src) SWIG_Python_AcquirePtr(ptr, src)
+#define swig_owntype int
+
+/* for raw packed data */
+#define SWIG_ConvertPacked(obj, ptr, sz, ty) SWIG_Python_ConvertPacked(obj, ptr, sz, ty)
+#define SWIG_NewPackedObj(ptr, sz, type) SWIG_Python_NewPackedObj(ptr, sz, type)
+
+/* for class or struct pointers */
+#define SWIG_ConvertInstance(obj, pptr, type, flags) SWIG_ConvertPtr(obj, pptr, type, flags)
+#define SWIG_NewInstanceObj(ptr, type, flags) SWIG_NewPointerObj(ptr, type, flags)
+
+/* for C or C++ function pointers */
+#define SWIG_ConvertFunctionPtr(obj, pptr, type) SWIG_Python_ConvertFunctionPtr(obj, pptr, type)
+#define SWIG_NewFunctionPtrObj(ptr, type) SWIG_Python_NewPointerObj(ptr, type, 0)
+
+/* for C++ member pointers, ie, member methods */
+#define SWIG_ConvertMember(obj, ptr, sz, ty) SWIG_Python_ConvertPacked(obj, ptr, sz, ty)
+#define SWIG_NewMemberObj(ptr, sz, type) SWIG_Python_NewPackedObj(ptr, sz, type)
+
+
+/* Runtime API */
+
+#define SWIG_GetModule(clientdata) SWIG_Python_GetModule()
+#define SWIG_SetModule(clientdata, pointer) SWIG_Python_SetModule(pointer)
+#define SWIG_NewClientData(obj) PySwigClientData_New(obj)
+
+#define SWIG_SetErrorObj SWIG_Python_SetErrorObj
+#define SWIG_SetErrorMsg SWIG_Python_SetErrorMsg
+#define SWIG_ErrorType(code) SWIG_Python_ErrorType(code)
+#define SWIG_Error(code, msg) SWIG_Python_SetErrorMsg(SWIG_ErrorType(code), msg)
+#define SWIG_fail goto fail
+
+
+/* Runtime API implementation */
+
+/* Error manipulation */
+
+SWIGINTERN void
+SWIG_Python_SetErrorObj(PyObject *errtype, PyObject *obj) {
+ SWIG_PYTHON_THREAD_BEGIN_BLOCK;
+ PyErr_SetObject(errtype, obj);
+ Py_DECREF(obj);
+ SWIG_PYTHON_THREAD_END_BLOCK;
+}
+
+SWIGINTERN void
+SWIG_Python_SetErrorMsg(PyObject *errtype, const char *msg) {
+ SWIG_PYTHON_THREAD_BEGIN_BLOCK;
+ PyErr_SetString(errtype, (char *) msg);
+ SWIG_PYTHON_THREAD_END_BLOCK;
+}
+
+#define SWIG_Python_Raise(obj, type, desc) SWIG_Python_SetErrorObj(SWIG_Python_ExceptionType(desc), obj)
+
+/* Set a constant value */
+
+SWIGINTERN void
+SWIG_Python_SetConstant(PyObject *d, const char *name, PyObject *obj) {
+ PyDict_SetItemString(d, (char*) name, obj);
+ Py_DECREF(obj);
+}
+
+/* Append a value to the result obj */
+
+SWIGINTERN PyObject*
+SWIG_Python_AppendOutput(PyObject* result, PyObject* obj) {
+#if !defined(SWIG_PYTHON_OUTPUT_TUPLE)
+ if (!result) {
+ result = obj;
+ } else if (result == Py_None) {
+ Py_DECREF(result);
+ result = obj;
+ } else {
+ if (!PyList_Check(result)) {
+ PyObject *o2 = result;
+ result = PyList_New(1);
+ PyList_SetItem(result, 0, o2);
+ }
+ PyList_Append(result,obj);
+ Py_DECREF(obj);
+ }
+ return result;
+#else
+ PyObject* o2;
+ PyObject* o3;
+ if (!result) {
+ result = obj;
+ } else if (result == Py_None) {
+ Py_DECREF(result);
+ result = obj;
+ } else {
+ if (!PyTuple_Check(result)) {
+ o2 = result;
+ result = PyTuple_New(1);
+ PyTuple_SET_ITEM(result, 0, o2);
+ }
+ o3 = PyTuple_New(1);
+ PyTuple_SET_ITEM(o3, 0, obj);
+ o2 = result;
+ result = PySequence_Concat(o2, o3);
+ Py_DECREF(o2);
+ Py_DECREF(o3);
+ }
+ return result;
+#endif
+}
+
+/* Unpack the argument tuple */
+
+SWIGINTERN int
+SWIG_Python_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t max, PyObject **objs)
+{
+ if (!args) {
+ if (!min && !max) {
+ return 1;
+ } else {
+ PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got none",
+ name, (min == max ? "" : "at least "), (int)min);
+ return 0;
+ }
+ }
+ if (!PyTuple_Check(args)) {
+ PyErr_SetString(PyExc_SystemError, "UnpackTuple() argument list is not a tuple");
+ return 0;
+ } else {
+ register Py_ssize_t l = PyTuple_GET_SIZE(args);
+ if (l < min) {
+ PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got %d",
+ name, (min == max ? "" : "at least "), (int)min, (int)l);
+ return 0;
+ } else if (l > max) {
+ PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got %d",
+ name, (min == max ? "" : "at most "), (int)max, (int)l);
+ return 0;
+ } else {
+ register int i;
+ for (i = 0; i < l; ++i) {
+ objs[i] = PyTuple_GET_ITEM(args, i);
+ }
+ for (; l < max; ++l) {
+ objs[l] = 0;
+ }
+ return i + 1;
+ }
+ }
+}
+
+/* A functor is a function object with one single object argument */
+#if PY_VERSION_HEX >= 0x02020000
+#define SWIG_Python_CallFunctor(functor, obj) PyObject_CallFunctionObjArgs(functor, obj, NULL);
+#else
+#define SWIG_Python_CallFunctor(functor, obj) PyObject_CallFunction(functor, "O", obj);
+#endif
+
+/*
+ Helper for static pointer initialization for both C and C++ code, for example
+ static PyObject *SWIG_STATIC_POINTER(MyVar) = NewSomething(...);
+*/
+#ifdef __cplusplus
+#define SWIG_STATIC_POINTER(var) var
+#else
+#define SWIG_STATIC_POINTER(var) var = 0; if (!var) var
+#endif
+
+/* -----------------------------------------------------------------------------
+ * Pointer declarations
+ * ----------------------------------------------------------------------------- */
+
+/* Flags for new pointer objects */
+#define SWIG_POINTER_NOSHADOW (SWIG_POINTER_OWN << 1)
+#define SWIG_POINTER_NEW (SWIG_POINTER_NOSHADOW | SWIG_POINTER_OWN)
+
+#define SWIG_POINTER_IMPLICIT_CONV (SWIG_POINTER_DISOWN << 1)
+
+#ifdef __cplusplus
+extern "C" {
+#if 0
+} /* cc-mode */
+#endif
+#endif
+
+/* How to access Py_None */
+#if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+# ifndef SWIG_PYTHON_NO_BUILD_NONE
+# ifndef SWIG_PYTHON_BUILD_NONE
+# define SWIG_PYTHON_BUILD_NONE
+# endif
+# endif
+#endif
+
+#ifdef SWIG_PYTHON_BUILD_NONE
+# ifdef Py_None
+# undef Py_None
+# define Py_None SWIG_Py_None()
+# endif
+SWIGRUNTIMEINLINE PyObject *
+_SWIG_Py_None(void)
+{
+ PyObject *none = Py_BuildValue((char*)"");
+ Py_DECREF(none);
+ return none;
+}
+SWIGRUNTIME PyObject *
+SWIG_Py_None(void)
+{
+ static PyObject *SWIG_STATIC_POINTER(none) = _SWIG_Py_None();
+ return none;
+}
+#endif
+
+/* The python void return value */
+
+SWIGRUNTIMEINLINE PyObject *
+SWIG_Py_Void(void)
+{
+ PyObject *none = Py_None;
+ Py_INCREF(none);
+ return none;
+}
+
+/* PySwigClientData */
+
+typedef struct {
+ PyObject *klass;
+ PyObject *newraw;
+ PyObject *newargs;
+ PyObject *destroy;
+ int delargs;
+ int implicitconv;
+} PySwigClientData;
+
+SWIGRUNTIMEINLINE int
+SWIG_Python_CheckImplicit(swig_type_info *ty)
+{
+ PySwigClientData *data = (PySwigClientData *)ty->clientdata;
+ return data ? data->implicitconv : 0;
+}
+
+SWIGRUNTIMEINLINE PyObject *
+SWIG_Python_ExceptionType(swig_type_info *desc) {
+ PySwigClientData *data = desc ? (PySwigClientData *) desc->clientdata : 0;
+ PyObject *klass = data ? data->klass : 0;
+ return (klass ? klass : PyExc_RuntimeError);
+}
+
+
+SWIGRUNTIME PySwigClientData *
+PySwigClientData_New(PyObject* obj)
+{
+ if (!obj) {
+ return 0;
+ } else {
+ PySwigClientData *data = (PySwigClientData *)malloc(sizeof(PySwigClientData));
+ /* the klass element */
+ data->klass = obj;
+ Py_INCREF(data->klass);
+ /* the newraw method and newargs arguments used to create a new raw instance */
+ if (PyClass_Check(obj)) {
+ data->newraw = 0;
+ data->newargs = obj;
+ Py_INCREF(obj);
+ } else {
+#if (PY_VERSION_HEX < 0x02020000)
+ data->newraw = 0;
+#else
+ data->newraw = PyObject_GetAttrString(data->klass, (char *)"__new__");
+#endif
+ if (data->newraw) {
+ Py_INCREF(data->newraw);
+ data->newargs = PyTuple_New(1);
+ PyTuple_SetItem(data->newargs, 0, obj);
+ } else {
+ data->newargs = obj;
+ }
+ Py_INCREF(data->newargs);
+ }
+ /* the destroy method, aka as the C++ delete method */
+ data->destroy = PyObject_GetAttrString(data->klass, (char *)"__swig_destroy__");
+ if (PyErr_Occurred()) {
+ PyErr_Clear();
+ data->destroy = 0;
+ }
+ if (data->destroy) {
+ int flags;
+ Py_INCREF(data->destroy);
+ flags = PyCFunction_GET_FLAGS(data->destroy);
+#ifdef METH_O
+ data->delargs = !(flags & (METH_O));
+#else
+ data->delargs = 0;
+#endif
+ } else {
+ data->delargs = 0;
+ }
+ data->implicitconv = 0;
+ return data;
+ }
+}
+
+SWIGRUNTIME void
+PySwigClientData_Del(PySwigClientData* data)
+{
+ Py_XDECREF(data->newraw);
+ Py_XDECREF(data->newargs);
+ Py_XDECREF(data->destroy);
+}
+
+/* =============== PySwigObject =====================*/
+
+typedef struct {
+ PyObject_HEAD
+ void *ptr;
+ swig_type_info *ty;
+ int own;
+ PyObject *next;
+} PySwigObject;
+
+SWIGRUNTIME PyObject *
+PySwigObject_long(PySwigObject *v)
+{
+ return PyLong_FromVoidPtr(v->ptr);
+}
+
+SWIGRUNTIME PyObject *
+PySwigObject_format(const char* fmt, PySwigObject *v)
+{
+ PyObject *res = NULL;
+ PyObject *args = PyTuple_New(1);
+ if (args) {
+ if (PyTuple_SetItem(args, 0, PySwigObject_long(v)) == 0) {
+ PyObject *ofmt = PyString_FromString(fmt);
+ if (ofmt) {
+ res = PyString_Format(ofmt,args);
+ Py_DECREF(ofmt);
+ }
+ Py_DECREF(args);
+ }
+ }
+ return res;
+}
+
+SWIGRUNTIME PyObject *
+PySwigObject_oct(PySwigObject *v)
+{
+ return PySwigObject_format("%o",v);
+}
+
+SWIGRUNTIME PyObject *
+PySwigObject_hex(PySwigObject *v)
+{
+ return PySwigObject_format("%x",v);
+}
+
+SWIGRUNTIME PyObject *
+#ifdef METH_NOARGS
+PySwigObject_repr(PySwigObject *v)
+#else
+PySwigObject_repr(PySwigObject *v, PyObject *args)
+#endif
+{
+ const char *name = SWIG_TypePrettyName(v->ty);
+ PyObject *hex = PySwigObject_hex(v);
+ PyObject *repr = PyString_FromFormat("<Swig Object of type '%s' at 0x%s>", name, PyString_AsString(hex));
+ Py_DECREF(hex);
+ if (v->next) {
+#ifdef METH_NOARGS
+ PyObject *nrep = PySwigObject_repr((PySwigObject *)v->next);
+#else
+ PyObject *nrep = PySwigObject_repr((PySwigObject *)v->next, args);
+#endif
+ PyString_ConcatAndDel(&repr,nrep);
+ }
+ return repr;
+}
+
+SWIGRUNTIME int
+PySwigObject_print(PySwigObject *v, FILE *fp, int SWIGUNUSEDPARM(flags))
+{
+#ifdef METH_NOARGS
+ PyObject *repr = PySwigObject_repr(v);
+#else
+ PyObject *repr = PySwigObject_repr(v, NULL);
+#endif
+ if (repr) {
+ fputs(PyString_AsString(repr), fp);
+ Py_DECREF(repr);
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+SWIGRUNTIME PyObject *
+PySwigObject_str(PySwigObject *v)
+{
+ char result[SWIG_BUFFER_SIZE];
+ return SWIG_PackVoidPtr(result, v->ptr, v->ty->name, sizeof(result)) ?
+ PyString_FromString(result) : 0;
+}
+
+SWIGRUNTIME int
+PySwigObject_compare(PySwigObject *v, PySwigObject *w)
+{
+ void *i = v->ptr;
+ void *j = w->ptr;
+ return (i < j) ? -1 : ((i > j) ? 1 : 0);
+}
+
+SWIGRUNTIME PyTypeObject* _PySwigObject_type(void);
+
+SWIGRUNTIME PyTypeObject*
+PySwigObject_type(void) {
+ static PyTypeObject *SWIG_STATIC_POINTER(type) = _PySwigObject_type();
+ return type;
+}
+
+SWIGRUNTIMEINLINE int
+PySwigObject_Check(PyObject *op) {
+ return ((op)->ob_type == PySwigObject_type())
+ || (strcmp((op)->ob_type->tp_name,"PySwigObject") == 0);
+}
+
+SWIGRUNTIME PyObject *
+PySwigObject_New(void *ptr, swig_type_info *ty, int own);
+
+SWIGRUNTIME void
+PySwigObject_dealloc(PyObject *v)
+{
+ PySwigObject *sobj = (PySwigObject *) v;
+ PyObject *next = sobj->next;
+ if (sobj->own == SWIG_POINTER_OWN) {
+ swig_type_info *ty = sobj->ty;
+ PySwigClientData *data = ty ? (PySwigClientData *) ty->clientdata : 0;
+ PyObject *destroy = data ? data->destroy : 0;
+ if (destroy) {
+ /* destroy is always a VARARGS method */
+ PyObject *res;
+ if (data->delargs) {
+ /* we need to create a temporal object to carry the destroy operation */
+ PyObject *tmp = PySwigObject_New(sobj->ptr, ty, 0);
+ res = SWIG_Python_CallFunctor(destroy, tmp);
+ Py_DECREF(tmp);
+ } else {
+ PyCFunction meth = PyCFunction_GET_FUNCTION(destroy);
+ PyObject *mself = PyCFunction_GET_SELF(destroy);
+ res = ((*meth)(mself, v));
+ }
+ Py_XDECREF(res);
+ }
+#if !defined(SWIG_PYTHON_SILENT_MEMLEAK)
+ else {
+ const char *name = SWIG_TypePrettyName(ty);
+ printf("swig/python detected a memory leak of type '%s', no destructor found.\n", (name ? name : "unknown"));
+ }
+#endif
+ }
+ Py_XDECREF(next);
+ PyObject_DEL(v);
+}
+
+SWIGRUNTIME PyObject*
+PySwigObject_append(PyObject* v, PyObject* next)
+{
+ PySwigObject *sobj = (PySwigObject *) v;
+#ifndef METH_O
+ PyObject *tmp = 0;
+ if (!PyArg_ParseTuple(next,(char *)"O:append", &tmp)) return NULL;
+ next = tmp;
+#endif
+ if (!PySwigObject_Check(next)) {
+ return NULL;
+ }
+ sobj->next = next;
+ Py_INCREF(next);
+ return SWIG_Py_Void();
+}
+
+SWIGRUNTIME PyObject*
+#ifdef METH_NOARGS
+PySwigObject_next(PyObject* v)
+#else
+PySwigObject_next(PyObject* v, PyObject *SWIGUNUSEDPARM(args))
+#endif
+{
+ PySwigObject *sobj = (PySwigObject *) v;
+ if (sobj->next) {
+ Py_INCREF(sobj->next);
+ return sobj->next;
+ } else {
+ return SWIG_Py_Void();
+ }
+}
+
+SWIGINTERN PyObject*
+#ifdef METH_NOARGS
+PySwigObject_disown(PyObject *v)
+#else
+PySwigObject_disown(PyObject* v, PyObject *SWIGUNUSEDPARM(args))
+#endif
+{
+ PySwigObject *sobj = (PySwigObject *)v;
+ sobj->own = 0;
+ return SWIG_Py_Void();
+}
+
+SWIGINTERN PyObject*
+#ifdef METH_NOARGS
+PySwigObject_acquire(PyObject *v)
+#else
+PySwigObject_acquire(PyObject* v, PyObject *SWIGUNUSEDPARM(args))
+#endif
+{
+ PySwigObject *sobj = (PySwigObject *)v;
+ sobj->own = SWIG_POINTER_OWN;
+ return SWIG_Py_Void();
+}
+
+SWIGINTERN PyObject*
+PySwigObject_own(PyObject *v, PyObject *args)
+{
+ PyObject *val = 0;
+#if (PY_VERSION_HEX < 0x02020000)
+ if (!PyArg_ParseTuple(args,(char *)"|O:own",&val))
+#else
+ if (!PyArg_UnpackTuple(args, (char *)"own", 0, 1, &val))
+#endif
+ {
+ return NULL;
+ }
+ else
+ {
+ PySwigObject *sobj = (PySwigObject *)v;
+ PyObject *obj = PyBool_FromLong(sobj->own);
+ if (val) {
+#ifdef METH_NOARGS
+ if (PyObject_IsTrue(val)) {
+ PySwigObject_acquire(v);
+ } else {
+ PySwigObject_disown(v);
+ }
+#else
+ if (PyObject_IsTrue(val)) {
+ PySwigObject_acquire(v,args);
+ } else {
+ PySwigObject_disown(v,args);
+ }
+#endif
+ }
+ return obj;
+ }
+}
+
+#ifdef METH_O
+static PyMethodDef
+swigobject_methods[] = {
+ {(char *)"disown", (PyCFunction)PySwigObject_disown, METH_NOARGS, (char *)"releases ownership of the pointer"},
+ {(char *)"acquire", (PyCFunction)PySwigObject_acquire, METH_NOARGS, (char *)"aquires ownership of the pointer"},
+ {(char *)"own", (PyCFunction)PySwigObject_own, METH_VARARGS, (char *)"returns/sets ownership of the pointer"},
+ {(char *)"append", (PyCFunction)PySwigObject_append, METH_O, (char *)"appends another 'this' object"},
+ {(char *)"next", (PyCFunction)PySwigObject_next, METH_NOARGS, (char *)"returns the next 'this' object"},
+ {(char *)"__repr__",(PyCFunction)PySwigObject_repr, METH_NOARGS, (char *)"returns object representation"},
+ {0, 0, 0, 0}
+};
+#else
+static PyMethodDef
+swigobject_methods[] = {
+ {(char *)"disown", (PyCFunction)PySwigObject_disown, METH_VARARGS, (char *)"releases ownership of the pointer"},
+ {(char *)"acquire", (PyCFunction)PySwigObject_acquire, METH_VARARGS, (char *)"aquires ownership of the pointer"},
+ {(char *)"own", (PyCFunction)PySwigObject_own, METH_VARARGS, (char *)"returns/sets ownership of the pointer"},
+ {(char *)"append", (PyCFunction)PySwigObject_append, METH_VARARGS, (char *)"appends another 'this' object"},
+ {(char *)"next", (PyCFunction)PySwigObject_next, METH_VARARGS, (char *)"returns the next 'this' object"},
+ {(char *)"__repr__",(PyCFunction)PySwigObject_repr, METH_VARARGS, (char *)"returns object representation"},
+ {0, 0, 0, 0}
+};
+#endif
+
+#if PY_VERSION_HEX < 0x02020000
+SWIGINTERN PyObject *
+PySwigObject_getattr(PySwigObject *sobj,char *name)
+{
+ return Py_FindMethod(swigobject_methods, (PyObject *)sobj, name);
+}
+#endif
+
+SWIGRUNTIME PyTypeObject*
+_PySwigObject_type(void) {
+ static char swigobject_doc[] = "Swig object carries a C/C++ instance pointer";
+
+ static PyNumberMethods PySwigObject_as_number = {
+ (binaryfunc)0, /*nb_add*/
+ (binaryfunc)0, /*nb_subtract*/
+ (binaryfunc)0, /*nb_multiply*/
+ (binaryfunc)0, /*nb_divide*/
+ (binaryfunc)0, /*nb_remainder*/
+ (binaryfunc)0, /*nb_divmod*/
+ (ternaryfunc)0,/*nb_power*/
+ (unaryfunc)0, /*nb_negative*/
+ (unaryfunc)0, /*nb_positive*/
+ (unaryfunc)0, /*nb_absolute*/
+ (inquiry)0, /*nb_nonzero*/
+ 0, /*nb_invert*/
+ 0, /*nb_lshift*/
+ 0, /*nb_rshift*/
+ 0, /*nb_and*/
+ 0, /*nb_xor*/
+ 0, /*nb_or*/
+ (coercion)0, /*nb_coerce*/
+ (unaryfunc)PySwigObject_long, /*nb_int*/
+ (unaryfunc)PySwigObject_long, /*nb_long*/
+ (unaryfunc)0, /*nb_float*/
+ (unaryfunc)PySwigObject_oct, /*nb_oct*/
+ (unaryfunc)PySwigObject_hex, /*nb_hex*/
+#if PY_VERSION_HEX >= 0x02050000 /* 2.5.0 */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_index */
+#elif PY_VERSION_HEX >= 0x02020000 /* 2.2.0 */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_inplace_true_divide */
+#elif PY_VERSION_HEX >= 0x02000000 /* 2.0.0 */
+ 0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_inplace_or */
+#endif
+ };
+
+ static PyTypeObject pyswigobject_type;
+ static int type_init = 0;
+ if (!type_init) {
+ const PyTypeObject tmp
+ = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ (char *)"PySwigObject", /* tp_name */
+ sizeof(PySwigObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)PySwigObject_dealloc, /* tp_dealloc */
+ (printfunc)PySwigObject_print, /* tp_print */
+#if PY_VERSION_HEX < 0x02020000
+ (getattrfunc)PySwigObject_getattr, /* tp_getattr */
+#else
+ (getattrfunc)0, /* tp_getattr */
+#endif
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)PySwigObject_compare, /* tp_compare */
+ (reprfunc)PySwigObject_repr, /* tp_repr */
+ &PySwigObject_as_number, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)PySwigObject_str, /* tp_str */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ swigobject_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+#if PY_VERSION_HEX >= 0x02020000
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ swigobject_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+#endif
+#if PY_VERSION_HEX >= 0x02030000
+ 0, /* tp_del */
+#endif
+#ifdef COUNT_ALLOCS
+ 0,0,0,0 /* tp_alloc -> tp_next */
+#endif
+ };
+ pyswigobject_type = tmp;
+ pyswigobject_type.ob_type = &PyType_Type;
+ type_init = 1;
+ }
+ return &pyswigobject_type;
+}
+
+SWIGRUNTIME PyObject *
+PySwigObject_New(void *ptr, swig_type_info *ty, int own)
+{
+ PySwigObject *sobj = PyObject_NEW(PySwigObject, PySwigObject_type());
+ if (sobj) {
+ sobj->ptr = ptr;
+ sobj->ty = ty;
+ sobj->own = own;
+ sobj->next = 0;
+ }
+ return (PyObject *)sobj;
+}
+
+/* -----------------------------------------------------------------------------
+ * Implements a simple Swig Packed type, and use it instead of string
+ * ----------------------------------------------------------------------------- */
+
+typedef struct {
+ PyObject_HEAD
+ void *pack;
+ swig_type_info *ty;
+ size_t size;
+} PySwigPacked;
+
+SWIGRUNTIME int
+PySwigPacked_print(PySwigPacked *v, FILE *fp, int SWIGUNUSEDPARM(flags))
+{
+ char result[SWIG_BUFFER_SIZE];
+ fputs("<Swig Packed ", fp);
+ if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))) {
+ fputs("at ", fp);
+ fputs(result, fp);
+ }
+ fputs(v->ty->name,fp);
+ fputs(">", fp);
+ return 0;
+}
+
+SWIGRUNTIME PyObject *
+PySwigPacked_repr(PySwigPacked *v)
+{
+ char result[SWIG_BUFFER_SIZE];
+ if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))) {
+ return PyString_FromFormat("<Swig Packed at %s%s>", result, v->ty->name);
+ } else {
+ return PyString_FromFormat("<Swig Packed %s>", v->ty->name);
+ }
+}
+
+SWIGRUNTIME PyObject *
+PySwigPacked_str(PySwigPacked *v)
+{
+ char result[SWIG_BUFFER_SIZE];
+ if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))){
+ return PyString_FromFormat("%s%s", result, v->ty->name);
+ } else {
+ return PyString_FromString(v->ty->name);
+ }
+}
+
+SWIGRUNTIME int
+PySwigPacked_compare(PySwigPacked *v, PySwigPacked *w)
+{
+ size_t i = v->size;
+ size_t j = w->size;
+ int s = (i < j) ? -1 : ((i > j) ? 1 : 0);
+ return s ? s : strncmp((char *)v->pack, (char *)w->pack, 2*v->size);
+}
+
+SWIGRUNTIME PyTypeObject* _PySwigPacked_type(void);
+
+SWIGRUNTIME PyTypeObject*
+PySwigPacked_type(void) {
+ static PyTypeObject *SWIG_STATIC_POINTER(type) = _PySwigPacked_type();
+ return type;
+}
+
+SWIGRUNTIMEINLINE int
+PySwigPacked_Check(PyObject *op) {
+ return ((op)->ob_type == _PySwigPacked_type())
+ || (strcmp((op)->ob_type->tp_name,"PySwigPacked") == 0);
+}
+
+SWIGRUNTIME void
+PySwigPacked_dealloc(PyObject *v)
+{
+ if (PySwigPacked_Check(v)) {
+ PySwigPacked *sobj = (PySwigPacked *) v;
+ free(sobj->pack);
+ }
+ PyObject_DEL(v);
+}
+
+SWIGRUNTIME PyTypeObject*
+_PySwigPacked_type(void) {
+ static char swigpacked_doc[] = "Swig object carries a C/C++ instance pointer";
+ static PyTypeObject pyswigpacked_type;
+ static int type_init = 0;
+ if (!type_init) {
+ const PyTypeObject tmp
+ = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ (char *)"PySwigPacked", /* tp_name */
+ sizeof(PySwigPacked), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)PySwigPacked_dealloc, /* tp_dealloc */
+ (printfunc)PySwigPacked_print, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)PySwigPacked_compare, /* tp_compare */
+ (reprfunc)PySwigPacked_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)PySwigPacked_str, /* tp_str */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ swigpacked_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+#if PY_VERSION_HEX >= 0x02020000
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+#endif
+#if PY_VERSION_HEX >= 0x02030000
+ 0, /* tp_del */
+#endif
+#ifdef COUNT_ALLOCS
+ 0,0,0,0 /* tp_alloc -> tp_next */
+#endif
+ };
+ pyswigpacked_type = tmp;
+ pyswigpacked_type.ob_type = &PyType_Type;
+ type_init = 1;
+ }
+ return &pyswigpacked_type;
+}
+
+SWIGRUNTIME PyObject *
+PySwigPacked_New(void *ptr, size_t size, swig_type_info *ty)
+{
+ PySwigPacked *sobj = PyObject_NEW(PySwigPacked, PySwigPacked_type());
+ if (sobj) {
+ void *pack = malloc(size);
+ if (pack) {
+ memcpy(pack, ptr, size);
+ sobj->pack = pack;
+ sobj->ty = ty;
+ sobj->size = size;
+ } else {
+ PyObject_DEL((PyObject *) sobj);
+ sobj = 0;
+ }
+ }
+ return (PyObject *) sobj;
+}
+
+SWIGRUNTIME swig_type_info *
+PySwigPacked_UnpackData(PyObject *obj, void *ptr, size_t size)
+{
+ if (PySwigPacked_Check(obj)) {
+ PySwigPacked *sobj = (PySwigPacked *)obj;
+ if (sobj->size != size) return 0;
+ memcpy(ptr, sobj->pack, size);
+ return sobj->ty;
+ } else {
+ return 0;
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * pointers/data manipulation
+ * ----------------------------------------------------------------------------- */
+
+SWIGRUNTIMEINLINE PyObject *
+_SWIG_This(void)
+{
+ return PyString_FromString("this");
+}
+
+SWIGRUNTIME PyObject *
+SWIG_This(void)
+{
+ static PyObject *SWIG_STATIC_POINTER(swig_this) = _SWIG_This();
+ return swig_this;
+}
+
+/* #define SWIG_PYTHON_SLOW_GETSET_THIS */
+
+SWIGRUNTIME PySwigObject *
+SWIG_Python_GetSwigThis(PyObject *pyobj)
+{
+ if (PySwigObject_Check(pyobj)) {
+ return (PySwigObject *) pyobj;
+ } else {
+ PyObject *obj = 0;
+#if (!defined(SWIG_PYTHON_SLOW_GETSET_THIS) && (PY_VERSION_HEX >= 0x02030000))
+ if (PyInstance_Check(pyobj)) {
+ obj = _PyInstance_Lookup(pyobj, SWIG_This());
+ } else {
+ PyObject **dictptr = _PyObject_GetDictPtr(pyobj);
+ if (dictptr != NULL) {
+ PyObject *dict = *dictptr;
+ obj = dict ? PyDict_GetItem(dict, SWIG_This()) : 0;
+ } else {
+#ifdef PyWeakref_CheckProxy
+ if (PyWeakref_CheckProxy(pyobj)) {
+ PyObject *wobj = PyWeakref_GET_OBJECT(pyobj);
+ return wobj ? SWIG_Python_GetSwigThis(wobj) : 0;
+ }
+#endif
+ obj = PyObject_GetAttr(pyobj,SWIG_This());
+ if (obj) {
+ Py_DECREF(obj);
+ } else {
+ if (PyErr_Occurred()) PyErr_Clear();
+ return 0;
+ }
+ }
+ }
+#else
+ obj = PyObject_GetAttr(pyobj,SWIG_This());
+ if (obj) {
+ Py_DECREF(obj);
+ } else {
+ if (PyErr_Occurred()) PyErr_Clear();
+ return 0;
+ }
+#endif
+ if (obj && !PySwigObject_Check(obj)) {
+ /* a PyObject is called 'this', try to get the 'real this'
+ PySwigObject from it */
+ return SWIG_Python_GetSwigThis(obj);
+ }
+ return (PySwigObject *)obj;
+ }
+}
+
+/* Acquire a pointer value */
+
+SWIGRUNTIME int
+SWIG_Python_AcquirePtr(PyObject *obj, int own) {
+ if (own == SWIG_POINTER_OWN) {
+ PySwigObject *sobj = SWIG_Python_GetSwigThis(obj);
+ if (sobj) {
+ int oldown = sobj->own;
+ sobj->own = own;
+ return oldown;
+ }
+ }
+ return 0;
+}
+
+/* Convert a pointer value */
+
+SWIGRUNTIME int
+SWIG_Python_ConvertPtrAndOwn(PyObject *obj, void **ptr, swig_type_info *ty, int flags, int *own) {
+ if (!obj) return SWIG_ERROR;
+ if (obj == Py_None) {
+ if (ptr) *ptr = 0;
+ return SWIG_OK;
+ } else {
+ PySwigObject *sobj = SWIG_Python_GetSwigThis(obj);
+ if (own)
+ *own = 0;
+ while (sobj) {
+ void *vptr = sobj->ptr;
+ if (ty) {
+ swig_type_info *to = sobj->ty;
+ if (to == ty) {
+ /* no type cast needed */
+ if (ptr) *ptr = vptr;
+ break;
+ } else {
+ swig_cast_info *tc = SWIG_TypeCheck(to->name,ty);
+ if (!tc) {
+ sobj = (PySwigObject *)sobj->next;
+ } else {
+ if (ptr) {
+ int newmemory = 0;
+ *ptr = SWIG_TypeCast(tc,vptr,&newmemory);
+ if (newmemory == SWIG_CAST_NEW_MEMORY) {
+ assert(own);
+ if (own)
+ *own = *own | SWIG_CAST_NEW_MEMORY;
+ }
+ }
+ break;
+ }
+ }
+ } else {
+ if (ptr) *ptr = vptr;
+ break;
+ }
+ }
+ if (sobj) {
+ if (own)
+ *own = *own | sobj->own;
+ if (flags & SWIG_POINTER_DISOWN) {
+ sobj->own = 0;
+ }
+ return SWIG_OK;
+ } else {
+ int res = SWIG_ERROR;
+ if (flags & SWIG_POINTER_IMPLICIT_CONV) {
+ PySwigClientData *data = ty ? (PySwigClientData *) ty->clientdata : 0;
+ if (data && !data->implicitconv) {
+ PyObject *klass = data->klass;
+ if (klass) {
+ PyObject *impconv;
+ data->implicitconv = 1; /* avoid recursion and call 'explicit' constructors*/
+ impconv = SWIG_Python_CallFunctor(klass, obj);
+ data->implicitconv = 0;
+ if (PyErr_Occurred()) {
+ PyErr_Clear();
+ impconv = 0;
+ }
+ if (impconv) {
+ PySwigObject *iobj = SWIG_Python_GetSwigThis(impconv);
+ if (iobj) {
+ void *vptr;
+ res = SWIG_Python_ConvertPtrAndOwn((PyObject*)iobj, &vptr, ty, 0, 0);
+ if (SWIG_IsOK(res)) {
+ if (ptr) {
+ *ptr = vptr;
+ /* transfer the ownership to 'ptr' */
+ iobj->own = 0;
+ res = SWIG_AddCast(res);
+ res = SWIG_AddNewMask(res);
+ } else {
+ res = SWIG_AddCast(res);
+ }
+ }
+ }
+ Py_DECREF(impconv);
+ }
+ }
+ }
+ }
+ return res;
+ }
+ }
+}
+
+/* Convert a function ptr value */
+
+SWIGRUNTIME int
+SWIG_Python_ConvertFunctionPtr(PyObject *obj, void **ptr, swig_type_info *ty) {
+ if (!PyCFunction_Check(obj)) {
+ return SWIG_ConvertPtr(obj, ptr, ty, 0);
+ } else {
+ void *vptr = 0;
+
+ /* here we get the method pointer for callbacks */
+ const char *doc = (((PyCFunctionObject *)obj) -> m_ml -> ml_doc);
+ const char *desc = doc ? strstr(doc, "swig_ptr: ") : 0;
+ if (desc) {
+ desc = ty ? SWIG_UnpackVoidPtr(desc + 10, &vptr, ty->name) : 0;
+ if (!desc) return SWIG_ERROR;
+ }
+ if (ty) {
+ swig_cast_info *tc = SWIG_TypeCheck(desc,ty);
+ if (tc) {
+ int newmemory = 0;
+ *ptr = SWIG_TypeCast(tc,vptr,&newmemory);
+ assert(!newmemory); /* newmemory handling not yet implemented */
+ } else {
+ return SWIG_ERROR;
+ }
+ } else {
+ *ptr = vptr;
+ }
+ return SWIG_OK;
+ }
+}
+
+/* Convert a packed value value */
+
+SWIGRUNTIME int
+SWIG_Python_ConvertPacked(PyObject *obj, void *ptr, size_t sz, swig_type_info *ty) {
+ swig_type_info *to = PySwigPacked_UnpackData(obj, ptr, sz);
+ if (!to) return SWIG_ERROR;
+ if (ty) {
+ if (to != ty) {
+ /* check type cast? */
+ swig_cast_info *tc = SWIG_TypeCheck(to->name,ty);
+ if (!tc) return SWIG_ERROR;
+ }
+ }
+ return SWIG_OK;
+}
+
+/* -----------------------------------------------------------------------------
+ * Create a new pointer object
+ * ----------------------------------------------------------------------------- */
+
+/*
+ Create a new instance object, whitout calling __init__, and set the
+ 'this' attribute.
+*/
+
+SWIGRUNTIME PyObject*
+SWIG_Python_NewShadowInstance(PySwigClientData *data, PyObject *swig_this)
+{
+#if (PY_VERSION_HEX >= 0x02020000)
+ PyObject *inst = 0;
+ PyObject *newraw = data->newraw;
+ if (newraw) {
+ inst = PyObject_Call(newraw, data->newargs, NULL);
+ if (inst) {
+#if !defined(SWIG_PYTHON_SLOW_GETSET_THIS)
+ PyObject **dictptr = _PyObject_GetDictPtr(inst);
+ if (dictptr != NULL) {
+ PyObject *dict = *dictptr;
+ if (dict == NULL) {
+ dict = PyDict_New();
+ *dictptr = dict;
+ PyDict_SetItem(dict, SWIG_This(), swig_this);
+ }
+ }
+#else
+ PyObject *key = SWIG_This();
+ PyObject_SetAttr(inst, key, swig_this);
+#endif
+ }
+ } else {
+ PyObject *dict = PyDict_New();
+ PyDict_SetItem(dict, SWIG_This(), swig_this);
+ inst = PyInstance_NewRaw(data->newargs, dict);
+ Py_DECREF(dict);
+ }
+ return inst;
+#else
+#if (PY_VERSION_HEX >= 0x02010000)
+ PyObject *inst;
+ PyObject *dict = PyDict_New();
+ PyDict_SetItem(dict, SWIG_This(), swig_this);
+ inst = PyInstance_NewRaw(data->newargs, dict);
+ Py_DECREF(dict);
+ return (PyObject *) inst;
+#else
+ PyInstanceObject *inst = PyObject_NEW(PyInstanceObject, &PyInstance_Type);
+ if (inst == NULL) {
+ return NULL;
+ }
+ inst->in_class = (PyClassObject *)data->newargs;
+ Py_INCREF(inst->in_class);
+ inst->in_dict = PyDict_New();
+ if (inst->in_dict == NULL) {
+ Py_DECREF(inst);
+ return NULL;
+ }
+#ifdef Py_TPFLAGS_HAVE_WEAKREFS
+ inst->in_weakreflist = NULL;
+#endif
+#ifdef Py_TPFLAGS_GC
+ PyObject_GC_Init(inst);
+#endif
+ PyDict_SetItem(inst->in_dict, SWIG_This(), swig_this);
+ return (PyObject *) inst;
+#endif
+#endif
+}
+
+SWIGRUNTIME void
+SWIG_Python_SetSwigThis(PyObject *inst, PyObject *swig_this)
+{
+ PyObject *dict;
+#if (PY_VERSION_HEX >= 0x02020000) && !defined(SWIG_PYTHON_SLOW_GETSET_THIS)
+ PyObject **dictptr = _PyObject_GetDictPtr(inst);
+ if (dictptr != NULL) {
+ dict = *dictptr;
+ if (dict == NULL) {
+ dict = PyDict_New();
+ *dictptr = dict;
+ }
+ PyDict_SetItem(dict, SWIG_This(), swig_this);
+ return;
+ }
+#endif
+ dict = PyObject_GetAttrString(inst, (char*)"__dict__");
+ PyDict_SetItem(dict, SWIG_This(), swig_this);
+ Py_DECREF(dict);
+}
+
+
+SWIGINTERN PyObject *
+SWIG_Python_InitShadowInstance(PyObject *args) {
+ PyObject *obj[2];
+ if (!SWIG_Python_UnpackTuple(args,(char*)"swiginit", 2, 2, obj)) {
+ return NULL;
+ } else {
+ PySwigObject *sthis = SWIG_Python_GetSwigThis(obj[0]);
+ if (sthis) {
+ PySwigObject_append((PyObject*) sthis, obj[1]);
+ } else {
+ SWIG_Python_SetSwigThis(obj[0], obj[1]);
+ }
+ return SWIG_Py_Void();
+ }
+}
+
+/* Create a new pointer object */
+
+SWIGRUNTIME PyObject *
+SWIG_Python_NewPointerObj(void *ptr, swig_type_info *type, int flags) {
+ if (!ptr) {
+ return SWIG_Py_Void();
+ } else {
+ int own = (flags & SWIG_POINTER_OWN) ? SWIG_POINTER_OWN : 0;
+ PyObject *robj = PySwigObject_New(ptr, type, own);
+ PySwigClientData *clientdata = type ? (PySwigClientData *)(type->clientdata) : 0;
+ if (clientdata && !(flags & SWIG_POINTER_NOSHADOW)) {
+ PyObject *inst = SWIG_Python_NewShadowInstance(clientdata, robj);
+ if (inst) {
+ Py_DECREF(robj);
+ robj = inst;
+ }
+ }
+ return robj;
+ }
+}
+
+/* Create a new packed object */
+
+SWIGRUNTIMEINLINE PyObject *
+SWIG_Python_NewPackedObj(void *ptr, size_t sz, swig_type_info *type) {
+ return ptr ? PySwigPacked_New((void *) ptr, sz, type) : SWIG_Py_Void();
+}
+
+/* -----------------------------------------------------------------------------*
+ * Get type list
+ * -----------------------------------------------------------------------------*/
+
+#ifdef SWIG_LINK_RUNTIME
+void *SWIG_ReturnGlobalTypeList(void *);
+#endif
+
+SWIGRUNTIME swig_module_info *
+SWIG_Python_GetModule(void) {
+ static void *type_pointer = (void *)0;
+ /* first check if module already created */
+ if (!type_pointer) {
+#ifdef SWIG_LINK_RUNTIME
+ type_pointer = SWIG_ReturnGlobalTypeList((void *)0);
+#else
+ type_pointer = PyCObject_Import((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION,
+ (char*)"type_pointer" SWIG_TYPE_TABLE_NAME);
+ if (PyErr_Occurred()) {
+ PyErr_Clear();
+ type_pointer = (void *)0;
+ }
+#endif
+ }
+ return (swig_module_info *) type_pointer;
+}
+
+#if PY_MAJOR_VERSION < 2
+/* PyModule_AddObject function was introduced in Python 2.0. The following function
+ is copied out of Python/modsupport.c in python version 2.3.4 */
+SWIGINTERN int
+PyModule_AddObject(PyObject *m, char *name, PyObject *o)
+{
+ PyObject *dict;
+ if (!PyModule_Check(m)) {
+ PyErr_SetString(PyExc_TypeError,
+ "PyModule_AddObject() needs module as first arg");
+ return SWIG_ERROR;
+ }
+ if (!o) {
+ PyErr_SetString(PyExc_TypeError,
+ "PyModule_AddObject() needs non-NULL value");
+ return SWIG_ERROR;
+ }
+
+ dict = PyModule_GetDict(m);
+ if (dict == NULL) {
+ /* Internal error -- modules must have a dict! */
+ PyErr_Format(PyExc_SystemError, "module '%s' has no __dict__",
+ PyModule_GetName(m));
+ return SWIG_ERROR;
+ }
+ if (PyDict_SetItemString(dict, name, o))
+ return SWIG_ERROR;
+ Py_DECREF(o);
+ return SWIG_OK;
+}
+#endif
+
+SWIGRUNTIME void
+SWIG_Python_DestroyModule(void *vptr)
+{
+ swig_module_info *swig_module = (swig_module_info *) vptr;
+ swig_type_info **types = swig_module->types;
+ size_t i;
+ for (i =0; i < swig_module->size; ++i) {
+ swig_type_info *ty = types[i];
+ if (ty->owndata) {
+ PySwigClientData *data = (PySwigClientData *) ty->clientdata;
+ if (data) PySwigClientData_Del(data);
+ }
+ }
+ Py_DECREF(SWIG_This());
+}
+
+SWIGRUNTIME void
+SWIG_Python_SetModule(swig_module_info *swig_module) {
+ static PyMethodDef swig_empty_runtime_method_table[] = { {NULL, NULL, 0, NULL} };/* Sentinel */
+
+ PyObject *module = Py_InitModule((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION,
+ swig_empty_runtime_method_table);
+ PyObject *pointer = PyCObject_FromVoidPtr((void *) swig_module, SWIG_Python_DestroyModule);
+ if (pointer && module) {
+ PyModule_AddObject(module, (char*)"type_pointer" SWIG_TYPE_TABLE_NAME, pointer);
+ } else {
+ Py_XDECREF(pointer);
+ }
+}
+
+/* The python cached type query */
+SWIGRUNTIME PyObject *
+SWIG_Python_TypeCache(void) {
+ static PyObject *SWIG_STATIC_POINTER(cache) = PyDict_New();
+ return cache;
+}
+
+SWIGRUNTIME swig_type_info *
+SWIG_Python_TypeQuery(const char *type)
+{
+ PyObject *cache = SWIG_Python_TypeCache();
+ PyObject *key = PyString_FromString(type);
+ PyObject *obj = PyDict_GetItem(cache, key);
+ swig_type_info *descriptor;
+ if (obj) {
+ descriptor = (swig_type_info *) PyCObject_AsVoidPtr(obj);
+ } else {
+ swig_module_info *swig_module = SWIG_Python_GetModule();
+ descriptor = SWIG_TypeQueryModule(swig_module, swig_module, type);
+ if (descriptor) {
+ obj = PyCObject_FromVoidPtr(descriptor, NULL);
+ PyDict_SetItem(cache, key, obj);
+ Py_DECREF(obj);
+ }
+ }
+ Py_DECREF(key);
+ return descriptor;
+}
+
+/*
+ For backward compatibility only
+*/
+#define SWIG_POINTER_EXCEPTION 0
+#define SWIG_arg_fail(arg) SWIG_Python_ArgFail(arg)
+#define SWIG_MustGetPtr(p, type, argnum, flags) SWIG_Python_MustGetPtr(p, type, argnum, flags)
+
+SWIGRUNTIME int
+SWIG_Python_AddErrMesg(const char* mesg, int infront)
+{
+ if (PyErr_Occurred()) {
+ PyObject *type = 0;
+ PyObject *value = 0;
+ PyObject *traceback = 0;
+ PyErr_Fetch(&type, &value, &traceback);
+ if (value) {
+ PyObject *old_str = PyObject_Str(value);
+ Py_XINCREF(type);
+ PyErr_Clear();
+ if (infront) {
+ PyErr_Format(type, "%s %s", mesg, PyString_AsString(old_str));
+ } else {
+ PyErr_Format(type, "%s %s", PyString_AsString(old_str), mesg);
+ }
+ Py_DECREF(old_str);
+ }
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+SWIGRUNTIME int
+SWIG_Python_ArgFail(int argnum)
+{
+ if (PyErr_Occurred()) {
+ /* add information about failing argument */
+ char mesg[256];
+ PyOS_snprintf(mesg, sizeof(mesg), "argument number %d:", argnum);
+ return SWIG_Python_AddErrMesg(mesg, 1);
+ } else {
+ return 0;
+ }
+}
+
+SWIGRUNTIMEINLINE const char *
+PySwigObject_GetDesc(PyObject *self)
+{
+ PySwigObject *v = (PySwigObject *)self;
+ swig_type_info *ty = v ? v->ty : 0;
+ return ty ? ty->str : (char*)"";
+}
+
+SWIGRUNTIME void
+SWIG_Python_TypeError(const char *type, PyObject *obj)
+{
+ if (type) {
+#if defined(SWIG_COBJECT_TYPES)
+ if (obj && PySwigObject_Check(obj)) {
+ const char *otype = (const char *) PySwigObject_GetDesc(obj);
+ if (otype) {
+ PyErr_Format(PyExc_TypeError, "a '%s' is expected, 'PySwigObject(%s)' is received",
+ type, otype);
+ return;
+ }
+ } else
+#endif
+ {
+ const char *otype = (obj ? obj->ob_type->tp_name : 0);
+ if (otype) {
+ PyObject *str = PyObject_Str(obj);
+ const char *cstr = str ? PyString_AsString(str) : 0;
+ if (cstr) {
+ PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s(%s)' is received",
+ type, otype, cstr);
+ } else {
+ PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s' is received",
+ type, otype);
+ }
+ Py_XDECREF(str);
+ return;
+ }
+ }
+ PyErr_Format(PyExc_TypeError, "a '%s' is expected", type);
+ } else {
+ PyErr_Format(PyExc_TypeError, "unexpected type is received");
+ }
+}
+
+
+/* Convert a pointer value, signal an exception on a type mismatch */
+SWIGRUNTIME void *
+SWIG_Python_MustGetPtr(PyObject *obj, swig_type_info *ty, int argnum, int flags) {
+ void *result;
+ if (SWIG_Python_ConvertPtr(obj, &result, ty, flags) == -1) {
+ PyErr_Clear();
+ if (flags & SWIG_POINTER_EXCEPTION) {
+ SWIG_Python_TypeError(SWIG_TypePrettyName(ty), obj);
+ SWIG_Python_ArgFail(argnum);
+ }
+ }
+ return result;
+}
+
+
+#ifdef __cplusplus
+#if 0
+{ /* cc-mode */
+#endif
+}
+#endif
+
+
+
+#define SWIG_exception_fail(code, msg) do { SWIG_Error(code, msg); SWIG_fail; } while(0)
+
+#define SWIG_contract_assert(expr, msg) if (!(expr)) { SWIG_Error(SWIG_RuntimeError, msg); SWIG_fail; } else
+
+
+
+void push_object(PyObject **stack, PyObject *o)
+{
+ if ((!*stack) || (*stack == Py_None)) {
+ *stack = o;
+ } else {
+ PyObject *o2, *o3;
+ if (!PyTuple_Check(*stack)) {
+ o2 = *stack;
+ *stack = PyTuple_New(1);
+ PyTuple_SetItem(*stack,0,o2);
+ }
+ o3 = PyTuple_New(1);
+ PyTuple_SetItem(o3,0,o);
+ o2 = *stack;
+ *stack = PySequence_Concat(o2,o3);
+ Py_DECREF(o2);
+ Py_DECREF(o3);
+ }
+}
+
+
+/* -------- TYPES TABLE (BEGIN) -------- */
+
+#define SWIGTYPE_p_BSTR swig_types[0]
+#define SWIGTYPE_p_IEnumWbemClassObject swig_types[1]
+#define SWIGTYPE_p_IUnknown swig_types[2]
+#define SWIGTYPE_p_IWbemContext swig_types[3]
+#define SWIGTYPE_p_IWbemServices swig_types[4]
+#define SWIGTYPE_p_TALLOC_CTX swig_types[5]
+#define SWIGTYPE_p_char swig_types[6]
+#define SWIGTYPE_p_com_context swig_types[7]
+#define SWIGTYPE_p_int swig_types[8]
+#define SWIGTYPE_p_long_long swig_types[9]
+#define SWIGTYPE_p_p_IEnumWbemClassObject swig_types[10]
+#define SWIGTYPE_p_p_IWbemServices swig_types[11]
+#define SWIGTYPE_p_p_WbemClassObject swig_types[12]
+#define SWIGTYPE_p_short swig_types[13]
+#define SWIGTYPE_p_signed_char swig_types[14]
+#define SWIGTYPE_p_unsigned_char swig_types[15]
+#define SWIGTYPE_p_unsigned_int swig_types[16]
+#define SWIGTYPE_p_unsigned_long_long swig_types[17]
+#define SWIGTYPE_p_unsigned_short swig_types[18]
+static swig_type_info *swig_types[20];
+static swig_module_info swig_module = {swig_types, 19, 0, 0, 0, 0};
+#define SWIG_TypeQuery(name) SWIG_TypeQueryModule(&swig_module, &swig_module, name)
+#define SWIG_MangledTypeQuery(name) SWIG_MangledTypeQueryModule(&swig_module, &swig_module, name)
+
+/* -------- TYPES TABLE (END) -------- */
+
+#if (PY_VERSION_HEX <= 0x02000000)
+# if !defined(SWIG_PYTHON_CLASSIC)
+# error "This python version requires swig to be run with the '-classic' option"
+# endif
+#endif
+#if (PY_VERSION_HEX <= 0x02020000)
+# error "This python version requires swig to be run with the '-nomodern' option"
+#endif
+#if (PY_VERSION_HEX <= 0x02020000)
+# error "This python version requires swig to be run with the '-nomodernargs' option"
+#endif
+#ifndef METH_O
+# error "This python version requires swig to be run with the '-nofastunpack' option"
+#endif
+#ifdef SWIG_TypeQuery
+# undef SWIG_TypeQuery
+#endif
+#define SWIG_TypeQuery SWIG_Python_TypeQuery
+
+/*-----------------------------------------------
+ @(target):= _wmi.so
+ ------------------------------------------------*/
+#define SWIG_init init_wmi
+
+#define SWIG_name "_wmi"
+
+#define SWIGVERSION 0x010336
+#define SWIG_VERSION SWIGVERSION
+
+
+#define SWIG_as_voidptr(a) (void *)((const void *)(a))
+#define SWIG_as_voidptrptr(a) ((void)SWIG_as_voidptr(*a),(void**)(a))
+
+
+#include "libcli/util/pyerrors.h"
+
+
+#include "includes.h"
+#include "librpc/gen_ndr/misc.h"
+#include "librpc/rpc/dcerpc.h"
+#include "lib/com/dcom/dcom.h"
+#include "librpc/gen_ndr/com_dcom.h"
+#include "lib/wmi/wmi.h"
+
+
+WERROR WBEM_ConnectServer(struct com_context *ctx, const char *server, const char *nspace, const char *user, const char *password,
+ const char *locale, uint32_t flags, const char *authority, struct IWbemContext* wbem_ctx, struct IWbemServices** services);
+WERROR IEnumWbemClassObject_SmartNext(struct IEnumWbemClassObject *d, TALLOC_CTX *mem_ctx, int32_t lTimeout,uint32_t uCount,
+ struct WbemClassObject **apObjects, uint32_t *puReturned);
+
+static PyObject *PyObject_FromCVAR(uint32_t cimtype, union CIMVAR *cvar);
+static PyObject *PySWbemObject_FromWbemClassObject(struct WbemClassObject *wco);
+
+static struct com_context *com_ctx;
+static PyObject *ComError;
+static PyObject *mod_win32_client;
+static PyObject *mod_pywintypes;
+
+typedef struct IUnknown IUnknown;
+typedef struct IWbemServices IWbemServices;
+typedef struct IWbemClassObject IWbemClassObject;
+typedef struct IEnumWbemClassObject IEnumWbemClassObject;
+
+
+SWIGINTERN swig_type_info*
+SWIG_pchar_descriptor(void)
+{
+ static int init = 0;
+ static swig_type_info* info = 0;
+ if (!init) {
+ info = SWIG_TypeQuery("_p_char");
+ init = 1;
+ }
+ return info;
+}
+
+
+SWIGINTERN int
+SWIG_AsCharPtrAndSize(PyObject *obj, char** cptr, size_t* psize, int *alloc)
+{
+ if (PyString_Check(obj)) {
+ char *cstr; Py_ssize_t len;
+ PyString_AsStringAndSize(obj, &cstr, &len);
+ if (cptr) {
+ if (alloc) {
+ /*
+ In python the user should not be able to modify the inner
+ string representation. To warranty that, if you define
+ SWIG_PYTHON_SAFE_CSTRINGS, a new/copy of the python string
+ buffer is always returned.
+
+ The default behavior is just to return the pointer value,
+ so, be careful.
+ */
+#if defined(SWIG_PYTHON_SAFE_CSTRINGS)
+ if (*alloc != SWIG_OLDOBJ)
+#else
+ if (*alloc == SWIG_NEWOBJ)
+#endif
+ {
+ *cptr = (char *)memcpy((char *)malloc((len + 1)*sizeof(char)), cstr, sizeof(char)*(len + 1));
+ *alloc = SWIG_NEWOBJ;
+ }
+ else {
+ *cptr = cstr;
+ *alloc = SWIG_OLDOBJ;
+ }
+ } else {
+ *cptr = PyString_AsString(obj);
+ }
+ }
+ if (psize) *psize = len + 1;
+ return SWIG_OK;
+ } else {
+ swig_type_info* pchar_descriptor = SWIG_pchar_descriptor();
+ if (pchar_descriptor) {
+ void* vptr = 0;
+ if (SWIG_ConvertPtr(obj, &vptr, pchar_descriptor, 0) == SWIG_OK) {
+ if (cptr) *cptr = (char *) vptr;
+ if (psize) *psize = vptr ? (strlen((char *)vptr) + 1) : 0;
+ if (alloc) *alloc = SWIG_OLDOBJ;
+ return SWIG_OK;
+ }
+ }
+ }
+ return SWIG_TypeError;
+}
+
+
+
+
+
+#include <limits.h>
+#if !defined(SWIG_NO_LLONG_MAX)
+# if !defined(LLONG_MAX) && defined(__GNUC__) && defined (__LONG_LONG_MAX__)
+# define LLONG_MAX __LONG_LONG_MAX__
+# define LLONG_MIN (-LLONG_MAX - 1LL)
+# define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL)
+# endif
+#endif
+
+
+SWIGINTERN int
+SWIG_AsVal_double (PyObject *obj, double *val)
+{
+ int res = SWIG_TypeError;
+ if (PyFloat_Check(obj)) {
+ if (val) *val = PyFloat_AsDouble(obj);
+ return SWIG_OK;
+ } else if (PyLong_Check(obj)) {
+ if (val) *val = PyLong_AsLong(obj);
+ return SWIG_OK;
+ } else if (PyLong_Check(obj)) {
+ double v = PyLong_AsDouble(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = v;
+ return SWIG_OK;
+ } else {
+ PyErr_Clear();
+ }
+ }
+#ifdef SWIG_PYTHON_CAST_MODE
+ {
+ int dispatch = 0;
+ double d = PyFloat_AsDouble(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = d;
+ return SWIG_AddCast(SWIG_OK);
+ } else {
+ PyErr_Clear();
+ }
+ if (!dispatch) {
+ long v = PyLong_AsLong(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = v;
+ return SWIG_AddCast(SWIG_AddCast(SWIG_OK));
+ } else {
+ PyErr_Clear();
+ }
+ }
+ }
+#endif
+ return res;
+}
+
+
+#include <float.h>
+
+
+#include <math.h>
+
+
+SWIGINTERNINLINE int
+SWIG_CanCastAsInteger(double *d, double min, double max) {
+ double x = *d;
+ if ((min <= x && x <= max)) {
+ double fx = floor(x);
+ double cx = ceil(x);
+ double rd = ((x - fx) < 0.5) ? fx : cx; /* simple rint */
+ if ((errno == EDOM) || (errno == ERANGE)) {
+ errno = 0;
+ } else {
+ double summ, reps, diff;
+ if (rd < x) {
+ diff = x - rd;
+ } else if (rd > x) {
+ diff = rd - x;
+ } else {
+ return 1;
+ }
+ summ = rd + x;
+ reps = diff/summ;
+ if (reps < 8*DBL_EPSILON) {
+ *d = rd;
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+
+SWIGINTERN int
+SWIG_AsVal_unsigned_SS_long (PyObject *obj, unsigned long *val)
+{
+ if (PyLong_Check(obj)) {
+ long v = PyLong_AsLong(obj);
+ if (v >= 0) {
+ if (val) *val = v;
+ return SWIG_OK;
+ } else {
+ return SWIG_OverflowError;
+ }
+ } else if (PyLong_Check(obj)) {
+ unsigned long v = PyLong_AsUnsignedLong(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = v;
+ return SWIG_OK;
+ } else {
+ PyErr_Clear();
+ }
+ }
+#ifdef SWIG_PYTHON_CAST_MODE
+ {
+ int dispatch = 0;
+ unsigned long v = PyLong_AsUnsignedLong(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = v;
+ return SWIG_AddCast(SWIG_OK);
+ } else {
+ PyErr_Clear();
+ }
+ if (!dispatch) {
+ double d;
+ int res = SWIG_AddCast(SWIG_AsVal_double (obj,&d));
+ if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, 0, ULONG_MAX)) {
+ if (val) *val = (unsigned long)(d);
+ return res;
+ }
+ }
+ }
+#endif
+ return SWIG_TypeError;
+}
+
+
+SWIGINTERN int
+SWIG_AsVal_unsigned_SS_int (PyObject * obj, unsigned int *val)
+{
+ unsigned long v;
+ int res = SWIG_AsVal_unsigned_SS_long (obj, &v);
+ if (SWIG_IsOK(res)) {
+ if ((v > UINT_MAX)) {
+ return SWIG_OverflowError;
+ } else {
+ if (val) *val = (unsigned int)(v);
+ }
+ }
+ return res;
+}
+
+
+ #define SWIG_From_long PyLong_FromLong
+
+
+SWIGINTERNINLINE PyObject*
+SWIG_From_unsigned_SS_long (unsigned long value)
+{
+ return (value > LONG_MAX) ?
+ PyLong_FromUnsignedLong(value) : PyLong_FromLong((long)(value));
+}
+
+
+SWIGINTERNINLINE PyObject *
+SWIG_From_unsigned_SS_int (unsigned int value)
+{
+ return SWIG_From_unsigned_SS_long (value);
+}
+
+
+SWIGINTERN int
+SWIG_AsVal_long (PyObject *obj, long* val)
+{
+ if (PyLong_Check(obj)) {
+ if (val) *val = PyLong_AsLong(obj);
+ return SWIG_OK;
+ } else if (PyLong_Check(obj)) {
+ long v = PyLong_AsLong(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = v;
+ return SWIG_OK;
+ } else {
+ PyErr_Clear();
+ }
+ }
+#ifdef SWIG_PYTHON_CAST_MODE
+ {
+ int dispatch = 0;
+ long v = PyLong_AsLong(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = v;
+ return SWIG_AddCast(SWIG_OK);
+ } else {
+ PyErr_Clear();
+ }
+ if (!dispatch) {
+ double d;
+ int res = SWIG_AddCast(SWIG_AsVal_double (obj,&d));
+ if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, LONG_MIN, LONG_MAX)) {
+ if (val) *val = (long)(d);
+ return res;
+ }
+ }
+ }
+#endif
+ return SWIG_TypeError;
+}
+
+
+SWIGINTERN int
+SWIG_AsVal_int (PyObject * obj, int *val)
+{
+ long v;
+ int res = SWIG_AsVal_long (obj, &v);
+ if (SWIG_IsOK(res)) {
+ if ((v < INT_MIN || v > INT_MAX)) {
+ return SWIG_OverflowError;
+ } else {
+ if (val) *val = (int)(v);
+ }
+ }
+ return res;
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define RETURN_CVAR_ARRAY(fmt, arr) {\
+ PyObject *l, *o;\
+ uint32_t i;\
+\
+ if (!arr) {\
+ Py_INCREF(Py_None);\
+ return Py_None;\
+ }\
+ l = PyList_New(arr->count);\
+ if (!l) return NULL;\
+ for (i = 0; i < arr->count; ++i) {\
+ o = _Py_BuildValue(fmt, arr->item[i]);\
+ if (!o) {\
+ Py_DECREF(l);\
+ return NULL;\
+ }\
+ PyList_SET_ITEM(l, i, o);\
+ }\
+ return l;\
+}
+
+static PyObject *_Py_BuildValue(char *str, ...)
+{
+ PyObject * result = NULL;
+ va_list lst;
+ va_start(lst, str);
+ if (str && *str == 'I') {
+ uint32_t value = va_arg(lst, uint32_t);
+ if (value & 0x80000000) {
+ result = Py_BuildValue("L", (long)value);
+ } else {
+ result = Py_BuildValue("i", value);
+ }
+ } else {
+ result = Py_VaBuildValue(str, lst);
+ }
+ va_end(lst);
+ return result;
+}
+
+
+static PyObject *PyObject_FromCVAR(uint32_t cimtype, union CIMVAR *cvar)
+{
+ switch (cimtype) {
+ case CIM_SINT8: return Py_BuildValue("b", cvar->v_sint8);
+ case CIM_UINT8: return Py_BuildValue("B", cvar->v_uint8);
+ case CIM_SINT16: return Py_BuildValue("h", cvar->v_sint16);
+ case CIM_UINT16: return Py_BuildValue("H", cvar->v_uint16);
+ case CIM_SINT32: return Py_BuildValue("i", cvar->v_sint32);
+ case CIM_UINT32: return _Py_BuildValue("I", cvar->v_uint32);
+ case CIM_SINT64: return Py_BuildValue("L", cvar->v_sint64);
+ case CIM_UINT64: return Py_BuildValue("K", cvar->v_uint64);
+ case CIM_REAL32: return Py_BuildValue("f", cvar->v_real32);
+ case CIM_REAL64: return Py_BuildValue("d", cvar->v_real64);
+ case CIM_BOOLEAN: return Py_BuildValue("h", cvar->v_boolean);
+ case CIM_STRING: return Py_BuildValue("s", cvar->v_string);
+ case CIM_DATETIME: return Py_BuildValue("s", cvar->v_datetime);
+ case CIM_REFERENCE: return Py_BuildValue("s", cvar->v_reference);
+ case CIM_OBJECT: return PySWbemObject_FromWbemClassObject(cvar->v_object);
+ case CIM_ARR_SINT8: RETURN_CVAR_ARRAY("b", cvar->a_sint8);
+ case CIM_ARR_UINT8: RETURN_CVAR_ARRAY("B", cvar->a_uint8);
+ case CIM_ARR_SINT16: RETURN_CVAR_ARRAY("h", cvar->a_sint16);
+ case CIM_ARR_UINT16: RETURN_CVAR_ARRAY("H", cvar->a_uint16);
+ case CIM_ARR_SINT32: RETURN_CVAR_ARRAY("i", cvar->a_sint32);
+ case CIM_ARR_UINT32: RETURN_CVAR_ARRAY("I", cvar->a_uint32);
+ case CIM_ARR_SINT64: RETURN_CVAR_ARRAY("L", cvar->a_sint64);
+ case CIM_ARR_UINT64: RETURN_CVAR_ARRAY("K", cvar->a_uint64);
+ case CIM_ARR_REAL32: RETURN_CVAR_ARRAY("f", cvar->a_real32);
+ case CIM_ARR_REAL64: RETURN_CVAR_ARRAY("d", cvar->a_real64);
+ case CIM_ARR_BOOLEAN: RETURN_CVAR_ARRAY("h", cvar->a_boolean);
+ case CIM_ARR_STRING: RETURN_CVAR_ARRAY("s", cvar->a_string);
+ case CIM_ARR_DATETIME: RETURN_CVAR_ARRAY("s", cvar->a_datetime);
+ case CIM_ARR_REFERENCE: RETURN_CVAR_ARRAY("s", cvar->a_reference);
+ default:
+ {
+ char *str;
+ str = talloc_asprintf(NULL, "Unsupported CIMTYPE(0x%04X)", cimtype);
+ PyErr_SetString(PyExc_RuntimeError, str);
+ talloc_free(str);
+ return NULL;
+ }
+ }
+}
+
+#undef RETURN_CVAR_ARRAY
+
+PyObject *PySWbemObject_InitProperites(PyObject *o, struct WbemClassObject *wco)
+{
+ PyObject *properties;
+ PyObject *addProp;
+ uint32_t i;
+ int32_t r;
+ PyObject *result;
+
+ result = NULL;
+ properties = PyObject_GetAttrString(o, "Properties_");
+ if (!properties) return NULL;
+ addProp = PyObject_GetAttrString(properties, "Add");
+ if (!addProp) {
+ Py_DECREF(properties);
+ return NULL;
+ }
+
+ for (i = 0; i < wco->obj_class->__PROPERTY_COUNT; ++i) {
+ PyObject *args, *property;
+
+ args = Py_BuildValue("(si)", wco->obj_class->properties[i].property.name, wco->obj_class->properties[i].property.desc->cimtype & CIM_TYPEMASK);
+ if (!args) goto finish;
+ property = PyObject_CallObject(addProp, args);
+ Py_DECREF(args);
+ if (!property) goto finish;
+ if (wco->flags & WCF_INSTANCE) {
+ PyObject *value;
+
+ if (wco->instance->default_flags[i] & 1) {
+ value = Py_None;
+ Py_INCREF(Py_None);
+ } else
+ value = PyObject_FromCVAR(wco->obj_class->properties[i].property.desc->cimtype & CIM_TYPEMASK, &wco->instance->data[i]);
+ if (!value) {
+ Py_DECREF(property);
+ goto finish;
+ }
+ r = PyObject_SetAttrString(property, "Value", value);
+ Py_DECREF(value);
+ if (r == -1) {
+ PyErr_SetString(PyExc_RuntimeError, "Error setting value of property");
+ goto finish;
+ }
+ }
+ Py_DECREF(property);
+ }
+
+ Py_INCREF(Py_None);
+ result = Py_None;
+finish:
+ Py_DECREF(addProp);
+ Py_DECREF(properties);
+ return result;
+}
+
+static PyObject *PySWbemObject_FromWbemClassObject(struct WbemClassObject *wco)
+{
+ PyObject *swo_class, *swo, *args, *result;
+
+ swo_class = PyObject_GetAttrString(mod_win32_client, "SWbemObject");
+ if (!swo_class) return NULL;
+ args = PyTuple_New(0);
+ if (!args) {
+ Py_DECREF(swo_class);
+ return NULL;
+ }
+ swo = PyObject_CallObject(swo_class, args);
+ Py_DECREF(args);
+ Py_DECREF(swo_class);
+ if (!swo) return NULL;
+
+ result = PySWbemObject_InitProperites(swo, wco);
+ if (!result) {
+ Py_DECREF(swo);
+ return NULL;
+ }
+ Py_DECREF(result);
+
+ return swo;
+}
+
+
+SWIGINTERN PyObject *_wrap_WBEM_ConnectServer(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) {
+ PyObject *resultobj = 0;
+ struct com_context *arg1 = (struct com_context *) 0 ;
+ char *arg2 = (char *) 0 ;
+ char *arg3 = (char *) 0 ;
+ char *arg4 = (char *) 0 ;
+ char *arg5 = (char *) 0 ;
+ char *arg6 = (char *) 0 ;
+ uint32_t arg7 ;
+ char *arg8 = (char *) 0 ;
+ struct IWbemContext *arg9 = (struct IWbemContext *) 0 ;
+ struct IWbemServices **arg10 = (struct IWbemServices **) 0 ;
+ int res2 ;
+ char *buf2 = 0 ;
+ int alloc2 = 0 ;
+ int res3 ;
+ char *buf3 = 0 ;
+ int alloc3 = 0 ;
+ int res4 ;
+ char *buf4 = 0 ;
+ int alloc4 = 0 ;
+ int res5 ;
+ char *buf5 = 0 ;
+ int alloc5 = 0 ;
+ int res6 ;
+ char *buf6 = 0 ;
+ int alloc6 = 0 ;
+ unsigned int val7 ;
+ int ecode7 = 0 ;
+ int res8 ;
+ char *buf8 = 0 ;
+ int alloc8 = 0 ;
+ void *argp9 = 0 ;
+ int res9 = 0 ;
+ struct IWbemServices *temp10 ;
+ PyObject * obj0 = 0 ;
+ PyObject * obj1 = 0 ;
+ PyObject * obj2 = 0 ;
+ PyObject * obj3 = 0 ;
+ PyObject * obj4 = 0 ;
+ PyObject * obj5 = 0 ;
+ PyObject * obj6 = 0 ;
+ PyObject * obj7 = 0 ;
+ char * kwnames[] = {
+ (char *) "server",(char *) "nspace",(char *) "user",(char *) "password",(char *) "locale",(char *) "flags",(char *) "authority",(char *) "wbem_ctx", NULL
+ };
+ WERROR result;
+
+ {
+ arg1 = com_ctx;
+ }
+ {
+ arg10 = &temp10;
+ }
+ if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OOOOOOOO:WBEM_ConnectServer",kwnames,&obj0,&obj1,&obj2,&obj3,&obj4,&obj5,&obj6,&obj7)) SWIG_fail;
+ res2 = SWIG_AsCharPtrAndSize(obj0, &buf2, NULL, &alloc2);
+ if (!SWIG_IsOK(res2)) {
+ SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "WBEM_ConnectServer" "', argument " "2"" of type '" "char const *""'");
+ }
+ arg2 = (char *)(buf2);
+ res3 = SWIG_AsCharPtrAndSize(obj1, &buf3, NULL, &alloc3);
+ if (!SWIG_IsOK(res3)) {
+ SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "WBEM_ConnectServer" "', argument " "3"" of type '" "char const *""'");
+ }
+ arg3 = (char *)(buf3);
+ res4 = SWIG_AsCharPtrAndSize(obj2, &buf4, NULL, &alloc4);
+ if (!SWIG_IsOK(res4)) {
+ SWIG_exception_fail(SWIG_ArgError(res4), "in method '" "WBEM_ConnectServer" "', argument " "4"" of type '" "char const *""'");
+ }
+ arg4 = (char *)(buf4);
+ res5 = SWIG_AsCharPtrAndSize(obj3, &buf5, NULL, &alloc5);
+ if (!SWIG_IsOK(res5)) {
+ SWIG_exception_fail(SWIG_ArgError(res5), "in method '" "WBEM_ConnectServer" "', argument " "5"" of type '" "char const *""'");
+ }
+ arg5 = (char *)(buf5);
+ res6 = SWIG_AsCharPtrAndSize(obj4, &buf6, NULL, &alloc6);
+ if (!SWIG_IsOK(res6)) {
+ SWIG_exception_fail(SWIG_ArgError(res6), "in method '" "WBEM_ConnectServer" "', argument " "6"" of type '" "char const *""'");
+ }
+ arg6 = (char *)(buf6);
+ ecode7 = SWIG_AsVal_unsigned_SS_int(obj5, &val7);
+ if (!SWIG_IsOK(ecode7)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "WBEM_ConnectServer" "', argument " "7"" of type '" "uint32_t""'");
+ }
+ arg7 = (uint32_t)(val7);
+ res8 = SWIG_AsCharPtrAndSize(obj6, &buf8, NULL, &alloc8);
+ if (!SWIG_IsOK(res8)) {
+ SWIG_exception_fail(SWIG_ArgError(res8), "in method '" "WBEM_ConnectServer" "', argument " "8"" of type '" "char const *""'");
+ }
+ arg8 = (char *)(buf8);
+ res9 = SWIG_ConvertPtr(obj7, &argp9,SWIGTYPE_p_IWbemContext, 0 | 0 );
+ if (!SWIG_IsOK(res9)) {
+ SWIG_exception_fail(SWIG_ArgError(res9), "in method '" "WBEM_ConnectServer" "', argument " "9"" of type '" "struct IWbemContext *""'");
+ }
+ arg9 = (struct IWbemContext *)(argp9);
+ result = WBEM_ConnectServer(arg1,(char const *)arg2,(char const *)arg3,(char const *)arg4,(char const *)arg5,(char const *)arg6,arg7,(char const *)arg8,arg9,arg10);
+ if (!W_ERROR_IS_OK(result)) {
+ PyErr_SetWERROR(result);
+ SWIG_fail;
+ } else if (resultobj == NULL) {
+ resultobj = Py_None;
+ }
+ {
+ PyObject *o;
+ o = SWIG_NewPointerObj(*arg10, SWIGTYPE_p_IWbemServices, 0);
+ push_object(&resultobj, o);
+ }
+ if (alloc2 == SWIG_NEWOBJ) free((char*)buf2);
+ if (alloc3 == SWIG_NEWOBJ) free((char*)buf3);
+ if (alloc4 == SWIG_NEWOBJ) free((char*)buf4);
+ if (alloc5 == SWIG_NEWOBJ) free((char*)buf5);
+ if (alloc6 == SWIG_NEWOBJ) free((char*)buf6);
+ if (alloc8 == SWIG_NEWOBJ) free((char*)buf8);
+ return resultobj;
+fail:
+ if (alloc2 == SWIG_NEWOBJ) free((char*)buf2);
+ if (alloc3 == SWIG_NEWOBJ) free((char*)buf3);
+ if (alloc4 == SWIG_NEWOBJ) free((char*)buf4);
+ if (alloc5 == SWIG_NEWOBJ) free((char*)buf5);
+ if (alloc6 == SWIG_NEWOBJ) free((char*)buf6);
+ if (alloc8 == SWIG_NEWOBJ) free((char*)buf8);
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_IUnknown_Release(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ IUnknown *arg1 = (IUnknown *) 0 ;
+ TALLOC_CTX *arg2 = (TALLOC_CTX *) 0 ;
+ void *argp1 = 0 ;
+ int res1 = 0 ;
+ PyObject *swig_obj[1] ;
+ uint32_t result;
+
+ arg2 = NULL;
+ if (!args) SWIG_fail;
+ swig_obj[0] = args;
+ res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_IUnknown, 0 | 0 );
+ if (!SWIG_IsOK(res1)) {
+ SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IUnknown_Release" "', argument " "1"" of type '" "IUnknown *""'");
+ }
+ arg1 = (IUnknown *)(argp1);
+ result = (uint32_t)IUnknown_Release(arg1,arg2);
+ resultobj = SWIG_From_unsigned_SS_int((unsigned int)(result));
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_new_IUnknown(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ IUnknown *result = 0 ;
+
+ if (!SWIG_Python_UnpackTuple(args,"new_IUnknown",0,0,0)) SWIG_fail;
+ result = (IUnknown *)calloc(1, sizeof(IUnknown));
+ resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_IUnknown, SWIG_POINTER_NEW | 0 );
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_delete_IUnknown(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ IUnknown *arg1 = (IUnknown *) 0 ;
+ void *argp1 = 0 ;
+ int res1 = 0 ;
+ PyObject *swig_obj[1] ;
+
+ if (!args) SWIG_fail;
+ swig_obj[0] = args;
+ res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_IUnknown, SWIG_POINTER_DISOWN | 0 );
+ if (!SWIG_IsOK(res1)) {
+ SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "delete_IUnknown" "', argument " "1"" of type '" "IUnknown *""'");
+ }
+ arg1 = (IUnknown *)(argp1);
+ free((char *) arg1);
+ resultobj = SWIG_Py_Void();
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *IUnknown_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *obj;
+ if (!SWIG_Python_UnpackTuple(args,(char*)"swigregister", 1, 1,&obj)) return NULL;
+ SWIG_TypeNewClientData(SWIGTYPE_p_IUnknown, SWIG_NewClientData(obj));
+ return SWIG_Py_Void();
+}
+
+SWIGINTERN PyObject *IUnknown_swiginit(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ return SWIG_Python_InitShadowInstance(args);
+}
+
+SWIGINTERN PyObject *_wrap_IWbemServices_ExecQuery(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) {
+ PyObject *resultobj = 0;
+ IWbemServices *arg1 = (IWbemServices *) 0 ;
+ TALLOC_CTX *arg2 = (TALLOC_CTX *) 0 ;
+ struct BSTR arg3 ;
+ struct BSTR arg4 ;
+ int32_t arg5 ;
+ struct IWbemContext *arg6 = (struct IWbemContext *) 0 ;
+ struct IEnumWbemClassObject **arg7 = (struct IEnumWbemClassObject **) 0 ;
+ void *argp1 = 0 ;
+ int res1 = 0 ;
+ int val5 ;
+ int ecode5 = 0 ;
+ void *argp6 = 0 ;
+ int res6 = 0 ;
+ struct IEnumWbemClassObject *temp7 ;
+ PyObject * obj0 = 0 ;
+ PyObject * obj1 = 0 ;
+ PyObject * obj2 = 0 ;
+ PyObject * obj3 = 0 ;
+ PyObject * obj4 = 0 ;
+ char * kwnames[] = {
+ (char *) "self",(char *) "strQueryLanguage",(char *) "strQuery",(char *) "lFlags",(char *) "pCtx", NULL
+ };
+ WERROR result;
+
+ arg2 = NULL;
+ {
+ arg7 = &temp7;
+ }
+ if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OOOOO:IWbemServices_ExecQuery",kwnames,&obj0,&obj1,&obj2,&obj3,&obj4)) SWIG_fail;
+ res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_IWbemServices, 0 | 0 );
+ if (!SWIG_IsOK(res1)) {
+ SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IWbemServices_ExecQuery" "', argument " "1"" of type '" "IWbemServices *""'");
+ }
+ arg1 = (IWbemServices *)(argp1);
+ {
+ (&arg3)->data = PyString_AsString(obj1);
+ }
+ {
+ (&arg4)->data = PyString_AsString(obj2);
+ }
+ ecode5 = SWIG_AsVal_int(obj3, &val5);
+ if (!SWIG_IsOK(ecode5)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "IWbemServices_ExecQuery" "', argument " "5"" of type '" "int32_t""'");
+ }
+ arg5 = (int32_t)(val5);
+ res6 = SWIG_ConvertPtr(obj4, &argp6,SWIGTYPE_p_IWbemContext, 0 | 0 );
+ if (!SWIG_IsOK(res6)) {
+ SWIG_exception_fail(SWIG_ArgError(res6), "in method '" "IWbemServices_ExecQuery" "', argument " "6"" of type '" "struct IWbemContext *""'");
+ }
+ arg6 = (struct IWbemContext *)(argp6);
+ result = IWbemServices_ExecQuery(arg1,arg2,arg3,arg4,arg5,arg6,arg7);
+ if (!W_ERROR_IS_OK(result)) {
+ PyErr_SetWERROR(result);
+ SWIG_fail;
+ } else if (resultobj == NULL) {
+ resultobj = Py_None;
+ }
+ {
+ PyObject *o;
+ o = SWIG_NewPointerObj(*arg7, SWIGTYPE_p_IEnumWbemClassObject, 0);
+ push_object(&resultobj, o);
+ }
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_IWbemServices_ExecNotificationQuery(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) {
+ PyObject *resultobj = 0;
+ IWbemServices *arg1 = (IWbemServices *) 0 ;
+ TALLOC_CTX *arg2 = (TALLOC_CTX *) 0 ;
+ struct BSTR arg3 ;
+ struct BSTR arg4 ;
+ int32_t arg5 ;
+ struct IWbemContext *arg6 = (struct IWbemContext *) 0 ;
+ struct IEnumWbemClassObject **arg7 = (struct IEnumWbemClassObject **) 0 ;
+ void *argp1 = 0 ;
+ int res1 = 0 ;
+ int val5 ;
+ int ecode5 = 0 ;
+ void *argp6 = 0 ;
+ int res6 = 0 ;
+ struct IEnumWbemClassObject *temp7 ;
+ PyObject * obj0 = 0 ;
+ PyObject * obj1 = 0 ;
+ PyObject * obj2 = 0 ;
+ PyObject * obj3 = 0 ;
+ PyObject * obj4 = 0 ;
+ char * kwnames[] = {
+ (char *) "self",(char *) "strQueryLanguage",(char *) "strQuery",(char *) "lFlags",(char *) "pCtx", NULL
+ };
+ WERROR result;
+
+ arg2 = NULL;
+ {
+ arg7 = &temp7;
+ }
+ if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OOOOO:IWbemServices_ExecNotificationQuery",kwnames,&obj0,&obj1,&obj2,&obj3,&obj4)) SWIG_fail;
+ res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_IWbemServices, 0 | 0 );
+ if (!SWIG_IsOK(res1)) {
+ SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IWbemServices_ExecNotificationQuery" "', argument " "1"" of type '" "IWbemServices *""'");
+ }
+ arg1 = (IWbemServices *)(argp1);
+ {
+ (&arg3)->data = PyString_AsString(obj1);
+ }
+ {
+ (&arg4)->data = PyString_AsString(obj2);
+ }
+ ecode5 = SWIG_AsVal_int(obj3, &val5);
+ if (!SWIG_IsOK(ecode5)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "IWbemServices_ExecNotificationQuery" "', argument " "5"" of type '" "int32_t""'");
+ }
+ arg5 = (int32_t)(val5);
+ res6 = SWIG_ConvertPtr(obj4, &argp6,SWIGTYPE_p_IWbemContext, 0 | 0 );
+ if (!SWIG_IsOK(res6)) {
+ SWIG_exception_fail(SWIG_ArgError(res6), "in method '" "IWbemServices_ExecNotificationQuery" "', argument " "6"" of type '" "struct IWbemContext *""'");
+ }
+ arg6 = (struct IWbemContext *)(argp6);
+ result = IWbemServices_ExecNotificationQuery(arg1,arg2,arg3,arg4,arg5,arg6,arg7);
+ if (!W_ERROR_IS_OK(result)) {
+ PyErr_SetWERROR(result);
+ SWIG_fail;
+ } else if (resultobj == NULL) {
+ resultobj = Py_None;
+ }
+ {
+ PyObject *o;
+ o = SWIG_NewPointerObj(*arg7, SWIGTYPE_p_IEnumWbemClassObject, 0);
+ push_object(&resultobj, o);
+ }
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_IWbemServices_CreateInstanceEnum(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) {
+ PyObject *resultobj = 0;
+ IWbemServices *arg1 = (IWbemServices *) 0 ;
+ TALLOC_CTX *arg2 = (TALLOC_CTX *) 0 ;
+ struct BSTR arg3 ;
+ int32_t arg4 ;
+ struct IWbemContext *arg5 = (struct IWbemContext *) 0 ;
+ struct IEnumWbemClassObject **arg6 = (struct IEnumWbemClassObject **) 0 ;
+ void *argp1 = 0 ;
+ int res1 = 0 ;
+ int val4 ;
+ int ecode4 = 0 ;
+ void *argp5 = 0 ;
+ int res5 = 0 ;
+ struct IEnumWbemClassObject *temp6 ;
+ PyObject * obj0 = 0 ;
+ PyObject * obj1 = 0 ;
+ PyObject * obj2 = 0 ;
+ PyObject * obj3 = 0 ;
+ char * kwnames[] = {
+ (char *) "self",(char *) "strClass",(char *) "lFlags",(char *) "pCtx", NULL
+ };
+ WERROR result;
+
+ arg2 = NULL;
+ {
+ arg6 = &temp6;
+ }
+ if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OOOO:IWbemServices_CreateInstanceEnum",kwnames,&obj0,&obj1,&obj2,&obj3)) SWIG_fail;
+ res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_IWbemServices, 0 | 0 );
+ if (!SWIG_IsOK(res1)) {
+ SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IWbemServices_CreateInstanceEnum" "', argument " "1"" of type '" "IWbemServices *""'");
+ }
+ arg1 = (IWbemServices *)(argp1);
+ {
+ (&arg3)->data = PyString_AsString(obj1);
+ }
+ ecode4 = SWIG_AsVal_int(obj2, &val4);
+ if (!SWIG_IsOK(ecode4)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "IWbemServices_CreateInstanceEnum" "', argument " "4"" of type '" "int32_t""'");
+ }
+ arg4 = (int32_t)(val4);
+ res5 = SWIG_ConvertPtr(obj3, &argp5,SWIGTYPE_p_IWbemContext, 0 | 0 );
+ if (!SWIG_IsOK(res5)) {
+ SWIG_exception_fail(SWIG_ArgError(res5), "in method '" "IWbemServices_CreateInstanceEnum" "', argument " "5"" of type '" "struct IWbemContext *""'");
+ }
+ arg5 = (struct IWbemContext *)(argp5);
+ result = IWbemServices_CreateInstanceEnum(arg1,arg2,arg3,arg4,arg5,arg6);
+ if (!W_ERROR_IS_OK(result)) {
+ PyErr_SetWERROR(result);
+ SWIG_fail;
+ } else if (resultobj == NULL) {
+ resultobj = Py_None;
+ }
+ {
+ PyObject *o;
+ o = SWIG_NewPointerObj(*arg6, SWIGTYPE_p_IEnumWbemClassObject, 0);
+ push_object(&resultobj, o);
+ }
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_new_IWbemServices(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ IWbemServices *result = 0 ;
+
+ if (!SWIG_Python_UnpackTuple(args,"new_IWbemServices",0,0,0)) SWIG_fail;
+ result = (IWbemServices *)calloc(1, sizeof(IWbemServices));
+ resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_IWbemServices, SWIG_POINTER_NEW | 0 );
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_delete_IWbemServices(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ IWbemServices *arg1 = (IWbemServices *) 0 ;
+ void *argp1 = 0 ;
+ int res1 = 0 ;
+ PyObject *swig_obj[1] ;
+
+ if (!args) SWIG_fail;
+ swig_obj[0] = args;
+ res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_IWbemServices, SWIG_POINTER_DISOWN | 0 );
+ if (!SWIG_IsOK(res1)) {
+ SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "delete_IWbemServices" "', argument " "1"" of type '" "IWbemServices *""'");
+ }
+ arg1 = (IWbemServices *)(argp1);
+ free((char *) arg1);
+ resultobj = SWIG_Py_Void();
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *IWbemServices_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *obj;
+ if (!SWIG_Python_UnpackTuple(args,(char*)"swigregister", 1, 1,&obj)) return NULL;
+ SWIG_TypeNewClientData(SWIGTYPE_p_IWbemServices, SWIG_NewClientData(obj));
+ return SWIG_Py_Void();
+}
+
+SWIGINTERN PyObject *IWbemServices_swiginit(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ return SWIG_Python_InitShadowInstance(args);
+}
+
+SWIGINTERN PyObject *_wrap_IEnumWbemClassObject_Reset(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ IEnumWbemClassObject *arg1 = (IEnumWbemClassObject *) 0 ;
+ TALLOC_CTX *arg2 = (TALLOC_CTX *) 0 ;
+ void *argp1 = 0 ;
+ int res1 = 0 ;
+ PyObject *swig_obj[1] ;
+ WERROR result;
+
+ arg2 = NULL;
+ if (!args) SWIG_fail;
+ swig_obj[0] = args;
+ res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_IEnumWbemClassObject, 0 | 0 );
+ if (!SWIG_IsOK(res1)) {
+ SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IEnumWbemClassObject_Reset" "', argument " "1"" of type '" "IEnumWbemClassObject *""'");
+ }
+ arg1 = (IEnumWbemClassObject *)(argp1);
+ result = IEnumWbemClassObject_Reset(arg1,arg2);
+ if (!W_ERROR_IS_OK(result)) {
+ PyErr_SetWERROR(result);
+ SWIG_fail;
+ } else if (resultobj == NULL) {
+ resultobj = Py_None;
+ }
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_new_IEnumWbemClassObject(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ IEnumWbemClassObject *result = 0 ;
+
+ if (!SWIG_Python_UnpackTuple(args,"new_IEnumWbemClassObject",0,0,0)) SWIG_fail;
+ result = (IEnumWbemClassObject *)calloc(1, sizeof(IEnumWbemClassObject));
+ resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_IEnumWbemClassObject, SWIG_POINTER_NEW | 0 );
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_delete_IEnumWbemClassObject(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ IEnumWbemClassObject *arg1 = (IEnumWbemClassObject *) 0 ;
+ void *argp1 = 0 ;
+ int res1 = 0 ;
+ PyObject *swig_obj[1] ;
+
+ if (!args) SWIG_fail;
+ swig_obj[0] = args;
+ res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_IEnumWbemClassObject, SWIG_POINTER_DISOWN | 0 );
+ if (!SWIG_IsOK(res1)) {
+ SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "delete_IEnumWbemClassObject" "', argument " "1"" of type '" "IEnumWbemClassObject *""'");
+ }
+ arg1 = (IEnumWbemClassObject *)(argp1);
+ free((char *) arg1);
+ resultobj = SWIG_Py_Void();
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *IEnumWbemClassObject_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *obj;
+ if (!SWIG_Python_UnpackTuple(args,(char*)"swigregister", 1, 1,&obj)) return NULL;
+ SWIG_TypeNewClientData(SWIGTYPE_p_IEnumWbemClassObject, SWIG_NewClientData(obj));
+ return SWIG_Py_Void();
+}
+
+SWIGINTERN PyObject *IEnumWbemClassObject_swiginit(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ return SWIG_Python_InitShadowInstance(args);
+}
+
+SWIGINTERN PyObject *_wrap_IEnumWbemClassObject_SmartNext(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) {
+ PyObject *resultobj = 0;
+ struct IEnumWbemClassObject *arg1 = (struct IEnumWbemClassObject *) 0 ;
+ TALLOC_CTX *arg2 = (TALLOC_CTX *) 0 ;
+ int32_t arg3 ;
+ uint32_t arg4 ;
+ struct WbemClassObject **arg5 = (struct WbemClassObject **) 0 ;
+ uint32_t *arg6 = (uint32_t *) 0 ;
+ void *argp1 = 0 ;
+ int res1 = 0 ;
+ int val3 ;
+ int ecode3 = 0 ;
+ uint32_t uReturned4 ;
+ PyObject * obj0 = 0 ;
+ PyObject * obj1 = 0 ;
+ PyObject * obj2 = 0 ;
+ char * kwnames[] = {
+ (char *) "d",(char *) "lTimeout",(char *) "uCount", NULL
+ };
+ WERROR result;
+
+ arg2 = NULL;
+ if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OOO:IEnumWbemClassObject_SmartNext",kwnames,&obj0,&obj1,&obj2)) SWIG_fail;
+ res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_IEnumWbemClassObject, 0 | 0 );
+ if (!SWIG_IsOK(res1)) {
+ SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IEnumWbemClassObject_SmartNext" "', argument " "1"" of type '" "struct IEnumWbemClassObject *""'");
+ }
+ arg1 = (struct IEnumWbemClassObject *)(argp1);
+ ecode3 = SWIG_AsVal_int(obj1, &val3);
+ if (!SWIG_IsOK(ecode3)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "IEnumWbemClassObject_SmartNext" "', argument " "3"" of type '" "int32_t""'");
+ }
+ arg3 = (int32_t)(val3);
+ {
+ if (PyLong_Check(obj2))
+ arg4 = PyLong_AsUnsignedLong(obj2);
+ else if (PyLong_Check(obj2))
+ arg4 = PyLong_AsLong(obj2);
+ else {
+ PyErr_SetString(PyExc_TypeError,"Expected a long or an int");
+ return NULL;
+ }
+ arg5 = talloc_array(NULL, struct WbemClassObject *, arg4);
+ arg6 = &uReturned4;
+ }
+ result = IEnumWbemClassObject_SmartNext(arg1,arg2,arg3,arg4,arg5,arg6);
+ if (!W_ERROR_IS_OK(result)) {
+ PyErr_SetWERROR(result);
+ SWIG_fail;
+ } else if (resultobj == NULL) {
+ resultobj = Py_None;
+ }
+ {
+ uint32_t i;
+ PyObject *o;
+ int32_t error;
+
+ error = 0;
+
+ resultobj = PyTuple_New(*arg6);
+ for (i = 0; i < *arg6; ++i) {
+ if (!error) {
+ o = PySWbemObject_FromWbemClassObject(arg5[i]);
+ if (!o)
+ --error;
+ else
+ error = PyTuple_SetItem(resultobj, i, o);
+ }
+ talloc_free(arg5[i]);
+ }
+ talloc_free(arg5);
+ if (error) return NULL;
+ }
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
+static PyMethodDef SwigMethods[] = {
+ { (char *)"WBEM_ConnectServer", (PyCFunction) _wrap_WBEM_ConnectServer, METH_VARARGS | METH_KEYWORDS, NULL},
+ { (char *)"IUnknown_Release", (PyCFunction)_wrap_IUnknown_Release, METH_O, NULL},
+ { (char *)"new_IUnknown", (PyCFunction)_wrap_new_IUnknown, METH_NOARGS, NULL},
+ { (char *)"delete_IUnknown", (PyCFunction)_wrap_delete_IUnknown, METH_O, NULL},
+ { (char *)"IUnknown_swigregister", IUnknown_swigregister, METH_VARARGS, NULL},
+ { (char *)"IUnknown_swiginit", IUnknown_swiginit, METH_VARARGS, NULL},
+ { (char *)"IWbemServices_ExecQuery", (PyCFunction) _wrap_IWbemServices_ExecQuery, METH_VARARGS | METH_KEYWORDS, NULL},
+ { (char *)"IWbemServices_ExecNotificationQuery", (PyCFunction) _wrap_IWbemServices_ExecNotificationQuery, METH_VARARGS | METH_KEYWORDS, NULL},
+ { (char *)"IWbemServices_CreateInstanceEnum", (PyCFunction) _wrap_IWbemServices_CreateInstanceEnum, METH_VARARGS | METH_KEYWORDS, NULL},
+ { (char *)"new_IWbemServices", (PyCFunction)_wrap_new_IWbemServices, METH_NOARGS, NULL},
+ { (char *)"delete_IWbemServices", (PyCFunction)_wrap_delete_IWbemServices, METH_O, NULL},
+ { (char *)"IWbemServices_swigregister", IWbemServices_swigregister, METH_VARARGS, NULL},
+ { (char *)"IWbemServices_swiginit", IWbemServices_swiginit, METH_VARARGS, NULL},
+ { (char *)"IEnumWbemClassObject_Reset", (PyCFunction)_wrap_IEnumWbemClassObject_Reset, METH_O, NULL},
+ { (char *)"new_IEnumWbemClassObject", (PyCFunction)_wrap_new_IEnumWbemClassObject, METH_NOARGS, NULL},
+ { (char *)"delete_IEnumWbemClassObject", (PyCFunction)_wrap_delete_IEnumWbemClassObject, METH_O, NULL},
+ { (char *)"IEnumWbemClassObject_swigregister", IEnumWbemClassObject_swigregister, METH_VARARGS, NULL},
+ { (char *)"IEnumWbemClassObject_swiginit", IEnumWbemClassObject_swiginit, METH_VARARGS, NULL},
+ { (char *)"IEnumWbemClassObject_SmartNext", (PyCFunction) _wrap_IEnumWbemClassObject_SmartNext, METH_VARARGS | METH_KEYWORDS, NULL},
+ { NULL, NULL, 0, NULL }
+};
+
+
+/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (BEGIN) -------- */
+
+static swig_type_info _swigt__p_BSTR = {"_p_BSTR", "struct BSTR *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_IEnumWbemClassObject = {"_p_IEnumWbemClassObject", "struct IEnumWbemClassObject *|IEnumWbemClassObject *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_IUnknown = {"_p_IUnknown", "struct IUnknown *|IUnknown *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_IWbemContext = {"_p_IWbemContext", "struct IWbemContext *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_IWbemServices = {"_p_IWbemServices", "struct IWbemServices *|IWbemServices *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_TALLOC_CTX = {"_p_TALLOC_CTX", "TALLOC_CTX *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_char = {"_p_char", "char *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_com_context = {"_p_com_context", "struct com_context *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_int = {"_p_int", "intptr_t *|int *|int_least32_t *|int_fast32_t *|int32_t *|int_fast16_t *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_long_long = {"_p_long_long", "int_least64_t *|int_fast64_t *|int64_t *|long long *|intmax_t *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_p_IEnumWbemClassObject = {"_p_p_IEnumWbemClassObject", "struct IEnumWbemClassObject **", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_p_IWbemServices = {"_p_p_IWbemServices", "struct IWbemServices **", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_p_WbemClassObject = {"_p_p_WbemClassObject", "struct WbemClassObject **", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_short = {"_p_short", "short *|int_least16_t *|int16_t *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_signed_char = {"_p_signed_char", "signed char *|int_least8_t *|int_fast8_t *|int8_t *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_unsigned_char = {"_p_unsigned_char", "unsigned char *|uint_least8_t *|uint_fast8_t *|uint8_t *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_unsigned_int = {"_p_unsigned_int", "uintptr_t *|uint_least32_t *|uint_fast32_t *|uint32_t *|unsigned int *|uint_fast16_t *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_unsigned_long_long = {"_p_unsigned_long_long", "uint_least64_t *|uint_fast64_t *|uint64_t *|unsigned long long *|uintmax_t *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_unsigned_short = {"_p_unsigned_short", "unsigned short *|uint_least16_t *|uint16_t *", 0, 0, (void*)0, 0};
+
+static swig_type_info *swig_type_initial[] = {
+ &_swigt__p_BSTR,
+ &_swigt__p_IEnumWbemClassObject,
+ &_swigt__p_IUnknown,
+ &_swigt__p_IWbemContext,
+ &_swigt__p_IWbemServices,
+ &_swigt__p_TALLOC_CTX,
+ &_swigt__p_char,
+ &_swigt__p_com_context,
+ &_swigt__p_int,
+ &_swigt__p_long_long,
+ &_swigt__p_p_IEnumWbemClassObject,
+ &_swigt__p_p_IWbemServices,
+ &_swigt__p_p_WbemClassObject,
+ &_swigt__p_short,
+ &_swigt__p_signed_char,
+ &_swigt__p_unsigned_char,
+ &_swigt__p_unsigned_int,
+ &_swigt__p_unsigned_long_long,
+ &_swigt__p_unsigned_short,
+};
+
+static swig_cast_info _swigc__p_BSTR[] = { {&_swigt__p_BSTR, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_IEnumWbemClassObject[] = { {&_swigt__p_IEnumWbemClassObject, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_IUnknown[] = { {&_swigt__p_IUnknown, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_IWbemContext[] = { {&_swigt__p_IWbemContext, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_IWbemServices[] = { {&_swigt__p_IWbemServices, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_TALLOC_CTX[] = { {&_swigt__p_TALLOC_CTX, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_char[] = { {&_swigt__p_char, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_com_context[] = { {&_swigt__p_com_context, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_int[] = { {&_swigt__p_int, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_long_long[] = { {&_swigt__p_long_long, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_p_IEnumWbemClassObject[] = { {&_swigt__p_p_IEnumWbemClassObject, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_p_IWbemServices[] = { {&_swigt__p_p_IWbemServices, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_p_WbemClassObject[] = { {&_swigt__p_p_WbemClassObject, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_short[] = { {&_swigt__p_short, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_signed_char[] = { {&_swigt__p_signed_char, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_unsigned_char[] = { {&_swigt__p_unsigned_char, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_unsigned_int[] = { {&_swigt__p_unsigned_int, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_unsigned_long_long[] = { {&_swigt__p_unsigned_long_long, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_unsigned_short[] = { {&_swigt__p_unsigned_short, 0, 0, 0},{0, 0, 0, 0}};
+
+static swig_cast_info *swig_cast_initial[] = {
+ _swigc__p_BSTR,
+ _swigc__p_IEnumWbemClassObject,
+ _swigc__p_IUnknown,
+ _swigc__p_IWbemContext,
+ _swigc__p_IWbemServices,
+ _swigc__p_TALLOC_CTX,
+ _swigc__p_char,
+ _swigc__p_com_context,
+ _swigc__p_int,
+ _swigc__p_long_long,
+ _swigc__p_p_IEnumWbemClassObject,
+ _swigc__p_p_IWbemServices,
+ _swigc__p_p_WbemClassObject,
+ _swigc__p_short,
+ _swigc__p_signed_char,
+ _swigc__p_unsigned_char,
+ _swigc__p_unsigned_int,
+ _swigc__p_unsigned_long_long,
+ _swigc__p_unsigned_short,
+};
+
+
+/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (END) -------- */
+
+static swig_const_info swig_const_table[] = {
+{0, 0, 0, 0.0, 0, 0}};
+
+#ifdef __cplusplus
+}
+#endif
+/* -----------------------------------------------------------------------------
+ * Type initialization:
+ * This problem is tough by the requirement that no dynamic
+ * memory is used. Also, since swig_type_info structures store pointers to
+ * swig_cast_info structures and swig_cast_info structures store pointers back
+ * to swig_type_info structures, we need some lookup code at initialization.
+ * The idea is that swig generates all the structures that are needed.
+ * The runtime then collects these partially filled structures.
+ * The SWIG_InitializeModule function takes these initial arrays out of
+ * swig_module, and does all the lookup, filling in the swig_module.types
+ * array with the correct data and linking the correct swig_cast_info
+ * structures together.
+ *
+ * The generated swig_type_info structures are assigned statically to an initial
+ * array. We just loop through that array, and handle each type individually.
+ * First we lookup if this type has been already loaded, and if so, use the
+ * loaded structure instead of the generated one. Then we have to fill in the
+ * cast linked list. The cast data is initially stored in something like a
+ * two-dimensional array. Each row corresponds to a type (there are the same
+ * number of rows as there are in the swig_type_initial array). Each entry in
+ * a column is one of the swig_cast_info structures for that type.
+ * The cast_initial array is actually an array of arrays, because each row has
+ * a variable number of columns. So to actually build the cast linked list,
+ * we find the array of casts associated with the type, and loop through it
+ * adding the casts to the list. The one last trick we need to do is making
+ * sure the type pointer in the swig_cast_info struct is correct.
+ *
+ * First off, we lookup the cast->type name to see if it is already loaded.
+ * There are three cases to handle:
+ * 1) If the cast->type has already been loaded AND the type we are adding
+ * casting info to has not been loaded (it is in this module), THEN we
+ * replace the cast->type pointer with the type pointer that has already
+ * been loaded.
+ * 2) If BOTH types (the one we are adding casting info to, and the
+ * cast->type) are loaded, THEN the cast info has already been loaded by
+ * the previous module so we just ignore it.
+ * 3) Finally, if cast->type has not already been loaded, then we add that
+ * swig_cast_info to the linked list (because the cast->type) pointer will
+ * be correct.
+ * ----------------------------------------------------------------------------- */
+
+#ifdef __cplusplus
+extern "C" {
+#if 0
+} /* c-mode */
+#endif
+#endif
+
+#if 0
+#define SWIGRUNTIME_DEBUG
+#endif
+
+
+SWIGRUNTIME void
+SWIG_InitializeModule(void *clientdata) {
+ size_t i;
+ swig_module_info *module_head, *iter;
+ int found, init;
+
+ clientdata = clientdata;
+
+ /* check to see if the circular list has been setup, if not, set it up */
+ if (swig_module.next==0) {
+ /* Initialize the swig_module */
+ swig_module.type_initial = swig_type_initial;
+ swig_module.cast_initial = swig_cast_initial;
+ swig_module.next = &swig_module;
+ init = 1;
+ } else {
+ init = 0;
+ }
+
+ /* Try and load any already created modules */
+ module_head = SWIG_GetModule(clientdata);
+ if (!module_head) {
+ /* This is the first module loaded for this interpreter */
+ /* so set the swig module into the interpreter */
+ SWIG_SetModule(clientdata, &swig_module);
+ module_head = &swig_module;
+ } else {
+ /* the interpreter has loaded a SWIG module, but has it loaded this one? */
+ found=0;
+ iter=module_head;
+ do {
+ if (iter==&swig_module) {
+ found=1;
+ break;
+ }
+ iter=iter->next;
+ } while (iter!= module_head);
+
+ /* if the is found in the list, then all is done and we may leave */
+ if (found) return;
+ /* otherwise we must add out module into the list */
+ swig_module.next = module_head->next;
+ module_head->next = &swig_module;
+ }
+
+ /* When multiple interpeters are used, a module could have already been initialized in
+ a different interpreter, but not yet have a pointer in this interpreter.
+ In this case, we do not want to continue adding types... everything should be
+ set up already */
+ if (init == 0) return;
+
+ /* Now work on filling in swig_module.types */
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: size %d\n", swig_module.size);
+#endif
+ for (i = 0; i < swig_module.size; ++i) {
+ swig_type_info *type = 0;
+ swig_type_info *ret;
+ swig_cast_info *cast;
+
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: type %d %s\n", i, swig_module.type_initial[i]->name);
+#endif
+
+ /* if there is another module already loaded */
+ if (swig_module.next != &swig_module) {
+ type = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, swig_module.type_initial[i]->name);
+ }
+ if (type) {
+ /* Overwrite clientdata field */
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: found type %s\n", type->name);
+#endif
+ if (swig_module.type_initial[i]->clientdata) {
+ type->clientdata = swig_module.type_initial[i]->clientdata;
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: found and overwrite type %s \n", type->name);
+#endif
+ }
+ } else {
+ type = swig_module.type_initial[i];
+ }
+
+ /* Insert casting types */
+ cast = swig_module.cast_initial[i];
+ while (cast->type) {
+ /* Don't need to add information already in the list */
+ ret = 0;
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: look cast %s\n", cast->type->name);
+#endif
+ if (swig_module.next != &swig_module) {
+ ret = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, cast->type->name);
+#ifdef SWIGRUNTIME_DEBUG
+ if (ret) printf("SWIG_InitializeModule: found cast %s\n", ret->name);
+#endif
+ }
+ if (ret) {
+ if (type == swig_module.type_initial[i]) {
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: skip old type %s\n", ret->name);
+#endif
+ cast->type = ret;
+ ret = 0;
+ } else {
+ /* Check for casting already in the list */
+ swig_cast_info *ocast = SWIG_TypeCheck(ret->name, type);
+#ifdef SWIGRUNTIME_DEBUG
+ if (ocast) printf("SWIG_InitializeModule: skip old cast %s\n", ret->name);
+#endif
+ if (!ocast) ret = 0;
+ }
+ }
+
+ if (!ret) {
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: adding cast %s\n", cast->type->name);
+#endif
+ if (type->cast) {
+ type->cast->prev = cast;
+ cast->next = type->cast;
+ }
+ type->cast = cast;
+ }
+ cast++;
+ }
+ /* Set entry in modules->types array equal to the type */
+ swig_module.types[i] = type;
+ }
+ swig_module.types[i] = 0;
+
+#ifdef SWIGRUNTIME_DEBUG
+ printf("**** SWIG_InitializeModule: Cast List ******\n");
+ for (i = 0; i < swig_module.size; ++i) {
+ int j = 0;
+ swig_cast_info *cast = swig_module.cast_initial[i];
+ printf("SWIG_InitializeModule: type %d %s\n", i, swig_module.type_initial[i]->name);
+ while (cast->type) {
+ printf("SWIG_InitializeModule: cast type %s\n", cast->type->name);
+ cast++;
+ ++j;
+ }
+ printf("---- Total casts: %d\n",j);
+ }
+ printf("**** SWIG_InitializeModule: Cast List ******\n");
+#endif
+}
+
+/* This function will propagate the clientdata field of type to
+* any new swig_type_info structures that have been added into the list
+* of equivalent types. It is like calling
+* SWIG_TypeClientData(type, clientdata) a second time.
+*/
+SWIGRUNTIME void
+SWIG_PropagateClientData(void) {
+ size_t i;
+ swig_cast_info *equiv;
+ static int init_run = 0;
+
+ if (init_run) return;
+ init_run = 1;
+
+ for (i = 0; i < swig_module.size; i++) {
+ if (swig_module.types[i]->clientdata) {
+ equiv = swig_module.types[i]->cast;
+ while (equiv) {
+ if (!equiv->converter) {
+ if (equiv->type && !equiv->type->clientdata)
+ SWIG_TypeClientData(equiv->type, swig_module.types[i]->clientdata);
+ }
+ equiv = equiv->next;
+ }
+ }
+ }
+}
+
+#ifdef __cplusplus
+#if 0
+{
+ /* c-mode */
+#endif
+}
+#endif
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /* Python-specific SWIG API */
+#define SWIG_newvarlink() SWIG_Python_newvarlink()
+#define SWIG_addvarlink(p, name, get_attr, set_attr) SWIG_Python_addvarlink(p, name, get_attr, set_attr)
+#define SWIG_InstallConstants(d, constants) SWIG_Python_InstallConstants(d, constants)
+
+ /* -----------------------------------------------------------------------------
+ * global variable support code.
+ * ----------------------------------------------------------------------------- */
+
+ typedef struct swig_globalvar {
+ char *name; /* Name of global variable */
+ PyObject *(*get_attr)(void); /* Return the current value */
+ int (*set_attr)(PyObject *); /* Set the value */
+ struct swig_globalvar *next;
+ } swig_globalvar;
+
+ typedef struct swig_varlinkobject {
+ PyObject_HEAD
+ swig_globalvar *vars;
+ } swig_varlinkobject;
+
+ SWIGINTERN PyObject *
+ swig_varlink_repr(swig_varlinkobject *SWIGUNUSEDPARM(v)) {
+ return PyString_FromString("<Swig global variables>");
+ }
+
+ SWIGINTERN PyObject *
+ swig_varlink_str(swig_varlinkobject *v) {
+ PyObject *str = PyString_FromString("(");
+ swig_globalvar *var;
+ for (var = v->vars; var; var=var->next) {
+ PyString_ConcatAndDel(&str,PyString_FromString(var->name));
+ if (var->next) PyString_ConcatAndDel(&str,PyString_FromString(", "));
+ }
+ PyString_ConcatAndDel(&str,PyString_FromString(")"));
+ return str;
+ }
+
+ SWIGINTERN int
+ swig_varlink_print(swig_varlinkobject *v, FILE *fp, int SWIGUNUSEDPARM(flags)) {
+ PyObject *str = swig_varlink_str(v);
+ fprintf(fp,"Swig global variables ");
+ fprintf(fp,"%s\n", PyString_AsString(str));
+ Py_DECREF(str);
+ return 0;
+ }
+
+ SWIGINTERN void
+ swig_varlink_dealloc(swig_varlinkobject *v) {
+ swig_globalvar *var = v->vars;
+ while (var) {
+ swig_globalvar *n = var->next;
+ free(var->name);
+ free(var);
+ var = n;
+ }
+ }
+
+ SWIGINTERN PyObject *
+ swig_varlink_getattr(swig_varlinkobject *v, char *n) {
+ PyObject *res = NULL;
+ swig_globalvar *var = v->vars;
+ while (var) {
+ if (strcmp(var->name,n) == 0) {
+ res = (*var->get_attr)();
+ break;
+ }
+ var = var->next;
+ }
+ if (res == NULL && !PyErr_Occurred()) {
+ PyErr_SetString(PyExc_NameError,"Unknown C global variable");
+ }
+ return res;
+ }
+
+ SWIGINTERN int
+ swig_varlink_setattr(swig_varlinkobject *v, char *n, PyObject *p) {
+ int res = 1;
+ swig_globalvar *var = v->vars;
+ while (var) {
+ if (strcmp(var->name,n) == 0) {
+ res = (*var->set_attr)(p);
+ break;
+ }
+ var = var->next;
+ }
+ if (res == 1 && !PyErr_Occurred()) {
+ PyErr_SetString(PyExc_NameError,"Unknown C global variable");
+ }
+ return res;
+ }
+
+ SWIGINTERN PyTypeObject*
+ swig_varlink_type(void) {
+ static char varlink__doc__[] = "Swig var link object";
+ static PyTypeObject varlink_type;
+ static int type_init = 0;
+ if (!type_init) {
+ const PyTypeObject tmp
+ = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* Number of items in variable part (ob_size) */
+ (char *)"swigvarlink", /* Type name (tp_name) */
+ sizeof(swig_varlinkobject), /* Basic size (tp_basicsize) */
+ 0, /* Itemsize (tp_itemsize) */
+ (destructor) swig_varlink_dealloc, /* Deallocator (tp_dealloc) */
+ (printfunc) swig_varlink_print, /* Print (tp_print) */
+ (getattrfunc) swig_varlink_getattr, /* get attr (tp_getattr) */
+ (setattrfunc) swig_varlink_setattr, /* Set attr (tp_setattr) */
+ 0, /* tp_compare */
+ (reprfunc) swig_varlink_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ (reprfunc)swig_varlink_str, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ 0, /* tp_flags */
+ varlink__doc__, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+#if PY_VERSION_HEX >= 0x02020000
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* tp_iter -> tp_weaklist */
+#endif
+#if PY_VERSION_HEX >= 0x02030000
+ 0, /* tp_del */
+#endif
+#ifdef COUNT_ALLOCS
+ 0,0,0,0 /* tp_alloc -> tp_next */
+#endif
+ };
+ varlink_type = tmp;
+ varlink_type.ob_type = &PyType_Type;
+ type_init = 1;
+ }
+ return &varlink_type;
+ }
+
+ /* Create a variable linking object for use later */
+ SWIGINTERN PyObject *
+ SWIG_Python_newvarlink(void) {
+ swig_varlinkobject *result = PyObject_NEW(swig_varlinkobject, swig_varlink_type());
+ if (result) {
+ result->vars = 0;
+ }
+ return ((PyObject*) result);
+ }
+
+ SWIGINTERN void
+ SWIG_Python_addvarlink(PyObject *p, char *name, PyObject *(*get_attr)(void), int (*set_attr)(PyObject *p)) {
+ swig_varlinkobject *v = (swig_varlinkobject *) p;
+ swig_globalvar *gv = (swig_globalvar *) malloc(sizeof(swig_globalvar));
+ if (gv) {
+ size_t size = strlen(name)+1;
+ gv->name = (char *)malloc(size);
+ if (gv->name) {
+ strncpy(gv->name,name,size);
+ gv->get_attr = get_attr;
+ gv->set_attr = set_attr;
+ gv->next = v->vars;
+ }
+ }
+ v->vars = gv;
+ }
+
+ SWIGINTERN PyObject *
+ SWIG_globals(void) {
+ static PyObject *_SWIG_globals = 0;
+ if (!_SWIG_globals) _SWIG_globals = SWIG_newvarlink();
+ return _SWIG_globals;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * constants/methods manipulation
+ * ----------------------------------------------------------------------------- */
+
+ /* Install Constants */
+ SWIGINTERN void
+ SWIG_Python_InstallConstants(PyObject *d, swig_const_info constants[]) {
+ PyObject *obj = 0;
+ size_t i;
+ for (i = 0; constants[i].type; ++i) {
+ switch(constants[i].type) {
+ case SWIG_PY_POINTER:
+ obj = SWIG_NewPointerObj(constants[i].pvalue, *(constants[i]).ptype,0);
+ break;
+ case SWIG_PY_BINARY:
+ obj = SWIG_NewPackedObj(constants[i].pvalue, constants[i].lvalue, *(constants[i].ptype));
+ break;
+ default:
+ obj = 0;
+ break;
+ }
+ if (obj) {
+ PyDict_SetItemString(d, constants[i].name, obj);
+ Py_DECREF(obj);
+ }
+ }
+ }
+
+ /* -----------------------------------------------------------------------------*/
+ /* Fix SwigMethods to carry the callback ptrs when needed */
+ /* -----------------------------------------------------------------------------*/
+
+ SWIGINTERN void
+ SWIG_Python_FixMethods(PyMethodDef *methods,
+ swig_const_info *const_table,
+ swig_type_info **types,
+ swig_type_info **types_initial) {
+ size_t i;
+ for (i = 0; methods[i].ml_name; ++i) {
+ const char *c = methods[i].ml_doc;
+ if (c && (c = strstr(c, "swig_ptr: "))) {
+ int j;
+ swig_const_info *ci = 0;
+ const char *name = c + 10;
+ for (j = 0; const_table[j].type; ++j) {
+ if (strncmp(const_table[j].name, name,
+ strlen(const_table[j].name)) == 0) {
+ ci = &(const_table[j]);
+ break;
+ }
+ }
+ if (ci) {
+ size_t shift = (ci->ptype) - types;
+ swig_type_info *ty = types_initial[shift];
+ size_t ldoc = (c - methods[i].ml_doc);
+ size_t lptr = strlen(ty->name)+2*sizeof(void*)+2;
+ char *ndoc = (char*)malloc(ldoc + lptr + 10);
+ if (ndoc) {
+ char *buff = ndoc;
+ void *ptr = (ci->type == SWIG_PY_POINTER) ? ci->pvalue : 0;
+ if (ptr) {
+ strncpy(buff, methods[i].ml_doc, ldoc);
+ buff += ldoc;
+ strncpy(buff, "swig_ptr: ", 10);
+ buff += 10;
+ SWIG_PackVoidPtr(buff, ptr, ty->name, lptr);
+ methods[i].ml_doc = ndoc;
+ }
+ }
+ }
+ }
+ }
+ }
+
+#ifdef __cplusplus
+}
+#endif
+
+/* -----------------------------------------------------------------------------*
+ * Partial Init method
+ * -----------------------------------------------------------------------------*/
+
+#ifdef __cplusplus
+extern "C"
+#endif
+SWIGEXPORT void SWIG_init(void) {
+ PyObject *m, *d;
+
+ /* Fix SwigMethods to carry the callback ptrs when needed */
+ SWIG_Python_FixMethods(SwigMethods, swig_const_table, swig_types, swig_type_initial);
+
+ m = Py_InitModule((char *) SWIG_name, SwigMethods);
+ d = PyModule_GetDict(m);
+
+ SWIG_InitializeModule(0);
+ SWIG_InstallConstants(d,swig_const_table);
+
+
+
+
+ mod_win32_client = PyImport_ImportModule("win32com.client");
+ mod_pywintypes = PyImport_ImportModule("pywintypes");
+ ComError = PyObject_GetAttrString(mod_pywintypes, "com_error");
+
+ wmi_init(&com_ctx, NULL);
+ {
+ PyObject *pModule;
+
+ pModule = PyImport_ImportModule( "win32com.client" );
+ }
+
+}
+
diff --git a/source4/lib/wmi/wmicore.c b/source4/lib/wmi/wmicore.c
new file mode 100644
index 0000000..dc9fee4
--- /dev/null
+++ b/source4/lib/wmi/wmicore.c
@@ -0,0 +1,253 @@
+/*
+ WMI Sample client
+ Copyright (C) 2006 Andrzej Hajda <andrzej.hajda@wp.pl>
+ Copyright (C) 2008 Jelmer Vernooij <jelmer@samba.org>
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "auth/credentials/credentials.h"
+#include "librpc/gen_ndr/com_dcom.h"
+#include "lib/com/dcom/dcom.h"
+#include "librpc/gen_ndr/wmi.h"
+#include "librpc/gen_ndr/com_wmi.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/ndr/ndr_table.h"
+
+struct IWbemServices;
+struct IWbemContext;
+
+#define WERR_CHECK(msg) if (!W_ERROR_IS_OK(result)) { \
+ DEBUG(0, ("ERROR: %s\n", msg)); \
+ goto end; \
+ } else { \
+ DEBUG(1, ("OK : %s\n", msg)); \
+ }
+
+void wmi_init(struct com_context **ctx, struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx)
+{
+ dcerpc_init(lp_ctx);
+ ndr_table_init();
+
+ /* FIXME: Register DCOM proxies? */
+
+ com_init_ctx(ctx, NULL);
+ dcom_client_init(*ctx, credentials);
+}
+
+/** FIXME: Use credentials struct rather than user/password here */
+WERROR WBEM_ConnectServer(struct com_context *ctx, const char *server, const uint16_t *nspace,
+ struct cli_credentials *credentials,
+ const char *locale, uint32_t flags, const char *authority,
+ struct IWbemContext* wbem_ctx, struct IWbemServices** services)
+{
+ struct GUID clsid;
+ struct GUID iid;
+ WERROR result;
+ HRESULT coresult;
+ struct IUnknown **mqi;
+ struct IWbemLevel1Login *pL;
+
+ GUID_from_string(CLSID_WBEMLEVEL1LOGIN, &clsid);
+ GUID_from_string(COM_IWBEMLEVEL1LOGIN_UUID, &iid);
+ result = dcom_create_object(ctx, &clsid, server, 1, &iid, &mqi, &coresult);
+ WERR_CHECK("dcom_create_object.");
+ result = coresult;
+ WERR_CHECK("Create remote WMI object.");
+ pL = (struct IWbemLevel1Login *)mqi[0];
+ talloc_free(mqi);
+
+ result = IWbemLevel1Login_NTLMLogin(pL, ctx, nspace, locale, flags, wbem_ctx, services);
+ WERR_CHECK("Login to remote object.");
+
+ IUnknown_Release((struct IUnknown *)pL, ctx);
+end:
+ return result;
+}
+
+struct werror_code_struct {
+ const char *dos_errstr;
+ WERROR werror;
+};
+
+static const struct werror_code_struct wmi_errs[] =
+{
+ { "RPC_S_CALL_FAILED", W_ERROR(RPC_S_CALL_FAILED) },
+
+ { "WBEM_NO_ERROR", W_ERROR(WBEM_NO_ERROR) },
+ { "WBEM_S_NO_ERROR", W_ERROR(WBEM_S_NO_ERROR) },
+ { "WBEM_S_SAME", W_ERROR(WBEM_S_SAME) },
+ { "WBEM_S_FALSE", W_ERROR(WBEM_S_FALSE) },
+ { "WBEM_S_ALREADY_EXISTS", W_ERROR(WBEM_S_ALREADY_EXISTS) },
+ { "WBEM_S_RESET_TO_DEFAULT", W_ERROR(WBEM_S_RESET_TO_DEFAULT) },
+ { "WBEM_S_DIFFERENT", W_ERROR(WBEM_S_DIFFERENT) },
+ { "WBEM_S_TIMEDOUT", W_ERROR(WBEM_S_TIMEDOUT) },
+ { "WBEM_S_NO_MORE_DATA", W_ERROR(WBEM_S_NO_MORE_DATA) },
+ { "WBEM_S_OPERATION_CANCELLED", W_ERROR(WBEM_S_OPERATION_CANCELLED) },
+ { "WBEM_S_PENDING", W_ERROR(WBEM_S_PENDING) },
+ { "WBEM_S_DUPLICATE_OBJECTS", W_ERROR(WBEM_S_DUPLICATE_OBJECTS) },
+ { "WBEM_S_ACCESS_DENIED", W_ERROR(WBEM_S_ACCESS_DENIED) },
+ { "WBEM_S_PARTIAL_RESULTS", W_ERROR(WBEM_S_PARTIAL_RESULTS) },
+ { "WBEM_S_NO_POSTHOOK", W_ERROR(WBEM_S_NO_POSTHOOK) },
+ { "WBEM_S_POSTHOOK_WITH_BOTH", W_ERROR(WBEM_S_POSTHOOK_WITH_BOTH) },
+ { "WBEM_S_POSTHOOK_WITH_NEW", W_ERROR(WBEM_S_POSTHOOK_WITH_NEW) },
+ { "WBEM_S_POSTHOOK_WITH_STATUS", W_ERROR(WBEM_S_POSTHOOK_WITH_STATUS) },
+ { "WBEM_S_POSTHOOK_WITH_OLD", W_ERROR(WBEM_S_POSTHOOK_WITH_OLD) },
+ { "WBEM_S_REDO_PREHOOK_WITH_ORIGINAL_OBJECT", W_ERROR(WBEM_S_REDO_PREHOOK_WITH_ORIGINAL_OBJECT) },
+ { "WBEM_S_SOURCE_NOT_AVAILABLE", W_ERROR(WBEM_S_SOURCE_NOT_AVAILABLE) },
+ { "WBEM_E_FAILED", W_ERROR(WBEM_E_FAILED) },
+ { "WBEM_E_NOT_FOUND", W_ERROR(WBEM_E_NOT_FOUND) },
+ { "WBEM_E_ACCESS_DENIED", W_ERROR(WBEM_E_ACCESS_DENIED) },
+ { "WBEM_E_PROVIDER_FAILURE", W_ERROR(WBEM_E_PROVIDER_FAILURE) },
+ { "WBEM_E_TYPE_MISMATCH", W_ERROR(WBEM_E_TYPE_MISMATCH) },
+ { "WBEM_E_OUT_OF_MEMORY", W_ERROR(WBEM_E_OUT_OF_MEMORY) },
+ { "WBEM_E_INVALID_CONTEXT", W_ERROR(WBEM_E_INVALID_CONTEXT) },
+ { "WBEM_E_INVALID_PARAMETER", W_ERROR(WBEM_E_INVALID_PARAMETER) },
+ { "WBEM_E_NOT_AVAILABLE", W_ERROR(WBEM_E_NOT_AVAILABLE) },
+ { "WBEM_E_CRITICAL_ERROR", W_ERROR(WBEM_E_CRITICAL_ERROR) },
+ { "WBEM_E_INVALID_STREAM", W_ERROR(WBEM_E_INVALID_STREAM) },
+ { "WBEM_E_NOT_SUPPORTED", W_ERROR(WBEM_E_NOT_SUPPORTED) },
+ { "WBEM_E_INVALID_SUPERCLASS", W_ERROR(WBEM_E_INVALID_SUPERCLASS) },
+ { "WBEM_E_INVALID_NAMESPACE", W_ERROR(WBEM_E_INVALID_NAMESPACE) },
+ { "WBEM_E_INVALID_OBJECT", W_ERROR(WBEM_E_INVALID_OBJECT) },
+ { "WBEM_E_INVALID_CLASS", W_ERROR(WBEM_E_INVALID_CLASS) },
+ { "WBEM_E_PROVIDER_NOT_FOUND", W_ERROR(WBEM_E_PROVIDER_NOT_FOUND) },
+ { "WBEM_E_INVALID_PROVIDER_REGISTRATION", W_ERROR(WBEM_E_INVALID_PROVIDER_REGISTRATION) },
+ { "WBEM_E_PROVIDER_LOAD_FAILURE", W_ERROR(WBEM_E_PROVIDER_LOAD_FAILURE) },
+ { "WBEM_E_INITIALIZATION_FAILURE", W_ERROR(WBEM_E_INITIALIZATION_FAILURE) },
+ { "WBEM_E_TRANSPORT_FAILURE", W_ERROR(WBEM_E_TRANSPORT_FAILURE) },
+ { "WBEM_E_INVALID_OPERATION", W_ERROR(WBEM_E_INVALID_OPERATION) },
+ { "WBEM_E_INVALID_QUERY", W_ERROR(WBEM_E_INVALID_QUERY) },
+ { "WBEM_E_INVALID_QUERY_TYPE", W_ERROR(WBEM_E_INVALID_QUERY_TYPE) },
+ { "WBEM_E_ALREADY_EXISTS", W_ERROR(WBEM_E_ALREADY_EXISTS) },
+ { "WBEM_E_OVERRIDE_NOT_ALLOWED", W_ERROR(WBEM_E_OVERRIDE_NOT_ALLOWED) },
+ { "WBEM_E_PROPAGATED_QUALIFIER", W_ERROR(WBEM_E_PROPAGATED_QUALIFIER) },
+ { "WBEM_E_PROPAGATED_PROPERTY", W_ERROR(WBEM_E_PROPAGATED_PROPERTY) },
+ { "WBEM_E_UNEXPECTED", W_ERROR(WBEM_E_UNEXPECTED) },
+ { "WBEM_E_ILLEGAL_OPERATION", W_ERROR(WBEM_E_ILLEGAL_OPERATION) },
+ { "WBEM_E_CANNOT_BE_KEY", W_ERROR(WBEM_E_CANNOT_BE_KEY) },
+ { "WBEM_E_INCOMPLETE_CLASS", W_ERROR(WBEM_E_INCOMPLETE_CLASS) },
+ { "WBEM_E_INVALID_SYNTAX", W_ERROR(WBEM_E_INVALID_SYNTAX) },
+ { "WBEM_E_NONDECORATED_OBJECT", W_ERROR(WBEM_E_NONDECORATED_OBJECT) },
+ { "WBEM_E_READ_ONLY", W_ERROR(WBEM_E_READ_ONLY) },
+ { "WBEM_E_PROVIDER_NOT_CAPABLE", W_ERROR(WBEM_E_PROVIDER_NOT_CAPABLE) },
+ { "WBEM_E_CLASS_HAS_CHILDREN", W_ERROR(WBEM_E_CLASS_HAS_CHILDREN) },
+ { "WBEM_E_CLASS_HAS_INSTANCES", W_ERROR(WBEM_E_CLASS_HAS_INSTANCES) },
+ { "WBEM_E_QUERY_NOT_IMPLEMENTED", W_ERROR(WBEM_E_QUERY_NOT_IMPLEMENTED) },
+ { "WBEM_E_ILLEGAL_NULL", W_ERROR(WBEM_E_ILLEGAL_NULL) },
+ { "WBEM_E_INVALID_QUALIFIER_TYPE", W_ERROR(WBEM_E_INVALID_QUALIFIER_TYPE) },
+ { "WBEM_E_INVALID_PROPERTY_TYPE", W_ERROR(WBEM_E_INVALID_PROPERTY_TYPE) },
+ { "WBEM_E_VALUE_OUT_OF_RANGE", W_ERROR(WBEM_E_VALUE_OUT_OF_RANGE) },
+ { "WBEM_E_CANNOT_BE_SINGLETON", W_ERROR(WBEM_E_CANNOT_BE_SINGLETON) },
+ { "WBEM_E_INVALID_CIM_TYPE", W_ERROR(WBEM_E_INVALID_CIM_TYPE) },
+ { "WBEM_E_INVALID_METHOD", W_ERROR(WBEM_E_INVALID_METHOD) },
+ { "WBEM_E_INVALID_METHOD_PARAMETERS", W_ERROR(WBEM_E_INVALID_METHOD_PARAMETERS) },
+ { "WBEM_E_SYSTEM_PROPERTY", W_ERROR(WBEM_E_SYSTEM_PROPERTY) },
+ { "WBEM_E_INVALID_PROPERTY", W_ERROR(WBEM_E_INVALID_PROPERTY) },
+ { "WBEM_E_CALL_CANCELLED", W_ERROR(WBEM_E_CALL_CANCELLED) },
+ { "WBEM_E_SHUTTING_DOWN", W_ERROR(WBEM_E_SHUTTING_DOWN) },
+ { "WBEM_E_PROPAGATED_METHOD", W_ERROR(WBEM_E_PROPAGATED_METHOD) },
+ { "WBEM_E_UNSUPPORTED_PARAMETER", W_ERROR(WBEM_E_UNSUPPORTED_PARAMETER) },
+ { "WBEM_E_MISSING_PARAMETER_ID", W_ERROR(WBEM_E_MISSING_PARAMETER_ID) },
+ { "WBEM_E_INVALID_PARAMETER_ID", W_ERROR(WBEM_E_INVALID_PARAMETER_ID) },
+ { "WBEM_E_NONCONSECUTIVE_PARAMETER_IDS", W_ERROR(WBEM_E_NONCONSECUTIVE_PARAMETER_IDS) },
+ { "WBEM_E_PARAMETER_ID_ON_RETVAL", W_ERROR(WBEM_E_PARAMETER_ID_ON_RETVAL) },
+ { "WBEM_E_INVALID_OBJECT_PATH", W_ERROR(WBEM_E_INVALID_OBJECT_PATH) },
+ { "WBEM_E_OUT_OF_DISK_SPACE", W_ERROR(WBEM_E_OUT_OF_DISK_SPACE) },
+ { "WBEM_E_BUFFER_TOO_SMALL", W_ERROR(WBEM_E_BUFFER_TOO_SMALL) },
+ { "WBEM_E_UNSUPPORTED_PUT_EXTENSION", W_ERROR(WBEM_E_UNSUPPORTED_PUT_EXTENSION) },
+ { "WBEM_E_UNKNOWN_OBJECT_TYPE", W_ERROR(WBEM_E_UNKNOWN_OBJECT_TYPE) },
+ { "WBEM_E_UNKNOWN_PACKET_TYPE", W_ERROR(WBEM_E_UNKNOWN_PACKET_TYPE) },
+ { "WBEM_E_MARSHAL_VERSION_MISMATCH", W_ERROR(WBEM_E_MARSHAL_VERSION_MISMATCH) },
+ { "WBEM_E_MARSHAL_INVALID_SIGNATURE", W_ERROR(WBEM_E_MARSHAL_INVALID_SIGNATURE) },
+ { "WBEM_E_INVALID_QUALIFIER", W_ERROR(WBEM_E_INVALID_QUALIFIER) },
+ { "WBEM_E_INVALID_DUPLICATE_PARAMETER", W_ERROR(WBEM_E_INVALID_DUPLICATE_PARAMETER) },
+ { "WBEM_E_TOO_MUCH_DATA", W_ERROR(WBEM_E_TOO_MUCH_DATA) },
+ { "WBEM_E_SERVER_TOO_BUSY", W_ERROR(WBEM_E_SERVER_TOO_BUSY) },
+ { "WBEM_E_INVALID_FLAVOR", W_ERROR(WBEM_E_INVALID_FLAVOR) },
+ { "WBEM_E_CIRCULAR_REFERENCE", W_ERROR(WBEM_E_CIRCULAR_REFERENCE) },
+ { "WBEM_E_UNSUPPORTED_CLASS_UPDATE", W_ERROR(WBEM_E_UNSUPPORTED_CLASS_UPDATE) },
+ { "WBEM_E_CANNOT_CHANGE_KEY_INHERITANCE", W_ERROR(WBEM_E_CANNOT_CHANGE_KEY_INHERITANCE) },
+ { "WBEM_E_CANNOT_CHANGE_INDEX_INHERITANCE", W_ERROR(WBEM_E_CANNOT_CHANGE_INDEX_INHERITANCE) },
+ { "WBEM_E_TOO_MANY_PROPERTIES", W_ERROR(WBEM_E_TOO_MANY_PROPERTIES) },
+ { "WBEM_E_UPDATE_TYPE_MISMATCH", W_ERROR(WBEM_E_UPDATE_TYPE_MISMATCH) },
+ { "WBEM_E_UPDATE_OVERRIDE_NOT_ALLOWED", W_ERROR(WBEM_E_UPDATE_OVERRIDE_NOT_ALLOWED) },
+ { "WBEM_E_UPDATE_PROPAGATED_METHOD", W_ERROR(WBEM_E_UPDATE_PROPAGATED_METHOD) },
+ { "WBEM_E_METHOD_NOT_IMPLEMENTED", W_ERROR(WBEM_E_METHOD_NOT_IMPLEMENTED) },
+ { "WBEM_E_METHOD_DISABLED", W_ERROR(WBEM_E_METHOD_DISABLED) },
+ { "WBEM_E_REFRESHER_BUSY", W_ERROR(WBEM_E_REFRESHER_BUSY) },
+ { "WBEM_E_UNPARSABLE_QUERY", W_ERROR(WBEM_E_UNPARSABLE_QUERY) },
+ { "WBEM_E_NOT_EVENT_CLASS", W_ERROR(WBEM_E_NOT_EVENT_CLASS) },
+ { "WBEM_E_MISSING_GROUP_WITHIN", W_ERROR(WBEM_E_MISSING_GROUP_WITHIN) },
+ { "WBEM_E_MISSING_AGGREGATION_LIST", W_ERROR(WBEM_E_MISSING_AGGREGATION_LIST) },
+ { "WBEM_E_PROPERTY_NOT_AN_OBJECT", W_ERROR(WBEM_E_PROPERTY_NOT_AN_OBJECT) },
+ { "WBEM_E_AGGREGATING_BY_OBJECT", W_ERROR(WBEM_E_AGGREGATING_BY_OBJECT) },
+ { "WBEM_E_UNINTERPRETABLE_PROVIDER_QUERY", W_ERROR(WBEM_E_UNINTERPRETABLE_PROVIDER_QUERY) },
+ { "WBEM_E_BACKUP_RESTORE_WINMGMT_RUNNING", W_ERROR(WBEM_E_BACKUP_RESTORE_WINMGMT_RUNNING) },
+ { "WBEM_E_QUEUE_OVERFLOW", W_ERROR(WBEM_E_QUEUE_OVERFLOW) },
+ { "WBEM_E_PRIVILEGE_NOT_HELD", W_ERROR(WBEM_E_PRIVILEGE_NOT_HELD) },
+ { "WBEM_E_INVALID_OPERATOR", W_ERROR(WBEM_E_INVALID_OPERATOR) },
+ { "WBEM_E_LOCAL_CREDENTIALS", W_ERROR(WBEM_E_LOCAL_CREDENTIALS) },
+ { "WBEM_E_CANNOT_BE_ABSTRACT", W_ERROR(WBEM_E_CANNOT_BE_ABSTRACT) },
+ { "WBEM_E_AMENDED_OBJECT", W_ERROR(WBEM_E_AMENDED_OBJECT) },
+ { "WBEM_E_CLIENT_TOO_SLOW", W_ERROR(WBEM_E_CLIENT_TOO_SLOW) },
+ { "WBEM_E_NULL_SECURITY_DESCRIPTOR", W_ERROR(WBEM_E_NULL_SECURITY_DESCRIPTOR) },
+ { "WBEM_E_TIMED_OUT", W_ERROR(WBEM_E_TIMED_OUT) },
+ { "WBEM_E_INVALID_ASSOCIATION", W_ERROR(WBEM_E_INVALID_ASSOCIATION) },
+ { "WBEM_E_AMBIGUOUS_OPERATION", W_ERROR(WBEM_E_AMBIGUOUS_OPERATION) },
+ { "WBEM_E_QUOTA_VIOLATION", W_ERROR(WBEM_E_QUOTA_VIOLATION) },
+ { "WBEM_E_RESERVED_001", W_ERROR(WBEM_E_RESERVED_001) },
+ { "WBEM_E_RESERVED_002", W_ERROR(WBEM_E_RESERVED_002) },
+ { "WBEM_E_UNSUPPORTED_LOCALE", W_ERROR(WBEM_E_UNSUPPORTED_LOCALE) },
+ { "WBEM_E_HANDLE_OUT_OF_DATE", W_ERROR(WBEM_E_HANDLE_OUT_OF_DATE) },
+ { "WBEM_E_CONNECTION_FAILED", W_ERROR(WBEM_E_CONNECTION_FAILED) },
+ { "WBEM_E_INVALID_HANDLE_REQUEST", W_ERROR(WBEM_E_INVALID_HANDLE_REQUEST) },
+ { "WBEM_E_PROPERTY_NAME_TOO_WIDE", W_ERROR(WBEM_E_PROPERTY_NAME_TOO_WIDE) },
+ { "WBEM_E_CLASS_NAME_TOO_WIDE", W_ERROR(WBEM_E_CLASS_NAME_TOO_WIDE) },
+ { "WBEM_E_METHOD_NAME_TOO_WIDE", W_ERROR(WBEM_E_METHOD_NAME_TOO_WIDE) },
+ { "WBEM_E_QUALIFIER_NAME_TOO_WIDE", W_ERROR(WBEM_E_QUALIFIER_NAME_TOO_WIDE) },
+ { "WBEM_E_RERUN_COMMAND", W_ERROR(WBEM_E_RERUN_COMMAND) },
+ { "WBEM_E_DATABASE_VER_MISMATCH", W_ERROR(WBEM_E_DATABASE_VER_MISMATCH) },
+ { "WBEM_E_VETO_DELETE", W_ERROR(WBEM_E_VETO_DELETE) },
+ { "WBEM_E_VETO_PUT", W_ERROR(WBEM_E_VETO_PUT) },
+ { "WBEM_E_INVALID_LOCALE", W_ERROR(WBEM_E_INVALID_LOCALE) },
+ { "WBEM_E_PROVIDER_SUSPENDED", W_ERROR(WBEM_E_PROVIDER_SUSPENDED) },
+ { "WBEM_E_SYNCHRONIZATION_REQUIRED", W_ERROR(WBEM_E_SYNCHRONIZATION_REQUIRED) },
+ { "WBEM_E_NO_SCHEMA", W_ERROR(WBEM_E_NO_SCHEMA) },
+ { "WBEM_E_PROVIDER_ALREADY_REGISTERED", W_ERROR(WBEM_E_PROVIDER_ALREADY_REGISTERED) },
+ { "WBEM_E_PROVIDER_NOT_REGISTERED", W_ERROR(WBEM_E_PROVIDER_NOT_REGISTERED) },
+ { "WBEM_E_FATAL_TRANSPORT_ERROR", W_ERROR(WBEM_E_FATAL_TRANSPORT_ERROR) },
+ { "WBEM_E_ENCRYPTED_CONNECTION_REQUIRED", W_ERROR(WBEM_E_ENCRYPTED_CONNECTION_REQUIRED) },
+ { "WBEM_E_PROVIDER_TIMED_OUT", W_ERROR(WBEM_E_PROVIDER_TIMED_OUT) },
+ { "WBEM_E_NO_KEY", W_ERROR(WBEM_E_NO_KEY) },
+ { "WBEM_E_PROVIDER_DISABLED", W_ERROR(WBEM_E_PROVIDER_DISABLED) },
+ { NULL, W_ERROR(0) }
+};
+
+const char *wmi_errstr(WERROR werror)
+{
+ int idx = 0;
+
+ while (wmi_errs[idx].dos_errstr != NULL) {
+ if (W_ERROR_V(wmi_errs[idx].werror) ==
+ W_ERROR_V(werror))
+ return wmi_errs[idx].dos_errstr;
+ idx++;
+ }
+
+ return win_errstr(werror);
+}
diff --git a/source4/lib/wmi/wscript_build b/source4/lib/wmi/wscript_build
new file mode 100644
index 0000000..59e0ce5
--- /dev/null
+++ b/source4/lib/wmi/wscript_build
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+
+
+bld.SAMBA_SUBSYSTEM('WMI',
+ source='wmicore.c wbemdata.c ../../../librpc/gen_ndr/dcom_p.c',
+ public_deps='RPC_NDR_OXIDRESOLVER NDR_DCOM RPC_NDR_REMACT ndr_table DCOM_PROXY_DCOM DCOM'
+ )
+
+
+bld.SAMBA_BINARY('wmic',
+ source='tools/wmic.c',
+ deps='POPT_SAMBA POPT_CREDENTIALS popt WMI'
+ )
+
+
+bld.SAMBA_BINARY('wmis',
+ source='tools/wmis.c',
+ deps='POPT_SAMBA POPT_CREDENTIALS popt WMI'
+ )
+
+
+bld.SAMBA_PYTHON('pywmi',
+ source='wmi_wrap.c',
+ public_deps='LIBCLI_SMB ndr samba-util samba-config WMI'
+ )
+