summaryrefslogtreecommitdiffstats
path: root/source4/lib
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
commit8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch)
tree4099e8021376c7d8c05bdf8503093d80e9c7bad0 /source4/lib
parentInitial commit. (diff)
downloadsamba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz
samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source4/lib')
-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.h87
-rw-r--r--source4/lib/messaging/messaging.c1521
-rw-r--r--source4/lib/messaging/messaging.h72
-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.c576
-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.c723
-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.c330
-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.h531
-rw-r--r--source4/lib/registry/rpc.c578
-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.c182
-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.c614
-rw-r--r--source4/lib/stream/packet.h65
-rw-r--r--source4/lib/stream/wscript_build8
-rw-r--r--source4/lib/tls/tls.h105
-rw-r--r--source4/lib/tls/tls_tstream.c1550
-rw-r--r--source4/lib/tls/tlscert.c159
-rw-r--r--source4/lib/tls/wscript_build15
75 files changed, 24063 insertions, 0 deletions
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..36a4d32
--- /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 = samba_tevent_context_init(mem_ctx);
+ 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..d6a5c46
--- /dev/null
+++ b/source4/lib/messaging/irpc.h
@@ -0,0 +1,87 @@
+/*
+ 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 "libcli/util/werror.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..6d859f7
--- /dev/null
+++ b/source4/lib/messaging/messaging.c
@@ -0,0 +1,1521 @@
+/*
+ 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>
+#include "lib/util/idtree.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. Return the number of
+ functions deregistered.
+*/
+size_t imessaging_deregister(struct imessaging_context *msg, uint32_t msg_type, void *private_data)
+{
+ struct dispatch_fn *d, *next;
+ size_t removed = 0;
+
+ if (msg_type >= msg->num_types) {
+ d = (struct dispatch_fn *)idr_find(msg->dispatch_tree,
+ msg_type);
+ if (!d) return 0;
+ idr_remove(msg->dispatch_tree, msg_type);
+ talloc_free(d);
+ return 1;
+ }
+
+ 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);
+ ++removed;
+ }
+ }
+
+ return removed;
+}
+
+/*
+*/
+int imessaging_cleanup(struct imessaging_context *msg)
+{
+ 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 void irpc_bh_do_ndr_print(struct dcerpc_binding_handle *h,
+ ndr_flags_type ndr_flags,
+ const void *_struct_ptr,
+ const struct ndr_interface_call *call)
+{
+ void *struct_ptr = discard_const(_struct_ptr);
+ bool print_in = false;
+ bool print_out = false;
+
+ if (DEBUGLEVEL >= 11) {
+ print_in = true;
+ print_out = true;
+ }
+
+ if (ndr_flags & NDR_IN) {
+ if (print_in) {
+ ndr_print_function_debug(call->ndr_print,
+ call->name,
+ ndr_flags,
+ struct_ptr);
+ }
+ }
+ if (ndr_flags & NDR_OUT) {
+ if (print_out) {
+ ndr_print_function_debug(call->ndr_print,
+ call->name,
+ ndr_flags,
+ struct_ptr);
+ }
+ }
+}
+
+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,
+ .do_ndr_print = irpc_bh_do_ndr_print,
+};
+
+/* 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..76b99ca
--- /dev/null
+++ b/source4/lib/messaging/messaging.h
@@ -0,0 +1,72 @@
+/*
+ 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 "lib/util/data_blob.h"
+#include "librpc/gen_ndr/messaging.h"
+
+struct loadparm_context;
+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);
+size_t 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..6b34306
--- /dev/null
+++ b/source4/lib/messaging/pymessaging.c
@@ -0,0 +1,576 @@
+/*
+ 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 "lib/replace/system/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)
+{
+ Py_ssize_t tuple_size;
+
+ 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;
+ }
+
+ tuple_size = PyTuple_Size(object);
+ if (tuple_size == 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 (tuple_size == 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 if (tuple_size == 1) {
+ 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;
+ } else {
+ PyErr_SetString(PyExc_ValueError, "Expected tuple containing one, two, or three elements");
+ return false;
+ }
+}
+
+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",
+ 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, "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)) {
+ talloc_free(ret->mem_ctx);
+ 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, "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;
+ PyObject *result = NULL;
+
+ struct server_id *p_server_id = NULL;
+
+ if (num_fds != 0) {
+ DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
+ return;
+ }
+
+ p_server_id = talloc(NULL, struct server_id);
+ if (!p_server_id) {
+ PyErr_NoMemory();
+ return;
+ }
+ *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);
+ if (py_server_id == NULL) {
+ return;
+ }
+
+ if (!PyArg_ParseTuple(callback_and_tuple, "OO",
+ &callback,
+ &py_private)) {
+ return;
+ }
+
+ result = PyObject_CallFunction(callback, discard_const_p(char, "OiOs#"),
+ py_private,
+ msg_type,
+ py_server_id,
+ data->data, data->length);
+ Py_XDECREF(result);
+}
+
+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 tuple 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)) {
+ Py_DECREF(callback_and_context);
+ 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 };
+ size_t removed;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|i:deregister",
+ discard_const_p(char *, kwnames), &callback, &msg_type)) {
+ return NULL;
+ }
+
+ removed = imessaging_deregister(iface->msg_ctx, msg_type, callback);
+ while (removed-- > 0) {
+ Py_DECREF(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) {
+ TALLOC_FREE(mem_ctx);
+ 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) {
+ TALLOC_FREE(mem_ctx);
+ 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) {
+ TALLOC_FREE(mem_ctx);
+ PyErr_NoMemory();
+ 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) {
+ TALLOC_FREE(mem_ctx);
+ 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 at 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, lp_ctx=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..bac4ae7
--- /dev/null
+++ b/source4/lib/policy/gp_filesys.c
@@ -0,0 +1,723 @@
+/*
+ * 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/dir.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;
+ char buf[4096];
+ ssize_t nread, total_read;
+ ssize_t nwrite, total_write;
+ struct stat s;
+ NTSTATUS status;
+
+ dir = opendir(local_path);
+ if (!dir) {
+ DEBUG(0, ("Failed to open directory: %s\n", local_path));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ while ((dirent = readdir(dir)) != NULL) {
+ if (ISDOT(dirent->d_name) || ISDOTDOT(dirent->d_name)) {
+ 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));
+ status = smbcli_mkdir(gp_ctx->cli->tree,
+ entry_remote_path);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (depth < GP_MAX_DEPTH) {
+ status = push_recursive(gp_ctx,
+ entry_local_path,
+ entry_remote_path,
+ depth + 1);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ }
+ } 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;
+ total_write = 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;
+ goto done;
+ }
+ nwrite = smbcli_write(gp_ctx->cli->tree,
+ remote_fd, 0, buf,
+ total_read, nread);
+ if (nwrite < 0) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ total_read += nread;
+ total_write += nwrite;
+ }
+ if (total_read != total_write) {
+ /* Weird and should not happen */
+ status = NT_STATUS_UNEXPECTED_IO_ERROR;
+ goto done;
+ }
+
+ 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..d8feadb
--- /dev/null
+++ b/source4/lib/policy/gp_manage.c
@@ -0,0 +1,330 @@
+/*
+ * 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"));
+ TALLOC_FREE(fs_sd);
+ 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..f85ea85
--- /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 "lib/replace/system/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..de40d2a
--- /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 "lib/replace/system/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..d0fc2a9
--- /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.\n"));
+ 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.\n"));
+ 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..84173cb
--- /dev/null
+++ b/source4/lib/registry/registry.h
@@ -0,0 +1,531 @@
+/*
+ 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 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..a0c959f
--- /dev/null
+++ b/source4/lib/registry/rpc.c
@@ -0,0 +1,578 @@
+/*
+ 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 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..35f968a
--- /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\\Software 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..7fd5bd2
--- /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, 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..977a974
--- /dev/null
+++ b/source4/lib/registry/tools/regdiff.c
@@ -0,0 +1,182 @@
+/*
+ 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, 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..ef54029
--- /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 consistent */
+ 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 maximum 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..ba2c17e
--- /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
+ * consistent, 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..6f4bfaa
--- /dev/null
+++ b/source4/lib/stream/packet.c
@@ -0,0 +1,614 @@
+/*
+ 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 successful LDAP bind reply.
+*/
+_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;
+}
diff --git a/source4/lib/stream/packet.h b/source4/lib/stream/packet.h
new file mode 100644
index 0000000..afd0591
--- /dev/null
+++ b/source4/lib/stream/packet.h
@@ -0,0 +1,65 @@
+/*
+ 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);
+
+
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..5e0c56b
--- /dev/null
+++ b/source4/lib/tls/tls_tstream.c
@@ -0,0 +1,1550 @@
+/*
+ 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_internal {
+ 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;
+};
+
+struct tstream_tls_params {
+ struct tstream_tls_params_internal *internal;
+};
+
+static int tstream_tls_params_internal_destructor(struct tstream_tls_params_internal *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 *tls_params)
+{
+ struct tstream_tls_params_internal *tlsp = tls_params->internal;
+
+ 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 = NULL;
+ struct tstream_tls_params_internal *tlsp = NULL;
+ int ret;
+
+ __tlsp = talloc_zero(mem_ctx, struct tstream_tls_params);
+ if (__tlsp == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ tlsp = talloc_zero(__tlsp, struct tstream_tls_params_internal);
+ if (tlsp == NULL) {
+ TALLOC_FREE(__tlsp);
+ return NT_STATUS_NO_MEMORY;
+ }
+ talloc_set_destructor(tlsp, tstream_tls_params_internal_destructor);
+ __tlsp->internal = tlsp;
+
+ 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;
+ struct tstream_tls_params_internal *tls_params = NULL;
+ int ret;
+ unsigned int flags = GNUTLS_CLIENT;
+
+ 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);
+
+ /*
+ * Note we need to make sure x509_cred and dh_params
+ * from tstream_tls_params_internal stay alive for
+ * the whole lifetime of this session!
+ *
+ * See 'man gnutls_credentials_set' and
+ * 'man gnutls_certificate_set_dh_params'.
+ *
+ * Note: here we use talloc_reference() in a way
+ * that does not expose it to the caller.
+ *
+ */
+ tls_params = talloc_reference(tlss, _tls_params->internal);
+ if (tevent_req_nomem(tls_params, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ 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);
+ }
+
+#ifdef GNUTLS_NO_TICKETS
+ /*
+ * tls_tstream can't properly handle 'New Session Ticket' messages
+ * sent 'after' the client sends the 'Finished' message.
+ * GNUTLS_NO_TICKETS was introduced in GnuTLS 3.5.6. This flag is to
+ * indicate the session Flag session should not use resumption with
+ * session tickets.
+ */
+ flags |= GNUTLS_NO_TICKETS;
+#endif
+
+ ret = gnutls_init(&tlss->tls_session, flags);
+ 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 = NULL;
+ struct tstream_tls_params_internal *tlsp = NULL;
+ int ret;
+ struct stat st;
+
+ if (!enabled || key_file == NULL || *key_file == 0) {
+ __tlsp = talloc_zero(mem_ctx, struct tstream_tls_params);
+ if (__tlsp == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ tlsp = talloc_zero(__tlsp, struct tstream_tls_params_internal);
+ if (tlsp == NULL) {
+ TALLOC_FREE(__tlsp);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ talloc_set_destructor(tlsp, tstream_tls_params_internal_destructor);
+ __tlsp->internal = tlsp;
+ tlsp->tls_enabled = false;
+
+ *_tlsp = __tlsp;
+ return NT_STATUS_OK;
+ }
+
+ __tlsp = talloc_zero(mem_ctx, struct tstream_tls_params);
+ if (__tlsp == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ tlsp = talloc_zero(__tlsp, struct tstream_tls_params_internal);
+ if (tlsp == NULL) {
+ TALLOC_FREE(__tlsp);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ talloc_set_destructor(tlsp, tstream_tls_params_internal_destructor);
+ __tlsp->internal = tlsp;
+
+ 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));
+ TALLOC_FREE(__tlsp);
+ 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;
+ struct tstream_tls_params_internal *tlsp = NULL;
+ 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);
+
+ /*
+ * Note we need to make sure x509_cred and dh_params
+ * from tstream_tls_params_internal stay alive for
+ * the whole lifetime of this session!
+ *
+ * See 'man gnutls_credentials_set' and
+ * 'man gnutls_certificate_set_dh_params'.
+ *
+ * Note: here we use talloc_reference() in a way
+ * that does not expose it to the caller.
+ */
+ tlsp = talloc_reference(tlss, _tlsp->internal);
+ if (tevent_req_nomem(tlsp, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ 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 ?
+ "unexpected_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
+ ''')