summaryrefslogtreecommitdiffstats
path: root/source4/libnet
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
commit4f5791ebd03eaec1c7da0865a383175b05102712 (patch)
tree8ce7b00f7a76baa386372422adebbe64510812d4 /source4/libnet
parentInitial commit. (diff)
downloadsamba-upstream.tar.xz
samba-upstream.zip
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--source4/libnet/composite.h56
-rw-r--r--source4/libnet/groupinfo.c384
-rw-r--r--source4/libnet/groupinfo.h54
-rw-r--r--source4/libnet/groupman.c139
-rw-r--r--source4/libnet/groupman.h35
-rw-r--r--source4/libnet/libnet.c55
-rw-r--r--source4/libnet/libnet.h86
-rw-r--r--source4/libnet/libnet_become_dc.c3281
-rw-r--r--source4/libnet/libnet_become_dc.h152
-rw-r--r--source4/libnet/libnet_domain.c1304
-rw-r--r--source4/libnet/libnet_domain.h70
-rw-r--r--source4/libnet/libnet_export_keytab.c207
-rw-r--r--source4/libnet/libnet_export_keytab.h32
-rw-r--r--source4/libnet/libnet_group.c764
-rw-r--r--source4/libnet/libnet_group.h74
-rw-r--r--source4/libnet/libnet_join.c1021
-rw-r--r--source4/libnet/libnet_join.h101
-rw-r--r--source4/libnet/libnet_lookup.c448
-rw-r--r--source4/libnet/libnet_lookup.h69
-rw-r--r--source4/libnet/libnet_passwd.c1112
-rw-r--r--source4/libnet/libnet_passwd.h144
-rw-r--r--source4/libnet/libnet_rpc.c1037
-rw-r--r--source4/libnet/libnet_rpc.h73
-rw-r--r--source4/libnet/libnet_samsync.h32
-rw-r--r--source4/libnet/libnet_share.c215
-rw-r--r--source4/libnet/libnet_share.h70
-rw-r--r--source4/libnet/libnet_site.c292
-rw-r--r--source4/libnet/libnet_site.h35
-rw-r--r--source4/libnet/libnet_time.c125
-rw-r--r--source4/libnet/libnet_time.h46
-rw-r--r--source4/libnet/libnet_unbecome_dc.c792
-rw-r--r--source4/libnet/libnet_unbecome_dc.h31
-rw-r--r--source4/libnet/libnet_user.c1241
-rw-r--r--source4/libnet/libnet_user.h156
-rw-r--r--source4/libnet/libnet_vampire.c838
-rw-r--r--source4/libnet/libnet_vampire.h58
-rw-r--r--source4/libnet/prereq_domain.c144
-rw-r--r--source4/libnet/py_net.c945
-rw-r--r--source4/libnet/py_net.h24
-rw-r--r--source4/libnet/py_net_dckeytab.c121
-rw-r--r--source4/libnet/userinfo.c382
-rw-r--r--source4/libnet/userinfo.h54
-rw-r--r--source4/libnet/userman.c922
-rw-r--r--source4/libnet/userman.h106
-rw-r--r--source4/libnet/wscript_build29
45 files changed, 17356 insertions, 0 deletions
diff --git a/source4/libnet/composite.h b/source4/libnet/composite.h
new file mode 100644
index 0000000..50bf1a7
--- /dev/null
+++ b/source4/libnet/composite.h
@@ -0,0 +1,56 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Rafal Szczesniak 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/>.
+*/
+
+/*
+ * Monitor structure and message types definitions. Composite function monitoring
+ * allows client application to be notified on function progress. This enables
+ * eg. gui client to display progress bars, status messages, etc.
+ */
+
+
+#define mon_SamrCreateUser (0x00000001)
+#define mon_SamrOpenUser (0x00000002)
+#define mon_SamrQueryUser (0x00000003)
+#define mon_SamrCloseUser (0x00000004)
+#define mon_SamrLookupName (0x00000005)
+#define mon_SamrDeleteUser (0x00000006)
+#define mon_SamrSetUser (0x00000007)
+#define mon_SamrClose (0x00000008)
+#define mon_SamrConnect (0x00000009)
+#define mon_SamrLookupDomain (0x0000000A)
+#define mon_SamrOpenDomain (0x0000000B)
+#define mon_SamrEnumDomains (0x0000000C)
+#define mon_LsaOpenPolicy (0x0000000D)
+#define mon_LsaQueryPolicy (0x0000000E)
+#define mon_LsaClose (0x0000000F)
+#define mon_SamrOpenGroup (0x00000010)
+#define mon_SamrQueryGroup (0x00000011)
+
+#define mon_NetLookupDc (0x00000100)
+#define mon_NetRpcConnect (0x00000200)
+
+#define mon_Mask_Rpc (0x000000FF)
+#define mon_Mask_Net (0x0000FF00)
+
+
+struct monitor_msg {
+ uint32_t type;
+ void *data;
+ size_t data_size;
+};
diff --git a/source4/libnet/groupinfo.c b/source4/libnet/groupinfo.c
new file mode 100644
index 0000000..3d2968b
--- /dev/null
+++ b/source4/libnet/groupinfo.c
@@ -0,0 +1,384 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Rafal Szczesniak 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/>.
+*/
+
+/*
+ a composite function for getting group information via samr pipe
+*/
+
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "librpc/gen_ndr/security.h"
+#include "libcli/security/security.h"
+#include "libnet/libnet.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+
+
+struct groupinfo_state {
+ struct dcerpc_binding_handle *binding_handle;
+ struct policy_handle domain_handle;
+ struct policy_handle group_handle;
+ uint16_t level;
+ struct samr_LookupNames lookup;
+ struct samr_OpenGroup opengroup;
+ struct samr_QueryGroupInfo querygroupinfo;
+ struct samr_Close samrclose;
+ union samr_GroupInfo *info;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static void continue_groupinfo_lookup(struct tevent_req *subreq);
+static void continue_groupinfo_opengroup(struct tevent_req *subreq);
+static void continue_groupinfo_getgroup(struct tevent_req *subreq);
+static void continue_groupinfo_closegroup(struct tevent_req *subreq);
+
+
+/**
+ * Stage 1 (optional): Look for a group name in SAM server.
+ */
+static void continue_groupinfo_lookup(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct groupinfo_state *s;
+ struct monitor_msg msg;
+ struct msg_rpc_lookup_name *msg_lookup;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type(c->private_data, struct groupinfo_state);
+
+ /* receive samr_Lookup reply */
+ c->status = dcerpc_samr_LookupNames_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ /* there could be a problem with name resolving itself */
+ if (!NT_STATUS_IS_OK(s->lookup.out.result)) {
+ composite_error(c, s->lookup.out.result);
+ return;
+ }
+
+ /* issue a monitor message */
+ if (s->monitor_fn) {
+ msg.type = mon_SamrLookupName;
+ msg_lookup = talloc(s, struct msg_rpc_lookup_name);
+ msg_lookup->rid = s->lookup.out.rids->ids;
+ msg_lookup->count = s->lookup.out.rids->count;
+ msg.data = (void*)msg_lookup;
+ msg.data_size = sizeof(*msg_lookup);
+
+ s->monitor_fn(&msg);
+ }
+
+ /* have we actually got name resolved
+ - we're looking for only one at the moment */
+ if (s->lookup.out.rids->count != s->lookup.in.num_names) {
+ composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+ if (s->lookup.out.types->count != s->lookup.in.num_names) {
+ composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ /* TODO: find proper status code for more than one rid found */
+
+ /* prepare parameters for LookupNames */
+ s->opengroup.in.domain_handle = &s->domain_handle;
+ s->opengroup.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ s->opengroup.in.rid = s->lookup.out.rids->ids[0];
+ s->opengroup.out.group_handle = &s->group_handle;
+
+ /* send request */
+ subreq = dcerpc_samr_OpenGroup_r_send(s, c->event_ctx,
+ s->binding_handle,
+ &s->opengroup);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_groupinfo_opengroup, c);
+}
+
+
+/**
+ * Stage 2: Open group policy handle.
+ */
+static void continue_groupinfo_opengroup(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct groupinfo_state *s;
+ struct monitor_msg msg;
+ struct msg_rpc_open_group *msg_open;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type(c->private_data, struct groupinfo_state);
+
+ /* receive samr_OpenGroup reply */
+ c->status = dcerpc_samr_OpenGroup_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ if (!NT_STATUS_IS_OK(s->opengroup.out.result)) {
+ composite_error(c, s->opengroup.out.result);
+ return;
+ }
+
+ /* issue a monitor message */
+ if (s->monitor_fn) {
+ msg.type = mon_SamrOpenGroup;
+ msg_open = talloc(s, struct msg_rpc_open_group);
+ msg_open->rid = s->opengroup.in.rid;
+ msg_open->access_mask = s->opengroup.in.access_mask;
+ msg.data = (void*)msg_open;
+ msg.data_size = sizeof(*msg_open);
+
+ s->monitor_fn(&msg);
+ }
+
+ /* prepare parameters for QueryGroupInfo call */
+ s->querygroupinfo.in.group_handle = &s->group_handle;
+ s->querygroupinfo.in.level = s->level;
+ s->querygroupinfo.out.info = talloc(s, union samr_GroupInfo *);
+ if (composite_nomem(s->querygroupinfo.out.info, c)) return;
+
+ /* queue rpc call, set event handling and new state */
+ subreq = dcerpc_samr_QueryGroupInfo_r_send(s,
+ c->event_ctx,
+ s->binding_handle,
+ &s->querygroupinfo);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_groupinfo_getgroup, c);
+}
+
+
+/**
+ * Stage 3: Get requested group information.
+ */
+static void continue_groupinfo_getgroup(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct groupinfo_state *s;
+ struct monitor_msg msg;
+ struct msg_rpc_query_group *msg_query;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type(c->private_data, struct groupinfo_state);
+
+ /* receive samr_QueryGroupInfo reply */
+ c->status = dcerpc_samr_QueryGroupInfo_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ /* check if querygroup itself went ok */
+ if (!NT_STATUS_IS_OK(s->querygroupinfo.out.result)) {
+ composite_error(c, s->querygroupinfo.out.result);
+ return;
+ }
+
+ s->info = talloc_steal(s, *s->querygroupinfo.out.info);
+
+ /* issue a monitor message */
+ if (s->monitor_fn) {
+ msg.type = mon_SamrQueryGroup;
+ msg_query = talloc(s, struct msg_rpc_query_group);
+ msg_query->level = s->querygroupinfo.in.level;
+ msg.data = (void*)msg_query;
+ msg.data_size = sizeof(*msg_query);
+
+ s->monitor_fn(&msg);
+ }
+
+ /* prepare arguments for Close call */
+ s->samrclose.in.handle = &s->group_handle;
+ s->samrclose.out.handle = &s->group_handle;
+
+ /* queue rpc call, set event handling and new state */
+ subreq = dcerpc_samr_Close_r_send(s, c->event_ctx,
+ s->binding_handle,
+ &s->samrclose);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_groupinfo_closegroup, c);
+}
+
+
+/**
+ * Stage 4: Close policy handle associated with opened group.
+ */
+static void continue_groupinfo_closegroup(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct groupinfo_state *s;
+ struct monitor_msg msg;
+ struct msg_rpc_close_group *msg_close;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type(c->private_data, struct groupinfo_state);
+
+ /* receive samr_Close reply */
+ c->status = dcerpc_samr_Close_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ if (!NT_STATUS_IS_OK(s->samrclose.out.result)) {
+ composite_error(c, s->samrclose.out.result);
+ return;
+ }
+
+ /* issue a monitor message */
+ if (s->monitor_fn) {
+ msg.type = mon_SamrClose;
+ msg_close = talloc(s, struct msg_rpc_close_group);
+ msg_close->rid = s->opengroup.in.rid;
+ msg.data = (void*)msg_close;
+ msg.data_size = sizeof(*msg_close);
+
+ s->monitor_fn(&msg);
+ }
+
+ composite_done(c);
+}
+
+
+/**
+ * Sends asynchronous groupinfo request
+ *
+ * @param p dce/rpc call pipe
+ * @param io arguments and results of the call
+ */
+struct composite_context *libnet_rpc_groupinfo_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dcerpc_binding_handle *b,
+ struct libnet_rpc_groupinfo *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct groupinfo_state *s;
+ struct dom_sid *sid;
+ struct tevent_req *subreq;
+
+ if (!b || !io) return NULL;
+
+ c = composite_create(mem_ctx, ev);
+ if (c == NULL) return c;
+
+ s = talloc_zero(c, struct groupinfo_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ s->level = io->in.level;
+ s->binding_handle= b;
+ s->domain_handle = io->in.domain_handle;
+ s->monitor_fn = monitor;
+
+ if (io->in.sid) {
+ sid = dom_sid_parse_talloc(s, io->in.sid);
+ if (composite_nomem(sid, c)) return c;
+
+ s->opengroup.in.domain_handle = &s->domain_handle;
+ s->opengroup.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ s->opengroup.in.rid = sid->sub_auths[sid->num_auths - 1];
+ s->opengroup.out.group_handle = &s->group_handle;
+
+ /* send request */
+ subreq = dcerpc_samr_OpenGroup_r_send(s, c->event_ctx,
+ s->binding_handle,
+ &s->opengroup);
+ if (composite_nomem(subreq, c)) return c;
+
+ tevent_req_set_callback(subreq, continue_groupinfo_opengroup, c);
+
+ } else {
+ /* preparing parameters to send rpc request */
+ s->lookup.in.domain_handle = &s->domain_handle;
+ s->lookup.in.num_names = 1;
+ s->lookup.in.names = talloc_array(s, struct lsa_String, 1);
+ if (composite_nomem(s->lookup.in.names, c)) return c;
+
+ s->lookup.in.names[0].string = talloc_strdup(s, io->in.groupname);
+ if (composite_nomem(s->lookup.in.names[0].string, c)) return c;
+ s->lookup.out.rids = talloc_zero(s, struct samr_Ids);
+ s->lookup.out.types = talloc_zero(s, struct samr_Ids);
+ if (composite_nomem(s->lookup.out.rids, c)) return c;
+ if (composite_nomem(s->lookup.out.types, c)) return c;
+
+ /* send request */
+ subreq = dcerpc_samr_LookupNames_r_send(s, c->event_ctx,
+ s->binding_handle,
+ &s->lookup);
+ if (composite_nomem(subreq, c)) return c;
+
+ tevent_req_set_callback(subreq, continue_groupinfo_lookup, c);
+ }
+
+ return c;
+}
+
+
+/**
+ * Waits for and receives result of asynchronous groupinfo call
+ *
+ * @param c composite context returned by asynchronous groupinfo call
+ * @param mem_ctx memory context of the call
+ * @param io pointer to results (and arguments) of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_rpc_groupinfo_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_rpc_groupinfo *io)
+{
+ NTSTATUS status;
+ struct groupinfo_state *s;
+
+ /* wait for results of sending request */
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status) && io) {
+ s = talloc_get_type(c->private_data, struct groupinfo_state);
+ talloc_steal(mem_ctx, s->info);
+ io->out.info = *s->info;
+ }
+
+ /* memory context associated to composite context is no longer needed */
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Synchronous version of groupinfo call
+ *
+ * @param pipe dce/rpc call pipe
+ * @param mem_ctx memory context for the call
+ * @param io arguments and results of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_rpc_groupinfo(struct tevent_context *ev,
+ struct dcerpc_binding_handle *b,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_rpc_groupinfo *io)
+{
+ struct composite_context *c = libnet_rpc_groupinfo_send(mem_ctx, ev, b,
+ io, NULL);
+ return libnet_rpc_groupinfo_recv(c, mem_ctx, io);
+}
diff --git a/source4/libnet/groupinfo.h b/source4/libnet/groupinfo.h
new file mode 100644
index 0000000..ad13840
--- /dev/null
+++ b/source4/libnet/groupinfo.h
@@ -0,0 +1,54 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Rafal Szczesniak 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 "librpc/gen_ndr/samr.h"
+
+/*
+ * IO structures for groupinfo.c functions
+ */
+
+struct libnet_rpc_groupinfo {
+ struct {
+ struct policy_handle domain_handle;
+ const char *groupname;
+ const char *sid;
+ uint16_t level;
+ } in;
+ struct {
+ union samr_GroupInfo info;
+ } out;
+};
+
+
+/*
+ * Monitor messages sent from groupinfo.c functions
+ */
+
+struct msg_rpc_open_group {
+ uint32_t rid, access_mask;
+};
+
+struct msg_rpc_query_group {
+ uint16_t level;
+};
+
+struct msg_rpc_close_group {
+ uint32_t rid;
+};
diff --git a/source4/libnet/groupman.c b/source4/libnet/groupman.c
new file mode 100644
index 0000000..c91eff3
--- /dev/null
+++ b/source4/libnet/groupman.c
@@ -0,0 +1,139 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Rafal Szczesniak 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/>.
+*/
+
+/*
+ a composite function for manipulating (add/edit/del) groups via samr pipe
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "libnet/libnet.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+
+
+struct groupadd_state {
+ struct dcerpc_binding_handle *binding_handle;
+ struct policy_handle domain_handle;
+ struct samr_CreateDomainGroup creategroup;
+ struct policy_handle group_handle;
+ uint32_t group_rid;
+
+ void (*monitor_fn)(struct monitor_msg*);
+};
+
+
+static void continue_groupadd_created(struct tevent_req *subreq);
+
+
+struct composite_context* libnet_rpc_groupadd_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dcerpc_binding_handle *b,
+ struct libnet_rpc_groupadd *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct groupadd_state *s;
+ struct tevent_req *subreq;
+
+ if (!b || !io) return NULL;
+
+ c = composite_create(mem_ctx, ev);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct groupadd_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ s->domain_handle = io->in.domain_handle;
+ s->binding_handle= b;
+ s->monitor_fn = monitor;
+
+ s->creategroup.in.domain_handle = &s->domain_handle;
+
+ s->creategroup.in.name = talloc_zero(c, struct lsa_String);
+ if (composite_nomem(s->creategroup.in.name, c)) return c;
+
+ s->creategroup.in.name->string = talloc_strdup(c, io->in.groupname);
+ if (composite_nomem(s->creategroup.in.name->string, c)) return c;
+
+ s->creategroup.in.access_mask = 0;
+
+ s->creategroup.out.group_handle = &s->group_handle;
+ s->creategroup.out.rid = &s->group_rid;
+
+ subreq = dcerpc_samr_CreateDomainGroup_r_send(s, c->event_ctx,
+ s->binding_handle,
+ &s->creategroup);
+ if (composite_nomem(subreq, c)) return c;
+
+ tevent_req_set_callback(subreq, continue_groupadd_created, c);
+ return c;
+}
+
+
+NTSTATUS libnet_rpc_groupadd_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_rpc_groupadd *io)
+{
+ NTSTATUS status;
+ struct groupadd_state *s;
+
+ status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status) && io) {
+ s = talloc_get_type(c->private_data, struct groupadd_state);
+ io->out.group_handle = s->group_handle;
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+static void continue_groupadd_created(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct groupadd_state *s;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type(c->private_data, struct groupadd_state);
+
+ c->status = dcerpc_samr_CreateDomainGroup_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ c->status = s->creategroup.out.result;
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ composite_done(c);
+}
+
+
+NTSTATUS libnet_rpc_groupadd(struct tevent_context *ev,
+ struct dcerpc_binding_handle *b,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_rpc_groupadd *io)
+{
+ struct composite_context *c;
+
+ c = libnet_rpc_groupadd_send(mem_ctx, ev, b, io, NULL);
+ return libnet_rpc_groupadd_recv(c, mem_ctx, io);
+}
diff --git a/source4/libnet/groupman.h b/source4/libnet/groupman.h
new file mode 100644
index 0000000..0acb02d
--- /dev/null
+++ b/source4/libnet/groupman.h
@@ -0,0 +1,35 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Rafal Szczesniak 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 "librpc/gen_ndr/misc.h"
+
+
+/*
+ * IO structures for groupman.c functions
+ */
+
+struct libnet_rpc_groupadd {
+ struct {
+ struct policy_handle domain_handle;
+ const char *groupname;
+ } in;
+ struct {
+ struct policy_handle group_handle;
+ } out;
+};
diff --git a/source4/libnet/libnet.c b/source4/libnet/libnet.c
new file mode 100644
index 0000000..a590893
--- /dev/null
+++ b/source4/libnet/libnet.c
@@ -0,0 +1,55 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 "libnet/libnet.h"
+#include "lib/events/events.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+
+struct libnet_context *libnet_context_init(struct tevent_context *ev,
+ struct loadparm_context *lp_ctx)
+{
+ struct libnet_context *ctx;
+
+ /* We require an event context here */
+ if (!ev) {
+ return NULL;
+ }
+
+ /* create brand new libnet context */
+ ctx = talloc_zero(ev, struct libnet_context);
+ if (!ctx) {
+ return NULL;
+ }
+
+ ctx->event_ctx = ev;
+ ctx->lp_ctx = lp_ctx;
+
+ /* make sure dcerpc is initialized */
+ dcerpc_init();
+
+ /* name resolution methods */
+ ctx->resolve_ctx = lpcfg_resolve_context(lp_ctx);
+
+ /* default buffer size for various operations requiring specifying a buffer */
+ ctx->samr.buf_size = 128;
+
+ return ctx;
+}
diff --git a/source4/libnet/libnet.h b/source4/libnet/libnet.h
new file mode 100644
index 0000000..41ddbea
--- /dev/null
+++ b/source4/libnet/libnet.h
@@ -0,0 +1,86 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Rafal Szczesniak 2005-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/>.
+*/
+#ifndef LIBNET_H
+#define LIBNET_H
+
+#include "librpc/gen_ndr/misc.h"
+
+struct libnet_context {
+ /* here we need:
+ * a client env context
+ * a user env context
+ */
+ struct cli_credentials *cred;
+
+ /* samr connection parameters - opened handles and related properties */
+ struct {
+ struct dcerpc_pipe *pipe;
+ struct dcerpc_binding_handle *samr_handle;
+ const char *name;
+ struct dom_sid *sid;
+ uint32_t access_mask;
+ struct policy_handle handle;
+ struct policy_handle connect_handle;
+ int buf_size;
+ } samr;
+
+ /* lsa connection parameters - opened handles and related properties */
+ struct {
+ struct dcerpc_pipe *pipe;
+ struct dcerpc_binding_handle *lsa_handle;
+ const char *name;
+ uint32_t access_mask;
+ struct policy_handle handle;
+ } lsa;
+
+ /* name resolution methods */
+ struct resolve_context *resolve_ctx;
+
+ struct tevent_context *event_ctx;
+
+ struct loadparm_context *lp_ctx;
+
+ /* if non-null then override the server address */
+ const char *server_address;
+};
+
+
+#include <ldb.h>
+#include "libnet/composite.h"
+#include "libnet/userman.h"
+#include "libnet/userinfo.h"
+#include "libnet/groupinfo.h"
+#include "libnet/groupman.h"
+#include "libnet/libnet_passwd.h"
+#include "libnet/libnet_time.h"
+#include "libnet/libnet_rpc.h"
+#include "libnet/libnet_join.h"
+#include "libnet/libnet_site.h"
+#include "libnet/libnet_become_dc.h"
+#include "libnet/libnet_unbecome_dc.h"
+#include "libnet/libnet_samsync.h"
+#include "libnet/libnet_vampire.h"
+#include "libnet/libnet_user.h"
+#include "libnet/libnet_group.h"
+#include "libnet/libnet_share.h"
+#include "libnet/libnet_lookup.h"
+#include "libnet/libnet_domain.h"
+#include "libnet/libnet_proto.h"
+#endif
diff --git a/source4/libnet/libnet_become_dc.c b/source4/libnet/libnet_become_dc.c
new file mode 100644
index 0000000..e9153a0
--- /dev/null
+++ b/source4/libnet/libnet_become_dc.c
@@ -0,0 +1,3281 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 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 "libnet/libnet.h"
+#include "libcli/composite/composite.h"
+#include "libcli/cldap/cldap.h"
+#include <ldb.h>
+#include <ldb_errors.h>
+#include "ldb_wrap.h"
+#include "dsdb/samdb/samdb.h"
+#include "../libds/common/flags.h"
+#include "librpc/gen_ndr/ndr_drsuapi_c.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "auth/gensec/gensec.h"
+#include "param/param.h"
+#include "lib/tsocket/tsocket.h"
+
+/*****************************************************************************
+ * Windows 2003 (w2k3) does the following steps when changing the server role
+ * from domain member to domain controller
+ *
+ * We mostly do the same.
+ *****************************************************************************/
+
+/*
+ * lookup DC:
+ * - using nbt name<1C> request and a samlogon mailslot request
+ * or
+ * - using a DNS SRV _ldap._tcp.dc._msdcs. request and a CLDAP netlogon request
+ *
+ * see: becomeDC_recv_cldap() and becomeDC_send_cldap()
+ */
+
+/*
+ * Open 1st LDAP connection to the DC using admin credentials
+ *
+ * see: becomeDC_connect_ldap1() and becomeDC_ldap_connect()
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_rootdse()
+ *
+ * Request:
+ * basedn: ""
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: *
+ * Result:
+ * ""
+ * currentTime: 20061202155100.0Z
+ * subschemaSubentry: CN=Aggregate,CN=Schema,CN=Configuration,<domain_partition>
+ * dsServiceName: CN=<netbios_name>,CN=Servers,CN=<site_name>,CN=Sites,CN=Configuration,<domain_partition>
+ * namingContexts: <domain_partition>
+ * CN=Configuration,<domain_partition>
+ * CN=Schema,CN=Configuration,<domain_partition>
+ * defaultNamingContext: <domain_partition>
+ * schemaNamingContext: CN=Schema,CN=Configuration,<domain_partition>
+ * configurationNamingContext:CN=Configuration,<domain_partition>
+ * rootDomainNamingContext:<domain_partition>
+ * supportedControl: ...
+ * supportedLDAPVersion: 3
+ * 2
+ * supportedLDAPPolicies: ...
+ * highestCommittedUSN: ...
+ * supportedSASLMechanisms:GSSAPI
+ * GSS-SPNEGO
+ * EXTERNAL
+ * DIGEST-MD5
+ * dnsHostName: <dns_host_name>
+ * ldapServiceName: <domain_dns_name>:<netbios_name>$@<REALM>
+ * serverName: CN=Servers,CN=<site_name>,CN=Sites,CN=Configuration,<domain_partition>
+ * supportedCapabilities: ...
+ * isSyncronized: TRUE
+ * isGlobalCatalogReady: TRUE
+ * domainFunctionality: 0
+ * forestFunctionality: 0
+ * domainControllerFunctionality: 2
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_crossref_behavior_version()
+ *
+ * Request:
+ * basedn: CN=Configuration,<domain_partition>
+ * scope: one
+ * filter: (cn=Partitions)
+ * attrs: msDS-Behavior-Version
+ * Result:
+ * CN=Partitions,CN=Configuration,<domain_partition>
+ * msDS-Behavior-Version: 0
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * NOTE: this seems to be a bug! as the messageID of the LDAP message is corrupted!
+ *
+ * not implemented here
+ *
+ * Request:
+ * basedn: CN=Schema,CN=Configuration,<domain_partition>
+ * scope: one
+ * filter: (cn=Partitions)
+ * attrs: msDS-Behavior-Version
+ * Result:
+ * <none>
+ *
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_domain_behavior_version()
+ *
+ * Request:
+ * basedn: <domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: msDS-Behavior-Version
+ * Result:
+ * <domain_partition>
+ * msDS-Behavior-Version: 0
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_schema_object_version()
+ *
+ * Request:
+ * basedn: CN=Schema,CN=Configuration,<domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: objectVersion
+ * Result:
+ * CN=Schema,CN=Configuration,<domain_partition>
+ * objectVersion: 30
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * not implemented, because the information is already there
+ *
+ * Request:
+ * basedn: ""
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: defaultNamingContext
+ * dnsHostName
+ * Result:
+ * ""
+ * defaultNamingContext: <domain_partition>
+ * dnsHostName: <dns_host_name>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_infrastructure_fsmo()
+ *
+ * Request:
+ * basedn: <WKGUID=2fbac1870ade11d297c400c04fd8d5cd,domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: 1.1
+ * Result:
+ * CN=Infrastructure,<domain_partition>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_w2k3_update_revision()
+ *
+ * Request:
+ * basedn: CN=Windows2003Update,CN=DomainUpdates,CN=System,<domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: revision
+ * Result:
+ * CN=Windows2003Update,CN=DomainUpdates,CN=System,<domain_partition>
+ * revision: 8
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_infrastructure_fsmo()
+ *
+ * Request:
+ * basedn: CN=Infrastructure,<domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: fSMORoleOwner
+ * Result:
+ * CN=Infrastructure,<domain_partition>
+ * fSMORoleOwner: CN=NTDS Settings,<infrastructure_fsmo_server_object>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_infrastructure_fsmo()
+ *
+ * Request:
+ * basedn: <infrastructure_fsmo_server_object>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: dnsHostName
+ * Result:
+ * <infrastructure_fsmo_server_object>
+ * dnsHostName: <dns_host_name>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_infrastructure_fsmo()
+ *
+ * Request:
+ * basedn: CN=NTDS Settings,<infrastructure_fsmo_server_object>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: objectGUID
+ * Result:
+ * CN=NTDS Settings,<infrastructure_fsmo_server_object>
+ * objectGUID: <object_guid>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_rid_manager_fsmo()
+ *
+ * Request:
+ * basedn: <domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: rIDManagerReference
+ * Result:
+ * <domain_partition>
+ * rIDManagerReference: CN=RID Manager$,CN=System,<domain_partition>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_rid_manager_fsmo()
+ *
+ * Request:
+ * basedn: CN=RID Manager$,CN=System,<domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: fSMORoleOwner
+ * Result:
+ * CN=Infrastructure,<domain_partition>
+ * fSMORoleOwner: CN=NTDS Settings,<rid_manager_fsmo_server_object>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_rid_manager_fsmo()
+ *
+ * Request:
+ * basedn: <rid_manager_fsmo_server_object>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: dnsHostName
+ * Result:
+ * <rid_manager_fsmo_server_object>
+ * dnsHostName: <dns_host_name>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_rid_manager_fsmo()
+ *
+ * Request:
+ * basedn: CN=NTDS Settings,<rid_manager_fsmo_server_object>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: msDs-ReplicationEpoch
+ * Result:
+ * CN=NTDS Settings,<rid_manager_fsmo_server_object>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_site_object()
+ *
+ * Request:
+ * basedn: CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs:
+ * Result:
+ * CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>
+ * objectClass: top
+ * site
+ * cn: <new_dc_site_name>
+ * distinguishedName:CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>
+ * instanceType: 4
+ * whenCreated: ...
+ * whenChanged: ...
+ * uSNCreated: ...
+ * uSNChanged: ...
+ * showInAdvancedViewOnly: TRUE
+ * name: <new_dc_site_name>
+ * objectGUID: <object_guid>
+ * systemFlags: 1107296256 <0x42000000>
+ * objectCategory: CN=Site,CN=Schema,CN=Configuration,<domain_partition>
+ */
+
+/***************************************************************
+ * Add this stage we call the check_options() callback function
+ * of the caller, to see if he wants us to continue
+ *
+ * see: becomeDC_check_options()
+ ***************************************************************/
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_computer_object()
+ *
+ * Request:
+ * basedn: <domain_partition>
+ * scope: sub
+ * filter: (&(|(objectClass=user)(objectClass=computer))(sAMAccountName=<new_dc_account_name>))
+ * attrs: distinguishedName
+ * userAccountControl
+ * Result:
+ * CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * distinguishedName: CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * userAccoountControl: 4096 <0x1000>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_server_object_1()
+ *
+ * Request:
+ * basedn: CN=<new_dc_netbios_name>,CN=Servers,CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs:
+ * Result:
+ * <noSuchObject>
+ * <matchedDN:CN=Servers,CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_server_object_2()
+ *
+ * Request:
+ * basedn: CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: serverReferenceBL
+ * typesOnly: TRUE!!!
+ * Result:
+ * CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ */
+
+/*
+ * LDAP add 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_server_object_add()
+ *
+ * Request:
+ * CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * objectClass: server
+ * systemFlags: 50000000 <0x2FAF080>
+ * serverReference:CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * Result:
+ * <success>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * not implemented, maybe we can add that later
+ *
+ * Request:
+ * basedn: CN=NTDS Settings,CN=<new_dc_netbios_name>,CN=Servers,CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs:
+ * Result:
+ * <noSuchObject>
+ * <matchedDN:CN=<new_dc_netbios_name>,CN=Servers,CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * not implemented because it gives no new information
+ *
+ * Request:
+ * basedn: CN=Partitions,CN=Configuration,<domain_partition>
+ * scope: sub
+ * filter: (nCName=<domain_partition>)
+ * attrs: nCName
+ * dnsRoot
+ * controls: LDAP_SERVER_EXTENDED_DN_OID:critical=false
+ * Result:
+ * <GUID=<hex_guid>>;CN=<domain_netbios_name>,CN=Partitions,<domain_partition>>
+ * nCName: <GUID=<hex_guid>>;<SID=<hex_sid>>;<domain_partition>>
+ * dnsRoot: <domain_dns_name>
+ */
+
+/*
+ * LDAP modify 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_server_object_modify()
+ *
+ * Request (add):
+ * CN=<new_dc_netbios_name>,CN=Servers,CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>>
+ * serverReference:CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * Result:
+ * <attributeOrValueExist>
+ */
+
+/*
+ * LDAP modify 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_server_object_modify()
+ *
+ * Request (replace):
+ * CN=<new_dc_netbios_name>,CN=Servers,CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>>
+ * serverReference:CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * Result:
+ * <success>
+ */
+
+/*
+ * Open 1st DRSUAPI connection to the DC using admin credentials
+ * DsBind with DRSUAPI_DS_BIND_GUID_W2K3 ("6afab99c-6e26-464a-975f-f58f105218bc")
+ * (w2k3 does 2 DsBind() calls here..., where is first is unused and contains garbage at the end)
+ *
+ * see: becomeDC_drsuapi_connect_send(), becomeDC_drsuapi1_connect_recv(),
+ * becomeDC_drsuapi_bind_send(), becomeDC_drsuapi_bind_recv() and becomeDC_drsuapi1_bind_recv()
+ */
+
+/*
+ * DsAddEntry to create the CN=NTDS Settings,CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name, ...
+ * on the 1st DRSUAPI connection
+ *
+ * see: becomeDC_drsuapi1_add_entry_send() and becomeDC_drsuapi1_add_entry_recv()
+ */
+
+/***************************************************************
+ * Add this stage we call the prepare_db() callback function
+ * of the caller, to see if he wants us to continue
+ *
+ * see: becomeDC_prepare_db()
+ ***************************************************************/
+
+/*
+ * Open 2nd and 3rd DRSUAPI connection to the DC using admin credentials
+ * - a DsBind with DRSUAPI_DS_BIND_GUID_W2K3 ("6afab99c-6e26-464a-975f-f58f105218bc")
+ * on the 2nd connection
+ *
+ * see: becomeDC_drsuapi_connect_send(), becomeDC_drsuapi2_connect_recv(),
+ * becomeDC_drsuapi_bind_send(), becomeDC_drsuapi_bind_recv(), becomeDC_drsuapi2_bind_recv()
+ * and becomeDC_drsuapi3_connect_recv()
+ */
+
+/*
+ * replicate CN=Schema,CN=Configuration,...
+ * on the 3rd DRSUAPI connection and the bind_handle from the 2nd connection
+ *
+ * see: becomeDC_drsuapi_pull_partition_send(), becomeDC_drsuapi_pull_partition_recv(),
+ * becomeDC_drsuapi3_pull_schema_send() and becomeDC_drsuapi3_pull_schema_recv()
+ *
+ ***************************************************************
+ * Add this stage we call the schema_chunk() callback function
+ * for each replication message
+ ***************************************************************/
+
+/*
+ * replicate CN=Configuration,...
+ * on the 3rd DRSUAPI connection and the bind_handle from the 2nd connection
+ *
+ * see: becomeDC_drsuapi_pull_partition_send(), becomeDC_drsuapi_pull_partition_recv(),
+ * becomeDC_drsuapi3_pull_config_send() and becomeDC_drsuapi3_pull_config_recv()
+ *
+ ***************************************************************
+ * Add this stage we call the config_chunk() callback function
+ * for each replication message
+ ***************************************************************/
+
+/*
+ * LDAP unbind on the 1st LDAP connection
+ *
+ * not implemented, because it's not needed...
+ */
+
+/*
+ * Open 2nd LDAP connection to the DC using admin credentials
+ *
+ * see: becomeDC_connect_ldap2() and becomeDC_ldap_connect()
+ */
+
+/*
+ * LDAP search 2nd LDAP connection:
+ *
+ * not implemented because it gives no new information
+ * same as becomeDC_ldap1_computer_object()
+ *
+ * Request:
+ * basedn: <domain_partition>
+ * scope: sub
+ * filter: (&(|(objectClass=user)(objectClass=computer))(sAMAccountName=<new_dc_account_name>))
+ * attrs: distinguishedName
+ * userAccountControl
+ * Result:
+ * CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * distinguishedName: CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * userAccoountControl: 4096 <0x00001000>
+ */
+
+/*
+ * LDAP search 2nd LDAP connection:
+ *
+ * not implemented because it gives no new information
+ * same as becomeDC_ldap1_computer_object()
+ *
+ * Request:
+ * basedn: CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: userAccountControl
+ * Result:
+ * CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * userAccoountControl: 4096 <0x00001000>
+ */
+
+/*
+ * LDAP modify 2nd LDAP connection:
+ *
+ * see: becomeDC_ldap2_modify_computer()
+ *
+ * Request (replace):
+ * CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * userAccoountControl: 532480 <0x82000>
+ * Result:
+ * <success>
+ */
+
+/*
+ * LDAP search 2nd LDAP connection:
+ *
+ * see: becomeDC_ldap2_move_computer()
+ *
+ * Request:
+ * basedn: <WKGUID=2fbac1870ade11d297c400c04fd8d5cd,<domain_partition>>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: 1.1
+ * Result:
+ * CN=Domain Controllers,<domain_partition>
+ */
+
+/*
+ * LDAP search 2nd LDAP connection:
+ *
+ * not implemented because it gives no new information
+ *
+ * Request:
+ * basedn: CN=Domain Controllers,<domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: distinguishedName
+ * Result:
+ * CN=Domain Controller,<domain_partition>
+ * distinguishedName: CN=Domain Controllers,<domain_partition>
+ */
+
+/*
+ * LDAP modifyRDN 2nd LDAP connection:
+ *
+ * see: becomeDC_ldap2_move_computer()
+ *
+ * Request:
+ * entry: CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * newrdn: CN=<new_dc_netbios_name>
+ * deleteoldrdn: TRUE
+ * newparent: CN=Domain Controllers,<domain_partition>
+ * Result:
+ * <success>
+ */
+
+/*
+ * LDAP unbind on the 2nd LDAP connection
+ *
+ * not implemented, because it's not needed...
+ */
+
+/*
+ * replicate Domain Partition
+ * on the 3rd DRSUAPI connection and the bind_handle from the 2nd connection
+ *
+ * see: becomeDC_drsuapi_pull_partition_send(), becomeDC_drsuapi_pull_partition_recv(),
+ * becomeDC_drsuapi3_pull_domain_send() and becomeDC_drsuapi3_pull_domain_recv()
+ *
+ ***************************************************************
+ * Add this stage we call the domain_chunk() callback function
+ * for each replication message
+ ***************************************************************/
+
+/* call DsReplicaUpdateRefs() for all partitions like this:
+ * req1: struct drsuapi_DsReplicaUpdateRefsRequest1
+ *
+ * naming_context: struct drsuapi_DsReplicaObjectIdentifier
+ * __ndr_size : 0x000000ae (174)
+ * __ndr_size_sid : 0x00000000 (0)
+ * guid : 00000000-0000-0000-0000-000000000000
+ * sid : S-0-0
+ * dn : 'CN=Schema,CN=Configuration,DC=w2k3,DC=vmnet1,DC=vm,DC=base'
+ *
+ * dest_dsa_dns_name : '4a0df188-a0b8-47ea-bbe5-e614723f16dd._msdcs.w2k3.vmnet1.vm.base'
+ * dest_dsa_guid : 4a0df188-a0b8-47ea-bbe5-e614723f16dd
+ * options : 0x0000001c (28)
+ * 0: DRSUAPI_DS_REPLICA_UPDATE_ASYNCHRONOUS_OPERATION
+ * 0: DRSUAPI_DS_REPLICA_UPDATE_WRITEABLE
+ * 1: DRSUAPI_DS_REPLICA_UPDATE_ADD_REFERENCE
+ * 1: DRSUAPI_DS_REPLICA_UPDATE_DELETE_REFERENCE
+ * 1: DRSUAPI_DS_REPLICA_UPDATE_0x00000010
+ *
+ * 4a0df188-a0b8-47ea-bbe5-e614723f16dd is the objectGUID the DsAddEntry() returned for the
+ * CN=NTDS Settings,CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name, ...
+ * on the 2nd!!! DRSUAPI connection
+ *
+ * see: becomeDC_drsuapi_update_refs_send(), becomeDC_drsuapi2_update_refs_schema_recv(),
+ * becomeDC_drsuapi2_update_refs_config_recv() and becomeDC_drsuapi2_update_refs_domain_recv()
+ */
+
+/*
+ * Windows does opens the 4th and 5th DRSUAPI connection...
+ * and does a DsBind() with the objectGUID from DsAddEntry() as bind_guid
+ * on the 4th connection
+ *
+ * and then 2 full replications of the domain partition on the 5th connection
+ * with the bind_handle from the 4th connection
+ *
+ * not implemented because it gives no new information
+ */
+
+struct libnet_BecomeDC_state {
+ struct composite_context *creq;
+
+ struct libnet_context *libnet;
+
+ struct dom_sid zero_sid;
+
+ struct {
+ struct cldap_socket *sock;
+ struct cldap_netlogon io;
+ struct NETLOGON_SAM_LOGON_RESPONSE_EX netlogon;
+ } cldap;
+
+ struct becomeDC_ldap {
+ struct ldb_context *ldb;
+ const struct ldb_message *rootdse;
+ } ldap1, ldap2;
+
+ struct becomeDC_drsuapi {
+ struct libnet_BecomeDC_state *s;
+ struct dcerpc_binding *binding;
+ struct dcerpc_pipe *pipe;
+ struct dcerpc_binding_handle *drsuapi_handle;
+ DATA_BLOB gensec_skey;
+ struct drsuapi_DsBind bind_r;
+ struct GUID bind_guid;
+ struct drsuapi_DsBindInfoCtr bind_info_ctr;
+ struct drsuapi_DsBindInfo28 local_info28;
+ struct drsuapi_DsBindInfo28 remote_info28;
+ struct policy_handle bind_handle;
+ } drsuapi1, drsuapi2, drsuapi3;
+
+ void *ndr_struct_ptr;
+
+ struct libnet_BecomeDC_Domain domain;
+ struct libnet_BecomeDC_Forest forest;
+ struct libnet_BecomeDC_SourceDSA source_dsa;
+ struct libnet_BecomeDC_DestDSA dest_dsa;
+
+ struct libnet_BecomeDC_Partition schema_part, config_part, domain_part;
+
+ struct becomeDC_fsmo {
+ const char *dns_name;
+ const char *server_dn_str;
+ const char *ntds_dn_str;
+ struct GUID ntds_guid;
+ } infrastructure_fsmo;
+
+ struct becomeDC_fsmo rid_manager_fsmo;
+
+ struct libnet_BecomeDC_CheckOptions _co;
+ struct libnet_BecomeDC_PrepareDB _pp;
+ struct libnet_BecomeDC_StoreChunk _sc;
+ struct libnet_BecomeDC_Callbacks callbacks;
+
+ bool rodc_join;
+ bool critical_only;
+};
+
+static int32_t get_dc_function_level(struct loadparm_context *lp_ctx)
+{
+ /* per default we are (Windows) 2008 R2 compatible */
+ return lpcfg_parm_int(lp_ctx, NULL, "ads", "dc function level",
+ DS_DOMAIN_FUNCTION_2008_R2);
+}
+
+static void becomeDC_recv_cldap(struct tevent_req *req);
+
+static void becomeDC_send_cldap(struct libnet_BecomeDC_state *s)
+{
+ struct composite_context *c = s->creq;
+ struct tevent_req *req;
+ struct tsocket_address *dest_address;
+ int ret;
+
+ s->cldap.io.in.dest_address = NULL;
+ s->cldap.io.in.dest_port = 0;
+ s->cldap.io.in.realm = s->domain.dns_name;
+ s->cldap.io.in.host = s->dest_dsa.netbios_name;
+ s->cldap.io.in.user = NULL;
+ s->cldap.io.in.domain_guid = NULL;
+ s->cldap.io.in.domain_sid = NULL;
+ s->cldap.io.in.acct_control = -1;
+ s->cldap.io.in.version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
+ s->cldap.io.in.map_response = true;
+
+ ret = tsocket_address_inet_from_strings(s, "ip",
+ s->source_dsa.address,
+ lpcfg_cldap_port(s->libnet->lp_ctx),
+ &dest_address);
+ if (ret != 0) {
+ c->status = map_nt_error_from_unix_common(errno);
+ if (!composite_is_ok(c)) return;
+ }
+
+ c->status = cldap_socket_init(s, NULL, dest_address, &s->cldap.sock);
+ if (!composite_is_ok(c)) return;
+
+ req = cldap_netlogon_send(s, s->libnet->event_ctx,
+ s->cldap.sock, &s->cldap.io);
+ if (composite_nomem(req, c)) return;
+ tevent_req_set_callback(req, becomeDC_recv_cldap, s);
+}
+
+static void becomeDC_connect_ldap1(struct libnet_BecomeDC_state *s);
+
+static void becomeDC_recv_cldap(struct tevent_req *req)
+{
+ struct libnet_BecomeDC_state *s = tevent_req_callback_data(req,
+ struct libnet_BecomeDC_state);
+ struct composite_context *c = s->creq;
+
+ c->status = cldap_netlogon_recv(req, s, &s->cldap.io);
+ talloc_free(req);
+ if (!composite_is_ok(c)) {
+ DEBUG(0,("Failed to send, receive or parse CLDAP reply from server %s for our host %s: %s\n",
+ s->cldap.io.in.dest_address,
+ s->cldap.io.in.host,
+ nt_errstr(c->status)));
+ return;
+ }
+ s->cldap.netlogon = s->cldap.io.out.netlogon.data.nt5_ex;
+
+ s->domain.dns_name = s->cldap.netlogon.dns_domain;
+ s->domain.netbios_name = s->cldap.netlogon.domain_name;
+ s->domain.guid = s->cldap.netlogon.domain_uuid;
+
+ s->forest.dns_name = s->cldap.netlogon.forest;
+
+ s->source_dsa.dns_name = s->cldap.netlogon.pdc_dns_name;
+ s->source_dsa.netbios_name = s->cldap.netlogon.pdc_name;
+ s->source_dsa.site_name = s->cldap.netlogon.server_site;
+
+ s->dest_dsa.site_name = s->cldap.netlogon.client_site;
+
+ DEBUG(0,("CLDAP response: forest=%s dns=%s netbios=%s server_site=%s client_site=%s\n",
+ s->forest.dns_name, s->domain.dns_name, s->domain.netbios_name,
+ s->source_dsa.site_name, s->dest_dsa.site_name));
+ if (!s->dest_dsa.site_name || strcmp(s->dest_dsa.site_name, "") == 0) {
+ DEBUG(0,("Got empty client site - using server site name %s\n",
+ s->source_dsa.site_name));
+ s->dest_dsa.site_name = s->source_dsa.site_name;
+ }
+
+ becomeDC_connect_ldap1(s);
+}
+
+static NTSTATUS becomeDC_ldap_connect(struct libnet_BecomeDC_state *s,
+ struct becomeDC_ldap *ldap)
+{
+ char *url;
+
+ url = talloc_asprintf(s, "ldap://%s/", s->source_dsa.dns_name);
+ NT_STATUS_HAVE_NO_MEMORY(url);
+
+ ldap->ldb = ldb_wrap_connect(s, s->libnet->event_ctx, s->libnet->lp_ctx, url,
+ NULL,
+ s->libnet->cred,
+ 0);
+ talloc_free(url);
+ if (ldap->ldb == NULL) {
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_ldap1_rootdse(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ static const char *attrs[] = {
+ "*",
+ NULL
+ };
+
+ basedn = ldb_dn_new(s, s->ldap1.ldb, NULL);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE, attrs,
+ "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->ldap1.rootdse = r->msgs[0];
+
+ s->domain.dn_str = ldb_msg_find_attr_as_string(s->ldap1.rootdse, "defaultNamingContext", NULL);
+ if (!s->domain.dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+
+ s->forest.root_dn_str = ldb_msg_find_attr_as_string(s->ldap1.rootdse, "rootDomainNamingContext", NULL);
+ if (!s->forest.root_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ s->forest.config_dn_str = ldb_msg_find_attr_as_string(s->ldap1.rootdse, "configurationNamingContext", NULL);
+ if (!s->forest.config_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ s->forest.schema_dn_str = ldb_msg_find_attr_as_string(s->ldap1.rootdse, "schemaNamingContext", NULL);
+ if (!s->forest.schema_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+
+ s->source_dsa.server_dn_str = ldb_msg_find_attr_as_string(s->ldap1.rootdse, "serverName", NULL);
+ if (!s->source_dsa.server_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ s->source_dsa.ntds_dn_str = ldb_msg_find_attr_as_string(s->ldap1.rootdse, "dsServiceName", NULL);
+ if (!s->source_dsa.ntds_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_ldap1_crossref_behavior_version(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ static const char *attrs[] = {
+ "msDs-Behavior-Version",
+ NULL
+ };
+
+ basedn = ldb_dn_new(s, s->ldap1.ldb, s->forest.config_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_ONELEVEL, attrs,
+ "(cn=Partitions)");
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->forest.crossref_behavior_version = ldb_msg_find_attr_as_uint(r->msgs[0], "msDs-Behavior-Version", 0);
+ if (s->forest.crossref_behavior_version >
+ get_dc_function_level(s->libnet->lp_ctx)) {
+ talloc_free(r);
+ DEBUG(0,("The servers function level %u is above 'ads:dc function level' of %u\n",
+ s->forest.crossref_behavior_version,
+ get_dc_function_level(s->libnet->lp_ctx)));
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ talloc_free(r);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_ldap1_domain_behavior_version(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ static const char *attrs[] = {
+ "msDs-Behavior-Version",
+ NULL
+ };
+
+ basedn = ldb_dn_new(s, s->ldap1.ldb, s->domain.dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE, attrs,
+ "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->domain.behavior_version = ldb_msg_find_attr_as_uint(r->msgs[0], "msDs-Behavior-Version", 0);
+ if (s->domain.behavior_version >
+ get_dc_function_level(s->libnet->lp_ctx)) {
+ talloc_free(r);
+ DEBUG(0,("The servers function level %u is above 'ads:dc function level' of %u\n",
+ s->forest.crossref_behavior_version,
+ get_dc_function_level(s->libnet->lp_ctx)));
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ talloc_free(r);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_ldap1_schema_object_version(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ static const char *attrs[] = {
+ "objectVersion",
+ NULL
+ };
+
+ basedn = ldb_dn_new(s, s->ldap1.ldb, s->forest.schema_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE, attrs,
+ "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->forest.schema_object_version = ldb_msg_find_attr_as_uint(r->msgs[0], "objectVersion", 0);
+
+ talloc_free(r);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_ldap1_w2k3_update_revision(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ static const char *attrs[] = {
+ "revision",
+ NULL
+ };
+
+ basedn = ldb_dn_new_fmt(s, s->ldap1.ldb, "CN=Windows2003Update,CN=DomainUpdates,CN=System,%s",
+ s->domain.dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE, attrs,
+ "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ /* w2k doesn't have this object */
+ s->domain.w2k3_update_revision = 0;
+ return NT_STATUS_OK;
+ } else if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->domain.w2k3_update_revision = ldb_msg_find_attr_as_uint(r->msgs[0], "revision", 0);
+
+ talloc_free(r);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_ldap1_infrastructure_fsmo(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ struct ldb_dn *ntds_dn;
+ struct ldb_dn *server_dn;
+ static const char *dns_attrs[] = {
+ "dnsHostName",
+ NULL
+ };
+ static const char *guid_attrs[] = {
+ "objectGUID",
+ NULL
+ };
+
+ ret = dsdb_wellknown_dn(s->ldap1.ldb, s,
+ ldb_get_default_basedn(s->ldap1.ldb),
+ DS_GUID_INFRASTRUCTURE_CONTAINER,
+ &basedn);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,("Failed to get well known DN for DS_GUID_INFRASTRUCTURE_CONTAINER on %s: %s\n",
+ ldb_dn_get_linearized(ldb_get_default_basedn(s->ldap1.ldb)),
+ ldb_errstring(s->ldap1.ldb)));
+ return NT_STATUS_LDAP(ret);
+ }
+
+ ret = samdb_reference_dn(s->ldap1.ldb, s, basedn, "fSMORoleOwner", &ntds_dn);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,("Failed to get reference DN from fsmoRoleOwner on %s: %s\n",
+ ldb_dn_get_linearized(basedn),
+ ldb_errstring(s->ldap1.ldb)));
+ talloc_free(basedn);
+ return NT_STATUS_LDAP(ret);
+ }
+
+ s->infrastructure_fsmo.ntds_dn_str = ldb_dn_get_linearized(ntds_dn);
+ NT_STATUS_HAVE_NO_MEMORY(s->infrastructure_fsmo.ntds_dn_str);
+
+ server_dn = ldb_dn_get_parent(s, ntds_dn);
+ NT_STATUS_HAVE_NO_MEMORY(server_dn);
+
+ s->infrastructure_fsmo.server_dn_str = ldb_dn_alloc_linearized(s, server_dn);
+ NT_STATUS_HAVE_NO_MEMORY(s->infrastructure_fsmo.server_dn_str);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, server_dn, LDB_SCOPE_BASE,
+ dns_attrs, "(objectClass=*)");
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,("Failed to get server DN %s: %s\n",
+ ldb_dn_get_linearized(server_dn),
+ ldb_errstring(s->ldap1.ldb)));
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->infrastructure_fsmo.dns_name = ldb_msg_find_attr_as_string(r->msgs[0], "dnsHostName", NULL);
+ if (!s->infrastructure_fsmo.dns_name) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ talloc_steal(s, s->infrastructure_fsmo.dns_name);
+
+ talloc_free(r);
+
+ ldb_dn_remove_extended_components(ntds_dn);
+ ret = ldb_search(s->ldap1.ldb, s, &r, ntds_dn, LDB_SCOPE_BASE,
+ guid_attrs, "(objectClass=*)");
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,("Failed to get NTDS Settings DN %s: %s\n",
+ ldb_dn_get_linearized(ntds_dn),
+ ldb_errstring(s->ldap1.ldb)));
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->infrastructure_fsmo.ntds_guid = samdb_result_guid(r->msgs[0], "objectGUID");
+
+ talloc_free(r);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_ldap1_rid_manager_fsmo(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ const char *reference_dn_str;
+ struct ldb_dn *ntds_dn;
+ struct ldb_dn *server_dn;
+ static const char *rid_attrs[] = {
+ "rIDManagerReference",
+ NULL
+ };
+ static const char *fsmo_attrs[] = {
+ "fSMORoleOwner",
+ NULL
+ };
+ static const char *dns_attrs[] = {
+ "dnsHostName",
+ NULL
+ };
+ static const char *guid_attrs[] = {
+ "objectGUID",
+ NULL
+ };
+
+ basedn = ldb_dn_new(s, s->ldap1.ldb, s->domain.dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE,
+ rid_attrs, "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ reference_dn_str = ldb_msg_find_attr_as_string(r->msgs[0], "rIDManagerReference", NULL);
+ if (!reference_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+
+ basedn = ldb_dn_new(s, s->ldap1.ldb, reference_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ talloc_free(r);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE,
+ fsmo_attrs, "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->rid_manager_fsmo.ntds_dn_str = ldb_msg_find_attr_as_string(r->msgs[0], "fSMORoleOwner", NULL);
+ if (!s->rid_manager_fsmo.ntds_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ talloc_steal(s, s->rid_manager_fsmo.ntds_dn_str);
+
+ talloc_free(r);
+
+ ntds_dn = ldb_dn_new(s, s->ldap1.ldb, s->rid_manager_fsmo.ntds_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(ntds_dn);
+
+ server_dn = ldb_dn_get_parent(s, ntds_dn);
+ NT_STATUS_HAVE_NO_MEMORY(server_dn);
+
+ s->rid_manager_fsmo.server_dn_str = ldb_dn_alloc_linearized(s, server_dn);
+ NT_STATUS_HAVE_NO_MEMORY(s->rid_manager_fsmo.server_dn_str);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, server_dn, LDB_SCOPE_BASE,
+ dns_attrs, "(objectClass=*)");
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->rid_manager_fsmo.dns_name = ldb_msg_find_attr_as_string(r->msgs[0], "dnsHostName", NULL);
+ if (!s->rid_manager_fsmo.dns_name) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ talloc_steal(s, s->rid_manager_fsmo.dns_name);
+
+ talloc_free(r);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, ntds_dn, LDB_SCOPE_BASE,
+ guid_attrs, "(objectClass=*)");
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->rid_manager_fsmo.ntds_guid = samdb_result_guid(r->msgs[0], "objectGUID");
+
+ talloc_free(r);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_ldap1_site_object(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+
+ basedn = ldb_dn_new_fmt(s, s->ldap1.ldb, "CN=%s,CN=Sites,%s",
+ s->dest_dsa.site_name,
+ s->forest.config_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE,
+ NULL, "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->dest_dsa.site_guid = samdb_result_guid(r->msgs[0], "objectGUID");
+
+ talloc_free(r);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_check_options(struct libnet_BecomeDC_state *s)
+{
+ if (!s->callbacks.check_options) return NT_STATUS_OK;
+
+ s->_co.domain = &s->domain;
+ s->_co.forest = &s->forest;
+ s->_co.source_dsa = &s->source_dsa;
+
+ return s->callbacks.check_options(s->callbacks.private_data, &s->_co);
+}
+
+static NTSTATUS becomeDC_ldap1_computer_object(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ static const char *attrs[] = {
+ "distinguishedName",
+ "userAccountControl",
+ NULL
+ };
+
+ basedn = ldb_dn_new(s, s->ldap1.ldb, s->domain.dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_SUBTREE, attrs,
+ "(&(|(objectClass=user)(objectClass=computer))(sAMAccountName=%s$))",
+ s->dest_dsa.netbios_name);
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->dest_dsa.computer_dn_str = ldb_msg_find_attr_as_string(r->msgs[0], "distinguishedName", NULL);
+ if (!s->dest_dsa.computer_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ talloc_steal(s, s->dest_dsa.computer_dn_str);
+
+ s->dest_dsa.user_account_control = ldb_msg_find_attr_as_uint(r->msgs[0], "userAccountControl", 0);
+
+ talloc_free(r);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_ldap1_server_object_1(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ const char *server_reference_dn_str;
+ struct ldb_dn *server_reference_dn;
+ struct ldb_dn *computer_dn;
+
+ basedn = ldb_dn_new_fmt(s, s->ldap1.ldb, "CN=%s,CN=Servers,CN=%s,CN=Sites,%s",
+ s->dest_dsa.netbios_name,
+ s->dest_dsa.site_name,
+ s->forest.config_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE,
+ NULL, "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ /* if the object doesn't exist, we'll create it later */
+ return NT_STATUS_OK;
+ } else if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ server_reference_dn_str = ldb_msg_find_attr_as_string(r->msgs[0], "serverReference", NULL);
+ if (server_reference_dn_str) {
+ server_reference_dn = ldb_dn_new(r, s->ldap1.ldb, server_reference_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(server_reference_dn);
+
+ computer_dn = ldb_dn_new(r, s->ldap1.ldb, s->dest_dsa.computer_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(computer_dn);
+
+ /*
+ * if the server object belongs to another DC in another domain
+ * in the forest, we should not touch this object!
+ */
+ if (ldb_dn_compare(computer_dn, server_reference_dn) != 0) {
+ talloc_free(r);
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+ }
+
+ /* if the server object is already for the dest_dsa, then we don't need to create it */
+ s->dest_dsa.server_dn_str = ldb_msg_find_attr_as_string(r->msgs[0], "distinguishedName", NULL);
+ if (!s->dest_dsa.server_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ talloc_steal(s, s->dest_dsa.server_dn_str);
+
+ talloc_free(r);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_ldap1_server_object_2(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ const char *server_reference_bl_dn_str;
+ static const char *attrs[] = {
+ "serverReferenceBL",
+ NULL
+ };
+
+ /* if the server_dn_str has a valid value, we skip this lookup */
+ if (s->dest_dsa.server_dn_str) return NT_STATUS_OK;
+
+ basedn = ldb_dn_new(s, s->ldap1.ldb, s->dest_dsa.computer_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE,
+ attrs, "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ server_reference_bl_dn_str = ldb_msg_find_attr_as_string(r->msgs[0], "serverReferenceBL", NULL);
+ if (!server_reference_bl_dn_str) {
+ /* if no back link is present, we're done for this function */
+ talloc_free(r);
+ return NT_STATUS_OK;
+ }
+
+ /* if the server object is already for the dest_dsa, then we don't need to create it */
+ s->dest_dsa.server_dn_str = ldb_msg_find_attr_as_string(r->msgs[0], "serverReferenceBL", NULL);
+ if (s->dest_dsa.server_dn_str) {
+ /* if a back link is present, we know that the server object is present */
+ talloc_steal(s, s->dest_dsa.server_dn_str);
+ }
+
+ talloc_free(r);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_ldap1_server_object_add(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_message *msg;
+ char *server_dn_str;
+
+ /* if the server_dn_str has a valid value, we skip this lookup */
+ if (s->dest_dsa.server_dn_str) return NT_STATUS_OK;
+
+ msg = ldb_msg_new(s);
+ NT_STATUS_HAVE_NO_MEMORY(msg);
+
+ msg->dn = ldb_dn_new_fmt(msg, s->ldap1.ldb, "CN=%s,CN=Servers,CN=%s,CN=Sites,%s",
+ s->dest_dsa.netbios_name,
+ s->dest_dsa.site_name,
+ s->forest.config_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(msg->dn);
+
+ ret = ldb_msg_add_string(msg, "objectClass", "server");
+ if (ret != 0) {
+ talloc_free(msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ret = ldb_msg_add_string(msg, "systemFlags", "50000000");
+ if (ret != 0) {
+ talloc_free(msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ret = ldb_msg_add_string(msg, "serverReference", s->dest_dsa.computer_dn_str);
+ if (ret != 0) {
+ talloc_free(msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ server_dn_str = ldb_dn_alloc_linearized(s, msg->dn);
+ NT_STATUS_HAVE_NO_MEMORY(server_dn_str);
+
+ ret = ldb_add(s->ldap1.ldb, msg);
+ talloc_free(msg);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(server_dn_str);
+ return NT_STATUS_LDAP(ret);
+ }
+
+ s->dest_dsa.server_dn_str = server_dn_str;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_ldap1_server_object_modify(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_message *msg;
+ unsigned int i;
+
+ /* make a 'modify' msg, and only for serverReference */
+ msg = ldb_msg_new(s);
+ NT_STATUS_HAVE_NO_MEMORY(msg);
+ msg->dn = ldb_dn_new(msg, s->ldap1.ldb, s->dest_dsa.server_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(msg->dn);
+
+ ret = ldb_msg_add_string(msg, "serverReference", s->dest_dsa.computer_dn_str);
+ if (ret != 0) {
+ talloc_free(msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* mark all the message elements (should be just one)
+ as LDB_FLAG_MOD_ADD */
+ for (i=0;i<msg->num_elements;i++) {
+ msg->elements[i].flags = LDB_FLAG_MOD_ADD;
+ }
+
+ ret = ldb_modify(s->ldap1.ldb, msg);
+ if (ret == LDB_SUCCESS) {
+ talloc_free(msg);
+ return NT_STATUS_OK;
+ } else if (ret == LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS) {
+ /* retry with LDB_FLAG_MOD_REPLACE */
+ } else {
+ talloc_free(msg);
+ return NT_STATUS_LDAP(ret);
+ }
+
+ /* mark all the message elements (should be just one)
+ as LDB_FLAG_MOD_REPLACE */
+ for (i=0;i<msg->num_elements;i++) {
+ msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ ret = ldb_modify(s->ldap1.ldb, msg);
+ talloc_free(msg);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static void becomeDC_drsuapi_connect_send(struct libnet_BecomeDC_state *s,
+ struct becomeDC_drsuapi *drsuapi,
+ void (*recv_fn)(struct composite_context *req));
+static void becomeDC_drsuapi1_connect_recv(struct composite_context *req);
+static void becomeDC_connect_ldap2(struct libnet_BecomeDC_state *s);
+
+static void becomeDC_connect_ldap1(struct libnet_BecomeDC_state *s)
+{
+ struct composite_context *c = s->creq;
+
+ c->status = becomeDC_ldap_connect(s, &s->ldap1);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap1_rootdse(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap1_crossref_behavior_version(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap1_domain_behavior_version(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap1_schema_object_version(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap1_w2k3_update_revision(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap1_infrastructure_fsmo(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap1_rid_manager_fsmo(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap1_site_object(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_check_options(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap1_computer_object(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap1_server_object_1(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap1_server_object_2(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap1_server_object_add(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap1_server_object_modify(s);
+ if (!composite_is_ok(c)) return;
+
+ becomeDC_drsuapi_connect_send(s, &s->drsuapi1, becomeDC_drsuapi1_connect_recv);
+}
+
+static void becomeDC_drsuapi_connect_send(struct libnet_BecomeDC_state *s,
+ struct becomeDC_drsuapi *drsuapi,
+ void (*recv_fn)(struct composite_context *req))
+{
+ struct composite_context *c = s->creq;
+ struct composite_context *creq;
+ char *binding_str;
+
+ drsuapi->s = s;
+
+ if (!drsuapi->binding) {
+ const char *krb5_str = "";
+ const char *print_str = "";
+ /*
+ * Note: Replication only works with Windows 2000 when 'krb5' is
+ * passed as auth_type here. If NTLMSSP is used, Windows
+ * 2000 returns garbage in the DsGetNCChanges() response
+ * if encrypted password attributes would be in the
+ * response. That means the replication of the schema and
+ * configuration partition works fine, but it fails for
+ * the domain partition.
+ */
+ if (lpcfg_parm_bool(s->libnet->lp_ctx, NULL, "become_dc",
+ "force krb5", true))
+ {
+ krb5_str = "krb5,";
+ }
+ if (lpcfg_parm_bool(s->libnet->lp_ctx, NULL, "become_dc",
+ "print", false))
+ {
+ print_str = "print,";
+ }
+ binding_str = talloc_asprintf(s, "ncacn_ip_tcp:%s[%s%sseal,target_hostname=%s]",
+ s->source_dsa.address,
+ krb5_str, print_str,
+ s->source_dsa.dns_name);
+ if (composite_nomem(binding_str, c)) return;
+ c->status = dcerpc_parse_binding(s, binding_str, &drsuapi->binding);
+ talloc_free(binding_str);
+ if (!composite_is_ok(c)) return;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ c->status = dcerpc_binding_set_flags(drsuapi->binding,
+ DCERPC_DEBUG_PRINT_BOTH,
+ 0);
+ if (!composite_is_ok(c)) return;
+ }
+
+ creq = dcerpc_pipe_connect_b_send(s, drsuapi->binding, &ndr_table_drsuapi,
+ s->libnet->cred, s->libnet->event_ctx,
+ s->libnet->lp_ctx);
+ composite_continue(c, creq, recv_fn, s);
+}
+
+static void becomeDC_drsuapi_bind_send(struct libnet_BecomeDC_state *s,
+ struct becomeDC_drsuapi *drsuapi,
+ void (*recv_fn)(struct tevent_req *subreq));
+static void becomeDC_drsuapi1_bind_recv(struct tevent_req *subreq);
+
+static void becomeDC_drsuapi1_connect_recv(struct composite_context *req)
+{
+ struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
+ struct libnet_BecomeDC_state);
+ struct composite_context *c = s->creq;
+
+ c->status = dcerpc_pipe_connect_b_recv(req, s, &s->drsuapi1.pipe);
+ if (!composite_is_ok(c)) return;
+
+ s->drsuapi1.drsuapi_handle = s->drsuapi1.pipe->binding_handle;
+
+ c->status = gensec_session_key(s->drsuapi1.pipe->conn->security_state.generic_state,
+ s,
+ &s->drsuapi1.gensec_skey);
+ if (!composite_is_ok(c)) return;
+
+ becomeDC_drsuapi_bind_send(s, &s->drsuapi1, becomeDC_drsuapi1_bind_recv);
+}
+
+static void becomeDC_drsuapi_bind_send(struct libnet_BecomeDC_state *s,
+ struct becomeDC_drsuapi *drsuapi,
+ void (*recv_fn)(struct tevent_req *subreq))
+{
+ struct composite_context *c = s->creq;
+ struct drsuapi_DsBindInfo28 *bind_info28;
+ struct tevent_req *subreq;
+
+ GUID_from_string(DRSUAPI_DS_BIND_GUID_W2K3, &drsuapi->bind_guid);
+
+ bind_info28 = &drsuapi->local_info28;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_BASE;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2;
+ if (s->domain.behavior_version >= DS_DOMAIN_FUNCTION_2003) {
+ /* TODO: find out how this is really triggered! */
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION;
+ }
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V5;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT;
+#if 0 /* we don't support XPRESS compression yet */
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_XPRESS_COMPRESS;
+#endif
+ bind_info28->site_guid = s->dest_dsa.site_guid;
+ bind_info28->pid = 0;
+ bind_info28->repl_epoch = 0;
+
+ drsuapi->bind_info_ctr.length = 28;
+ drsuapi->bind_info_ctr.info.info28 = *bind_info28;
+
+ drsuapi->bind_r.in.bind_guid = &drsuapi->bind_guid;
+ drsuapi->bind_r.in.bind_info = &drsuapi->bind_info_ctr;
+ drsuapi->bind_r.out.bind_handle = &drsuapi->bind_handle;
+
+ subreq = dcerpc_drsuapi_DsBind_r_send(s, c->event_ctx,
+ drsuapi->drsuapi_handle,
+ &drsuapi->bind_r);
+ if (composite_nomem(subreq, c)) return;
+ tevent_req_set_callback(subreq, recv_fn, s);
+}
+
+static WERROR becomeDC_drsuapi_bind_recv(struct libnet_BecomeDC_state *s,
+ struct becomeDC_drsuapi *drsuapi)
+{
+ if (!W_ERROR_IS_OK(drsuapi->bind_r.out.result)) {
+ return drsuapi->bind_r.out.result;
+ }
+
+ ZERO_STRUCT(drsuapi->remote_info28);
+ if (drsuapi->bind_r.out.bind_info) {
+ switch (drsuapi->bind_r.out.bind_info->length) {
+ case 24: {
+ struct drsuapi_DsBindInfo24 *info24;
+ info24 = &drsuapi->bind_r.out.bind_info->info.info24;
+ drsuapi->remote_info28.supported_extensions = info24->supported_extensions;
+ drsuapi->remote_info28.site_guid = info24->site_guid;
+ drsuapi->remote_info28.pid = info24->pid;
+ drsuapi->remote_info28.repl_epoch = 0;
+ break;
+ }
+ case 28: {
+ drsuapi->remote_info28 = drsuapi->bind_r.out.bind_info->info.info28;
+ break;
+ }
+ case 32: {
+ struct drsuapi_DsBindInfo32 *info32;
+ info32 = &drsuapi->bind_r.out.bind_info->info.info32;
+ drsuapi->remote_info28.supported_extensions = info32->supported_extensions;
+ drsuapi->remote_info28.site_guid = info32->site_guid;
+ drsuapi->remote_info28.pid = info32->pid;
+ drsuapi->remote_info28.repl_epoch = info32->repl_epoch;
+ break;
+ }
+ case 48: {
+ struct drsuapi_DsBindInfo48 *info48;
+ info48 = &drsuapi->bind_r.out.bind_info->info.info48;
+ drsuapi->remote_info28.supported_extensions = info48->supported_extensions;
+ drsuapi->remote_info28.site_guid = info48->site_guid;
+ drsuapi->remote_info28.pid = info48->pid;
+ drsuapi->remote_info28.repl_epoch = info48->repl_epoch;
+ break;
+ }
+ case 52: {
+ struct drsuapi_DsBindInfo52 *info52;
+ info52 = &drsuapi->bind_r.out.bind_info->info.info52;
+ drsuapi->remote_info28.supported_extensions = info52->supported_extensions;
+ drsuapi->remote_info28.site_guid = info52->site_guid;
+ drsuapi->remote_info28.pid = info52->pid;
+ drsuapi->remote_info28.repl_epoch = info52->repl_epoch;
+ break;
+ }
+ default:
+ DEBUG(1, ("Warning: invalid info length in bind info: %d\n",
+ drsuapi->bind_r.out.bind_info->length));
+ break;
+ }
+ }
+
+ return WERR_OK;
+}
+
+static void becomeDC_drsuapi1_add_entry_send(struct libnet_BecomeDC_state *s);
+
+static void becomeDC_drsuapi1_bind_recv(struct tevent_req *subreq)
+{
+ struct libnet_BecomeDC_state *s = tevent_req_callback_data(subreq,
+ struct libnet_BecomeDC_state);
+ struct composite_context *c = s->creq;
+ WERROR status;
+
+ c->status = dcerpc_drsuapi_DsBind_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ status = becomeDC_drsuapi_bind_recv(s, &s->drsuapi1);
+ if (!W_ERROR_IS_OK(status)) {
+ composite_error(c, werror_to_ntstatus(status));
+ return;
+ }
+
+ becomeDC_drsuapi1_add_entry_send(s);
+}
+
+static void becomeDC_drsuapi1_add_entry_recv(struct tevent_req *subreq);
+
+static void becomeDC_drsuapi1_add_entry_send(struct libnet_BecomeDC_state *s)
+{
+ struct composite_context *c = s->creq;
+ struct drsuapi_DsAddEntry *r;
+ struct drsuapi_DsReplicaObjectIdentifier *identifier;
+ uint32_t num_attrs, i = 0;
+ struct drsuapi_DsReplicaAttribute *attrs;
+ enum ndr_err_code ndr_err;
+ bool w2k3;
+ struct tevent_req *subreq;
+
+ /* choose a random invocationId */
+ s->dest_dsa.invocation_id = GUID_random();
+
+ /*
+ * if the schema version indicates w2k3, then also send some w2k3
+ * specific attributes.
+ */
+ if (s->forest.schema_object_version >= 30) {
+ w2k3 = true;
+ } else {
+ w2k3 = false;
+ }
+
+ r = talloc_zero(s, struct drsuapi_DsAddEntry);
+ if (composite_nomem(r, c)) return;
+
+ /* setup identifier */
+ identifier = talloc(r, struct drsuapi_DsReplicaObjectIdentifier);
+ if (composite_nomem(identifier, c)) return;
+ identifier->guid = GUID_zero();
+ identifier->sid = s->zero_sid;
+ identifier->dn = talloc_asprintf(identifier, "CN=NTDS Settings,%s",
+ s->dest_dsa.server_dn_str);
+ if (composite_nomem(identifier->dn, c)) return;
+
+ /* allocate attribute array */
+ num_attrs = 12;
+ attrs = talloc_array(r, struct drsuapi_DsReplicaAttribute, num_attrs);
+ if (composite_nomem(attrs, c)) return;
+
+ /* ntSecurityDescriptor */
+ {
+ struct drsuapi_DsAttributeValue *vs;
+ DATA_BLOB *vd;
+ struct security_descriptor *v;
+ struct dom_sid *domain_admins_sid;
+ const char *domain_admins_sid_str;
+
+ vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
+ if (composite_nomem(vs, c)) return;
+
+ vd = talloc_array(vs, DATA_BLOB, 1);
+ if (composite_nomem(vd, c)) return;
+
+ domain_admins_sid = dom_sid_add_rid(vs, s->domain.sid, DOMAIN_RID_ADMINS);
+ if (composite_nomem(domain_admins_sid, c)) return;
+
+ domain_admins_sid_str = dom_sid_string(domain_admins_sid, domain_admins_sid);
+ if (composite_nomem(domain_admins_sid_str, c)) return;
+
+ v = security_descriptor_dacl_create(vd,
+ 0,
+ /* owner: domain admins */
+ domain_admins_sid_str,
+ /* owner group: domain admins */
+ domain_admins_sid_str,
+ /* authenticated users */
+ SID_NT_AUTHENTICATED_USERS,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_STD_READ_CONTROL |
+ SEC_ADS_LIST |
+ SEC_ADS_READ_PROP |
+ SEC_ADS_LIST_OBJECT,
+ 0,
+ /* domain admins */
+ domain_admins_sid_str,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_STD_REQUIRED |
+ SEC_ADS_CREATE_CHILD |
+ SEC_ADS_LIST |
+ SEC_ADS_SELF_WRITE |
+ SEC_ADS_READ_PROP |
+ SEC_ADS_WRITE_PROP |
+ SEC_ADS_DELETE_TREE |
+ SEC_ADS_LIST_OBJECT |
+ SEC_ADS_CONTROL_ACCESS,
+ 0,
+ /* system */
+ SID_NT_SYSTEM,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_STD_REQUIRED |
+ SEC_ADS_CREATE_CHILD |
+ SEC_ADS_DELETE_CHILD |
+ SEC_ADS_LIST |
+ SEC_ADS_SELF_WRITE |
+ SEC_ADS_READ_PROP |
+ SEC_ADS_WRITE_PROP |
+ SEC_ADS_DELETE_TREE |
+ SEC_ADS_LIST_OBJECT |
+ SEC_ADS_CONTROL_ACCESS,
+ 0,
+ /* end */
+ NULL);
+ if (composite_nomem(v, c)) return;
+
+ ndr_err = ndr_push_struct_blob(&vd[0], vd, v,
+ (ndr_push_flags_fn_t)ndr_push_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ c->status = ndr_map_error2ntstatus(ndr_err);
+ if (!composite_is_ok(c)) return;
+ }
+
+ vs[0].blob = &vd[0];
+
+ attrs[i].attid = DRSUAPI_ATTID_ntSecurityDescriptor;
+ attrs[i].value_ctr.num_values = 1;
+ attrs[i].value_ctr.values = vs;
+
+ i++;
+ }
+
+ /* objectClass: nTDSDSA */
+ {
+ struct drsuapi_DsAttributeValue *vs;
+ DATA_BLOB *vd;
+
+ vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
+ if (composite_nomem(vs, c)) return;
+
+ vd = talloc_array(vs, DATA_BLOB, 1);
+ if (composite_nomem(vd, c)) return;
+
+ vd[0] = data_blob_talloc(vd, NULL, 4);
+ if (composite_nomem(vd[0].data, c)) return;
+
+ /* value for nTDSDSA */
+ SIVAL(vd[0].data, 0, 0x0017002F);
+
+ vs[0].blob = &vd[0];
+
+ attrs[i].attid = DRSUAPI_ATTID_objectClass;
+ attrs[i].value_ctr.num_values = 1;
+ attrs[i].value_ctr.values = vs;
+
+ i++;
+ }
+
+ /* objectCategory: CN=NTDS-DSA,CN=Schema,... or CN=NTDS-DSA-RO,CN=Schema,... */
+ {
+ struct drsuapi_DsAttributeValue *vs;
+ DATA_BLOB *vd;
+ struct drsuapi_DsReplicaObjectIdentifier3 v[1];
+
+ vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
+ if (composite_nomem(vs, c)) return;
+
+ vd = talloc_array(vs, DATA_BLOB, 1);
+ if (composite_nomem(vd, c)) return;
+
+ v[0].guid = GUID_zero();
+ v[0].sid = s->zero_sid;
+
+ if (s->rodc_join) {
+ v[0].dn = talloc_asprintf(vd, "CN=NTDS-DSA-RO,%s",
+ s->forest.schema_dn_str);
+ } else {
+ v[0].dn = talloc_asprintf(vd, "CN=NTDS-DSA,%s",
+ s->forest.schema_dn_str);
+ }
+ if (composite_nomem(v[0].dn, c)) return;
+
+ ndr_err = ndr_push_struct_blob(&vd[0], vd, &v[0],
+ (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ c->status = ndr_map_error2ntstatus(ndr_err);
+ if (!composite_is_ok(c)) return;
+ }
+
+ vs[0].blob = &vd[0];
+
+ attrs[i].attid = DRSUAPI_ATTID_objectCategory;
+ attrs[i].value_ctr.num_values = 1;
+ attrs[i].value_ctr.values = vs;
+
+ i++;
+ }
+
+ /* invocationId: random guid */
+ {
+ struct drsuapi_DsAttributeValue *vs;
+ DATA_BLOB *vd;
+ const struct GUID *v;
+
+ vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
+ if (composite_nomem(vs, c)) return;
+
+ vd = talloc_array(vs, DATA_BLOB, 1);
+ if (composite_nomem(vd, c)) return;
+
+ v = &s->dest_dsa.invocation_id;
+
+ c->status = GUID_to_ndr_blob(v, vd, &vd[0]);
+ if (!composite_is_ok(c)) return;
+
+ vs[0].blob = &vd[0];
+
+ attrs[i].attid = DRSUAPI_ATTID_invocationId;
+ attrs[i].value_ctr.num_values = 1;
+ attrs[i].value_ctr.values = vs;
+
+ i++;
+ }
+
+ /* hasMasterNCs: ... */
+ {
+ struct drsuapi_DsAttributeValue *vs;
+ DATA_BLOB *vd;
+ struct drsuapi_DsReplicaObjectIdentifier3 v[3];
+
+ vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 3);
+ if (composite_nomem(vs, c)) return;
+
+ vd = talloc_array(vs, DATA_BLOB, 3);
+ if (composite_nomem(vd, c)) return;
+
+ v[0].guid = GUID_zero();
+ v[0].sid = s->zero_sid;
+ v[0].dn = s->forest.config_dn_str;
+
+ v[1].guid = GUID_zero();
+ v[1].sid = s->zero_sid;
+ v[1].dn = s->domain.dn_str;
+
+ v[2].guid = GUID_zero();
+ v[2].sid = s->zero_sid;
+ v[2].dn = s->forest.schema_dn_str;
+
+ ndr_err = ndr_push_struct_blob(&vd[0], vd, &v[0],
+ (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ c->status = ndr_map_error2ntstatus(ndr_err);
+ if (!composite_is_ok(c)) return;
+ }
+
+ ndr_err = ndr_push_struct_blob(&vd[1], vd, &v[1],
+ (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ c->status = ndr_map_error2ntstatus(ndr_err);
+ if (!composite_is_ok(c)) return;
+ }
+
+ ndr_err = ndr_push_struct_blob(&vd[2], vd, &v[2],
+ (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ c->status = ndr_map_error2ntstatus(ndr_err);
+ if (!composite_is_ok(c)) return;
+ }
+
+ vs[0].blob = &vd[0];
+ vs[1].blob = &vd[1];
+ vs[2].blob = &vd[2];
+
+ attrs[i].attid = DRSUAPI_ATTID_hasMasterNCs;
+ attrs[i].value_ctr.num_values = 3;
+ attrs[i].value_ctr.values = vs;
+
+ i++;
+ }
+
+ /* msDS-hasMasterNCs: ... */
+ if (w2k3) {
+ struct drsuapi_DsAttributeValue *vs;
+ DATA_BLOB *vd;
+ struct drsuapi_DsReplicaObjectIdentifier3 v[3];
+
+ vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 3);
+ if (composite_nomem(vs, c)) return;
+
+ vd = talloc_array(vs, DATA_BLOB, 3);
+ if (composite_nomem(vd, c)) return;
+
+ v[0].guid = GUID_zero();
+ v[0].sid = s->zero_sid;
+ v[0].dn = s->forest.config_dn_str;
+
+ v[1].guid = GUID_zero();
+ v[1].sid = s->zero_sid;
+ v[1].dn = s->domain.dn_str;
+
+ v[2].guid = GUID_zero();
+ v[2].sid = s->zero_sid;
+ v[2].dn = s->forest.schema_dn_str;
+
+ ndr_err = ndr_push_struct_blob(&vd[0], vd, &v[0],
+ (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ c->status = ndr_map_error2ntstatus(ndr_err);
+ if (!composite_is_ok(c)) return;
+ }
+
+ ndr_err = ndr_push_struct_blob(&vd[1], vd, &v[1],
+ (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ c->status = ndr_map_error2ntstatus(ndr_err);
+ if (!composite_is_ok(c)) return;
+ }
+
+ ndr_err = ndr_push_struct_blob(&vd[2], vd, &v[2],
+ (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ c->status = ndr_map_error2ntstatus(ndr_err);
+ if (!composite_is_ok(c)) return;
+ }
+
+ vs[0].blob = &vd[0];
+ vs[1].blob = &vd[1];
+ vs[2].blob = &vd[2];
+
+ attrs[i].attid = DRSUAPI_ATTID_msDS_hasMasterNCs;
+ attrs[i].value_ctr.num_values = 3;
+ attrs[i].value_ctr.values = vs;
+
+ i++;
+ }
+
+ /* dMDLocation: CN=Schema,... */
+ {
+ struct drsuapi_DsAttributeValue *vs;
+ DATA_BLOB *vd;
+ struct drsuapi_DsReplicaObjectIdentifier3 v[1];
+
+ vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
+ if (composite_nomem(vs, c)) return;
+
+ vd = talloc_array(vs, DATA_BLOB, 1);
+ if (composite_nomem(vd, c)) return;
+
+ v[0].guid = GUID_zero();
+ v[0].sid = s->zero_sid;
+ v[0].dn = s->forest.schema_dn_str;
+
+ ndr_err = ndr_push_struct_blob(&vd[0], vd, &v[0],
+ (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ c->status = ndr_map_error2ntstatus(ndr_err);
+ if (!composite_is_ok(c)) return;
+ }
+
+ vs[0].blob = &vd[0];
+
+ attrs[i].attid = DRSUAPI_ATTID_dMDLocation;
+ attrs[i].value_ctr.num_values = 1;
+ attrs[i].value_ctr.values = vs;
+
+ i++;
+ }
+
+ /* msDS-HasDomainNCs: <domain_partition> */
+ if (w2k3) {
+ struct drsuapi_DsAttributeValue *vs;
+ DATA_BLOB *vd;
+ struct drsuapi_DsReplicaObjectIdentifier3 v[1];
+
+ vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
+ if (composite_nomem(vs, c)) return;
+
+ vd = talloc_array(vs, DATA_BLOB, 1);
+ if (composite_nomem(vd, c)) return;
+
+ v[0].guid = GUID_zero();
+ v[0].sid = s->zero_sid;
+ v[0].dn = s->domain.dn_str;
+
+ ndr_err = ndr_push_struct_blob(&vd[0], vd, &v[0],
+ (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ c->status = ndr_map_error2ntstatus(ndr_err);
+ if (!composite_is_ok(c)) return;
+ }
+
+ vs[0].blob = &vd[0];
+
+ attrs[i].attid = DRSUAPI_ATTID_msDS_HasDomainNCs;
+ attrs[i].value_ctr.num_values = 1;
+ attrs[i].value_ctr.values = vs;
+
+ i++;
+ }
+
+ /* msDS-Behavior-Version */
+ if (w2k3) {
+ struct drsuapi_DsAttributeValue *vs;
+ DATA_BLOB *vd;
+
+ vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
+ if (composite_nomem(vs, c)) return;
+
+ vd = talloc_array(vs, DATA_BLOB, 1);
+ if (composite_nomem(vd, c)) return;
+
+ vd[0] = data_blob_talloc(vd, NULL, 4);
+ if (composite_nomem(vd[0].data, c)) return;
+
+ SIVAL(vd[0].data, 0, get_dc_function_level(s->libnet->lp_ctx));
+
+ vs[0].blob = &vd[0];
+
+ attrs[i].attid = DRSUAPI_ATTID_msDS_Behavior_Version;
+ attrs[i].value_ctr.num_values = 1;
+ attrs[i].value_ctr.values = vs;
+
+ i++;
+ }
+
+ /* systemFlags */
+ {
+ struct drsuapi_DsAttributeValue *vs;
+ DATA_BLOB *vd;
+
+ vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
+ if (composite_nomem(vs, c)) return;
+
+ vd = talloc_array(vs, DATA_BLOB, 1);
+ if (composite_nomem(vd, c)) return;
+
+ vd[0] = data_blob_talloc(vd, NULL, 4);
+ if (composite_nomem(vd[0].data, c)) return;
+
+ if (s->rodc_join) {
+ SIVAL(vd[0].data, 0, SYSTEM_FLAG_CONFIG_ALLOW_RENAME);
+ } else {
+ SIVAL(vd[0].data, 0, SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
+ }
+
+ vs[0].blob = &vd[0];
+
+ attrs[i].attid = DRSUAPI_ATTID_systemFlags;
+ attrs[i].value_ctr.num_values = 1;
+ attrs[i].value_ctr.values = vs;
+
+ i++;
+ }
+
+ /* serverReference: ... */
+ {
+ struct drsuapi_DsAttributeValue *vs;
+ DATA_BLOB *vd;
+ struct drsuapi_DsReplicaObjectIdentifier3 v[1];
+
+ vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
+ if (composite_nomem(vs, c)) return;
+
+ vd = talloc_array(vs, DATA_BLOB, 1);
+ if (composite_nomem(vd, c)) return;
+
+ v[0].guid = GUID_zero();
+ v[0].sid = s->zero_sid;
+ v[0].dn = s->dest_dsa.computer_dn_str;
+
+ ndr_err = ndr_push_struct_blob(&vd[0], vd, &v[0],
+ (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ c->status = ndr_map_error2ntstatus(ndr_err);
+ if (!composite_is_ok(c)) return;
+ }
+
+ vs[0].blob = &vd[0];
+
+ attrs[i].attid = DRSUAPI_ATTID_serverReference;
+ attrs[i].value_ctr.num_values = 1;
+ attrs[i].value_ctr.values = vs;
+
+ i++;
+ }
+
+ /* options:... */
+ if (s->rodc_join) {
+ struct drsuapi_DsAttributeValue *vs;
+ DATA_BLOB *vd;
+
+ vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
+ if (composite_nomem(vs, c)) return;
+
+ vd = talloc_array(vs, DATA_BLOB, 1);
+ if (composite_nomem(vd, c)) return;
+
+ vd[0] = data_blob_talloc(vd, NULL, 4);
+ if (composite_nomem(vd[0].data, c)) return;
+
+ SIVAL(vd[0].data, 0, DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL);
+
+ vs[0].blob = &vd[0];
+
+ attrs[i].attid = DRSUAPI_ATTID_options;
+ attrs[i].value_ctr.num_values = 1;
+ attrs[i].value_ctr.values = vs;
+
+ i++;
+ }
+
+ /* truncate the attribute list to the attribute count we have filled in */
+ num_attrs = i;
+
+ /* setup request structure */
+ r->in.bind_handle = &s->drsuapi1.bind_handle;
+ r->in.level = 2;
+ r->in.req = talloc(s, union drsuapi_DsAddEntryRequest);
+ r->in.req->req2.first_object.next_object = NULL;
+ r->in.req->req2.first_object.object.identifier = identifier;
+ r->in.req->req2.first_object.object.flags = 0x00000000;
+ r->in.req->req2.first_object.object.attribute_ctr.num_attributes= num_attrs;
+ r->in.req->req2.first_object.object.attribute_ctr.attributes = attrs;
+
+ r->out.level_out = talloc(s, uint32_t);
+ r->out.ctr = talloc(s, union drsuapi_DsAddEntryCtr);
+
+ s->ndr_struct_ptr = r;
+ subreq = dcerpc_drsuapi_DsAddEntry_r_send(s, c->event_ctx,
+ s->drsuapi1.drsuapi_handle, r);
+ if (composite_nomem(subreq, c)) return;
+ tevent_req_set_callback(subreq, becomeDC_drsuapi1_add_entry_recv, s);
+}
+
+static void becomeDC_drsuapi2_connect_recv(struct composite_context *req);
+static NTSTATUS becomeDC_prepare_db(struct libnet_BecomeDC_state *s);
+
+static void becomeDC_drsuapi1_add_entry_recv(struct tevent_req *subreq)
+{
+ struct libnet_BecomeDC_state *s = tevent_req_callback_data(subreq,
+ struct libnet_BecomeDC_state);
+ struct composite_context *c = s->creq;
+ struct drsuapi_DsAddEntry *r = talloc_get_type_abort(s->ndr_struct_ptr,
+ struct drsuapi_DsAddEntry);
+ char *binding_str;
+ uint32_t assoc_group_id;
+
+ s->ndr_struct_ptr = NULL;
+
+ c->status = dcerpc_drsuapi_DsAddEntry_r_recv(subreq, r);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ if (!W_ERROR_IS_OK(r->out.result)) {
+ composite_error(c, werror_to_ntstatus(r->out.result));
+ return;
+ }
+
+ if (*r->out.level_out == 3) {
+ WERROR status;
+ union drsuapi_DsAddEntry_ErrData *err_data = r->out.ctr->ctr3.err_data;
+
+ /* check for errors */
+ status = err_data ? err_data->v1.status : WERR_OK;
+ if (!W_ERROR_IS_OK(status)) {
+ struct drsuapi_DsAddEntryErrorInfo_Attr_V1 *attr_err;
+ struct drsuapi_DsAddEntry_AttrErrListItem_V1 *attr_err_li;
+ struct drsuapi_DsAddEntryErrorInfo_Name_V1 *name_err;
+ struct drsuapi_DsAddEntryErrorInfo_Referr_V1 *ref_err;
+ struct drsuapi_DsAddEntry_RefErrListItem_V1 *ref_li;
+
+ if (r->out.ctr->ctr3.err_ver != 1) {
+ composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ DEBUG(0,("DsAddEntry (R3) of '%s' failed: "
+ "Errors: dir_err = %d, status = %s;\n",
+ r->in.req->req3.first_object.object.identifier->dn,
+ err_data->v1.dir_err,
+ win_errstr(err_data->v1.status)));
+
+ if (!err_data->v1.info) {
+ DEBUG(0, ("DsAddEntry (R3): no error info returned!\n"));
+ composite_error(c, werror_to_ntstatus(status));
+ return;
+ }
+
+ /* dump more detailed error */
+ switch (err_data->v1.dir_err) {
+ case DRSUAPI_DIRERR_ATTRIBUTE:
+ /* Dump attribute errors */
+ attr_err = &err_data->v1.info->attr_err;
+ DEBUGADD(0,(" Attribute Error: object = %s, count = %d;\n",
+ attr_err->id->dn,
+ attr_err->count));
+ attr_err_li = &attr_err->first;
+ for (; attr_err_li; attr_err_li = attr_err_li->next) {
+ struct drsuapi_DsAddEntry_AttrErr_V1 *err = &attr_err_li->err_data;
+ DEBUGADD(0,(" Error: err = %s, problem = 0x%08X, attid = 0x%08X;\n",
+ win_errstr(err->extended_err),
+ err->problem,
+ err->attid));
+ /* TODO: should we print attribute value here? */
+ }
+ break;
+ case DRSUAPI_DIRERR_NAME:
+ /* Dump Name resolution error */
+ name_err = &err_data->v1.info->name_err;
+ DEBUGADD(0,(" Name Error: err = %s, problem = 0x%08X, id_matched = %s;\n",
+ win_errstr(name_err->extended_err),
+ name_err->problem,
+ name_err->id_matched->dn));
+ break;
+ case DRSUAPI_DIRERR_REFERRAL:
+ /* Dump Referral errors */
+ ref_err = &err_data->v1.info->referral_err;
+ DEBUGADD(0,(" Referral Error: extended_err = %s\n",
+ win_errstr(ref_err->extended_err)));
+ ref_li = &ref_err->refer;
+ for (; ref_li; ref_li = ref_li->next) {
+ struct drsuapi_DsaAddressListItem_V1 *addr;
+ DEBUGADD(0,(" Referral: id_target = %s, ref_type = 0x%04X,",
+ ref_li->id_target->dn,
+ ref_li->ref_type));
+ if (ref_li->is_choice_set) {
+ DEBUGADD(0,(" choice = 0x%02X, ",
+ ref_li->choice));
+ }
+ DEBUGADD(0,(" add_list ("));
+ for (addr = ref_li->addr_list; addr; addr = addr->next) {
+ DEBUGADD(0,("%s", addr->address->string));
+ if (addr->next) {
+ DEBUGADD(0,(", "));
+ }
+ }
+ DEBUGADD(0,(");\n"));
+ }
+ break;
+ case DRSUAPI_DIRERR_SECURITY:
+ /* Dump Security error. */
+ DEBUGADD(0,(" Security Error: extended_err = %s, problem = 0x%08X\n",
+ win_errstr(err_data->v1.info->security_err.extended_err),
+ err_data->v1.info->security_err.problem));
+ break;
+ case DRSUAPI_DIRERR_SERVICE:
+ /* Dump Service error. */
+ DEBUGADD(0,(" Service Error: extended_err = %s, problem = 0x%08X\n",
+ win_errstr(err_data->v1.info->service_err.extended_err),
+ err_data->v1.info->service_err.problem));
+ break;
+ case DRSUAPI_DIRERR_UPDATE:
+ /* Dump Update error. */
+ DEBUGADD(0,(" Update Error: extended_err = %s, problem = 0x%08X\n",
+ win_errstr(err_data->v1.info->update_err.extended_err),
+ err_data->v1.info->update_err.problem));
+ break;
+ case DRSUAPI_DIRERR_SYSTEM:
+ /* System error. */
+ DEBUGADD(0,(" System Error: extended_err = %s, problem = 0x%08X\n",
+ win_errstr(err_data->v1.info->system_err.extended_err),
+ err_data->v1.info->system_err.problem));
+ break;
+ case DRSUAPI_DIRERR_OK: /* mute compiler warnings */
+ default:
+ DEBUGADD(0,(" Unknown DIRERR error class returned!\n"));
+ break;
+ }
+
+ composite_error(c, werror_to_ntstatus(status));
+ return;
+ }
+
+ if (1 != r->out.ctr->ctr3.count) {
+ DEBUG(0,("DsAddEntry - Ctr3: something very wrong had happened - "
+ "method succeeded but objects returned are %d (expected 1).\n",
+ r->out.ctr->ctr3.count));
+ composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ s->dest_dsa.ntds_guid = r->out.ctr->ctr3.objects[0].guid;
+
+ } else if (*r->out.level_out == 2) {
+ if (DRSUAPI_DIRERR_OK != r->out.ctr->ctr2.dir_err) {
+ DEBUG(0,("DsAddEntry failed with: dir_err = %d, extended_err = %s\n",
+ r->out.ctr->ctr2.dir_err,
+ win_errstr(r->out.ctr->ctr2.extended_err)));
+ composite_error(c, werror_to_ntstatus(r->out.ctr->ctr2.extended_err));
+ return;
+ }
+
+ if (1 != r->out.ctr->ctr2.count) {
+ DEBUG(0,("DsAddEntry: something very wrong had happened - "
+ "method succeeded but objects returned are %d (expected 1). "
+ "Errors: dir_err = %d, extended_err = %s\n",
+ r->out.ctr->ctr2.count,
+ r->out.ctr->ctr2.dir_err,
+ win_errstr(r->out.ctr->ctr2.extended_err)));
+ composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ s->dest_dsa.ntds_guid = r->out.ctr->ctr2.objects[0].guid;
+ } else {
+ composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ talloc_free(r);
+
+ s->dest_dsa.ntds_dn_str = talloc_asprintf(s, "CN=NTDS Settings,%s",
+ s->dest_dsa.server_dn_str);
+ if (composite_nomem(s->dest_dsa.ntds_dn_str, c)) return;
+
+ c->status = becomeDC_prepare_db(s);
+ if (!composite_is_ok(c)) return;
+
+ /* this avoids the epmapper lookup on the 2nd connection */
+ binding_str = dcerpc_binding_string(s, s->drsuapi1.binding);
+ if (composite_nomem(binding_str, c)) return;
+
+ c->status = dcerpc_parse_binding(s, binding_str, &s->drsuapi2.binding);
+ talloc_free(binding_str);
+ if (!composite_is_ok(c)) return;
+
+ if (DEBUGLEVEL >= 10) {
+ c->status = dcerpc_binding_set_flags(s->drsuapi2.binding,
+ DCERPC_DEBUG_PRINT_BOTH,
+ 0);
+ if (!composite_is_ok(c)) return;
+ }
+
+ /* w2k3 uses the same assoc_group_id as on the first connection, so we do */
+ assoc_group_id = dcerpc_binding_get_assoc_group_id(s->drsuapi1.pipe->binding);
+ c->status = dcerpc_binding_set_assoc_group_id(s->drsuapi2.binding, assoc_group_id);
+ if (!composite_is_ok(c)) return;
+
+ becomeDC_drsuapi_connect_send(s, &s->drsuapi2, becomeDC_drsuapi2_connect_recv);
+}
+
+static NTSTATUS becomeDC_prepare_db(struct libnet_BecomeDC_state *s)
+{
+ if (!s->callbacks.prepare_db) return NT_STATUS_OK;
+
+ s->_pp.domain = &s->domain;
+ s->_pp.forest = &s->forest;
+ s->_pp.source_dsa = &s->source_dsa;
+ s->_pp.dest_dsa = &s->dest_dsa;
+
+ return s->callbacks.prepare_db(s->callbacks.private_data, &s->_pp);
+}
+
+static void becomeDC_drsuapi2_bind_recv(struct tevent_req *subreq);
+
+static void becomeDC_drsuapi2_connect_recv(struct composite_context *req)
+{
+ struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
+ struct libnet_BecomeDC_state);
+ struct composite_context *c = s->creq;
+
+ c->status = dcerpc_pipe_connect_b_recv(req, s, &s->drsuapi2.pipe);
+ if (!composite_is_ok(c)) return;
+
+ s->drsuapi2.drsuapi_handle = s->drsuapi2.pipe->binding_handle;
+
+ c->status = gensec_session_key(s->drsuapi2.pipe->conn->security_state.generic_state,
+ s,
+ &s->drsuapi2.gensec_skey);
+ if (!composite_is_ok(c)) return;
+
+ becomeDC_drsuapi_bind_send(s, &s->drsuapi2, becomeDC_drsuapi2_bind_recv);
+}
+
+static void becomeDC_drsuapi3_connect_recv(struct composite_context *req);
+
+static void becomeDC_drsuapi2_bind_recv(struct tevent_req *subreq)
+{
+ struct libnet_BecomeDC_state *s = tevent_req_callback_data(subreq,
+ struct libnet_BecomeDC_state);
+ struct composite_context *c = s->creq;
+ char *binding_str;
+ uint32_t assoc_group_id;
+ WERROR status;
+
+ c->status = dcerpc_drsuapi_DsBind_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ status = becomeDC_drsuapi_bind_recv(s, &s->drsuapi2);
+ if (!W_ERROR_IS_OK(status)) {
+ composite_error(c, werror_to_ntstatus(status));
+ return;
+ }
+
+ /* this avoids the epmapper lookup on the 3rd connection */
+ binding_str = dcerpc_binding_string(s, s->drsuapi1.binding);
+ if (composite_nomem(binding_str, c)) return;
+
+ c->status = dcerpc_parse_binding(s, binding_str, &s->drsuapi3.binding);
+ talloc_free(binding_str);
+ if (!composite_is_ok(c)) return;
+
+ if (DEBUGLEVEL >= 10) {
+ c->status = dcerpc_binding_set_flags(s->drsuapi3.binding,
+ DCERPC_DEBUG_PRINT_BOTH,
+ 0);
+ if (!composite_is_ok(c)) return;
+ }
+
+ /* w2k3 uses the same assoc_group_id as on the first connection, so we do */
+ assoc_group_id = dcerpc_binding_get_assoc_group_id(s->drsuapi1.pipe->binding);
+ c->status = dcerpc_binding_set_assoc_group_id(s->drsuapi3.binding, assoc_group_id);
+ if (!composite_is_ok(c)) return;
+ /* w2k3 uses the concurrent multiplex feature on the 3rd connection, so we do */
+ c->status = dcerpc_binding_set_flags(s->drsuapi3.binding,
+ DCERPC_CONCURRENT_MULTIPLEX,
+ 0);
+ if (!composite_is_ok(c)) return;
+
+ becomeDC_drsuapi_connect_send(s, &s->drsuapi3, becomeDC_drsuapi3_connect_recv);
+}
+
+static void becomeDC_drsuapi3_pull_schema_send(struct libnet_BecomeDC_state *s);
+
+static void becomeDC_drsuapi3_connect_recv(struct composite_context *req)
+{
+ struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
+ struct libnet_BecomeDC_state);
+ struct composite_context *c = s->creq;
+
+ c->status = dcerpc_pipe_connect_b_recv(req, s, &s->drsuapi3.pipe);
+ if (!composite_is_ok(c)) return;
+
+ s->drsuapi3.drsuapi_handle = s->drsuapi3.pipe->binding_handle;
+
+ c->status = gensec_session_key(s->drsuapi3.pipe->conn->security_state.generic_state,
+ s,
+ &s->drsuapi3.gensec_skey);
+ if (!composite_is_ok(c)) return;
+
+ becomeDC_drsuapi3_pull_schema_send(s);
+}
+
+static void becomeDC_drsuapi_pull_partition_send(struct libnet_BecomeDC_state *s,
+ struct becomeDC_drsuapi *drsuapi_h,
+ struct becomeDC_drsuapi *drsuapi_p,
+ struct libnet_BecomeDC_Partition *partition,
+ void (*recv_fn)(struct tevent_req *subreq))
+{
+ struct composite_context *c = s->creq;
+ struct drsuapi_DsGetNCChanges *r;
+ struct tevent_req *subreq;
+
+ r = talloc(s, struct drsuapi_DsGetNCChanges);
+ if (composite_nomem(r, c)) return;
+
+ r->out.level_out = talloc(r, uint32_t);
+ if (composite_nomem(r->out.level_out, c)) return;
+ r->in.req = talloc(r, union drsuapi_DsGetNCChangesRequest);
+ if (composite_nomem(r->in.req, c)) return;
+ r->out.ctr = talloc(r, union drsuapi_DsGetNCChangesCtr);
+ if (composite_nomem(r->out.ctr, c)) return;
+
+ r->in.bind_handle = &drsuapi_h->bind_handle;
+ if (drsuapi_h->remote_info28.supported_extensions & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8) {
+ r->in.level = 8;
+ r->in.req->req8.destination_dsa_guid = partition->destination_dsa_guid;
+ r->in.req->req8.source_dsa_invocation_id= partition->source_dsa_invocation_id;
+ r->in.req->req8.naming_context = &partition->nc;
+ r->in.req->req8.highwatermark = partition->highwatermark;
+ r->in.req->req8.uptodateness_vector = NULL;
+ r->in.req->req8.replica_flags = partition->replica_flags;
+ r->in.req->req8.max_object_count = 133;
+ r->in.req->req8.max_ndr_size = 1336811;
+ r->in.req->req8.extended_op = DRSUAPI_EXOP_NONE;
+ r->in.req->req8.fsmo_info = 0;
+ r->in.req->req8.partial_attribute_set = NULL;
+ r->in.req->req8.partial_attribute_set_ex= NULL;
+ r->in.req->req8.mapping_ctr.num_mappings= 0;
+ r->in.req->req8.mapping_ctr.mappings = NULL;
+ } else {
+ r->in.level = 5;
+ r->in.req->req5.destination_dsa_guid = partition->destination_dsa_guid;
+ r->in.req->req5.source_dsa_invocation_id= partition->source_dsa_invocation_id;
+ r->in.req->req5.naming_context = &partition->nc;
+ r->in.req->req5.highwatermark = partition->highwatermark;
+ r->in.req->req5.uptodateness_vector = NULL;
+ r->in.req->req5.replica_flags = partition->replica_flags;
+ r->in.req->req5.max_object_count = 133;
+ r->in.req->req5.max_ndr_size = 1336770;
+ r->in.req->req5.extended_op = DRSUAPI_EXOP_NONE;
+ r->in.req->req5.fsmo_info = 0;
+ }
+
+ /*
+ * we should try to use the drsuapi_p->pipe here, as w2k3 does
+ * but it seems that some extra flags in the DCERPC Bind call
+ * are needed for it. Or the same KRB5 TGS is needed on both
+ * connections.
+ */
+ s->ndr_struct_ptr = r;
+ subreq = dcerpc_drsuapi_DsGetNCChanges_r_send(s, c->event_ctx,
+ drsuapi_p->drsuapi_handle,
+ r);
+ if (composite_nomem(subreq, c)) return;
+ tevent_req_set_callback(subreq, recv_fn, s);
+}
+
+static WERROR becomeDC_drsuapi_pull_partition_recv(struct libnet_BecomeDC_state *s,
+ struct becomeDC_drsuapi *drsuapi_h,
+ struct becomeDC_drsuapi *drsuapi_p,
+ struct libnet_BecomeDC_Partition *partition,
+ struct drsuapi_DsGetNCChanges *r)
+{
+ uint32_t req_level = r->in.level;
+ struct drsuapi_DsGetNCChangesRequest5 *req5 = NULL;
+ struct drsuapi_DsGetNCChangesRequest8 *req8 = NULL;
+ struct drsuapi_DsGetNCChangesRequest10 *req10 = NULL;
+ uint32_t ctr_level = 0;
+ struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL;
+ struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL;
+ struct GUID *source_dsa_guid = NULL;
+ struct GUID *source_dsa_invocation_id = NULL;
+ struct drsuapi_DsReplicaHighWaterMark *new_highwatermark = NULL;
+ bool more_data = false;
+ WERROR werr;
+
+ if (!W_ERROR_IS_OK(r->out.result)) {
+ return r->out.result;
+ }
+
+ switch (r->in.level) {
+ case 0:
+ /* none */
+ break;
+ case 5:
+ req5 = &r->in.req->req5;
+ break;
+ case 8:
+ req8 = &r->in.req->req8;
+ break;
+ case 10:
+ req10 = &r->in.req->req10;
+ break;
+ default:
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (*r->out.level_out == 1) {
+ ctr_level = 1;
+ ctr1 = &r->out.ctr->ctr1;
+ } else if (*r->out.level_out == 2 &&
+ r->out.ctr->ctr2.mszip1.ts) {
+ ctr_level = 1;
+ ctr1 = &r->out.ctr->ctr2.mszip1.ts->ctr1;
+ } else if (*r->out.level_out == 6) {
+ ctr_level = 6;
+ ctr6 = &r->out.ctr->ctr6;
+ } else if (*r->out.level_out == 7 &&
+ r->out.ctr->ctr7.level == 6 &&
+ r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_MSZIP &&
+ r->out.ctr->ctr7.ctr.mszip6.ts) {
+ ctr_level = 6;
+ ctr6 = &r->out.ctr->ctr7.ctr.mszip6.ts->ctr6;
+ } else if (*r->out.level_out == 7 &&
+ r->out.ctr->ctr7.level == 6 &&
+ r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_XPRESS &&
+ r->out.ctr->ctr7.ctr.xpress6.ts) {
+ ctr_level = 6;
+ ctr6 = &r->out.ctr->ctr7.ctr.xpress6.ts->ctr6;
+ } else {
+ return WERR_BAD_NET_RESP;
+ }
+
+ if (!ctr1 && ! ctr6) {
+ return WERR_BAD_NET_RESP;
+ }
+
+ if (ctr_level == 6) {
+ if (!W_ERROR_IS_OK(ctr6->drs_error)) {
+ return ctr6->drs_error;
+ }
+ }
+
+ switch (ctr_level) {
+ case 1:
+ source_dsa_guid = &ctr1->source_dsa_guid;
+ source_dsa_invocation_id = &ctr1->source_dsa_invocation_id;
+ new_highwatermark = &ctr1->new_highwatermark;
+ more_data = ctr1->more_data;
+ break;
+ case 6:
+ source_dsa_guid = &ctr6->source_dsa_guid;
+ source_dsa_invocation_id = &ctr6->source_dsa_invocation_id;
+ new_highwatermark = &ctr6->new_highwatermark;
+ more_data = ctr6->more_data;
+ break;
+ }
+
+ partition->highwatermark = *new_highwatermark;
+ partition->source_dsa_guid = *source_dsa_guid;
+ partition->source_dsa_invocation_id = *source_dsa_invocation_id;
+ partition->more_data = more_data;
+
+ if (!partition->store_chunk) return WERR_OK;
+
+ s->_sc.domain = &s->domain;
+ s->_sc.forest = &s->forest;
+ s->_sc.source_dsa = &s->source_dsa;
+ s->_sc.dest_dsa = &s->dest_dsa;
+ s->_sc.partition = partition;
+ s->_sc.req_level = req_level;
+ s->_sc.req5 = req5;
+ s->_sc.req8 = req8;
+ s->_sc.req10 = req10;
+ s->_sc.ctr_level = ctr_level;
+ s->_sc.ctr1 = ctr1;
+ s->_sc.ctr6 = ctr6;
+ /*
+ * we need to use the drsuapi_p->gensec_skey here,
+ * when we use drsuapi_p->pipe in the for this request
+ */
+ s->_sc.gensec_skey = &drsuapi_p->gensec_skey;
+
+ werr = partition->store_chunk(s->callbacks.private_data, &s->_sc);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ return WERR_OK;
+}
+
+static void becomeDC_drsuapi3_pull_schema_recv(struct tevent_req *subreq);
+
+static void becomeDC_drsuapi3_pull_schema_send(struct libnet_BecomeDC_state *s)
+{
+ s->schema_part.nc.guid = GUID_zero();
+ s->schema_part.nc.sid = s->zero_sid;
+ s->schema_part.nc.dn = s->forest.schema_dn_str;
+
+ s->schema_part.destination_dsa_guid = s->drsuapi2.bind_guid;
+
+ s->schema_part.replica_flags = DRSUAPI_DRS_WRIT_REP
+ | DRSUAPI_DRS_INIT_SYNC
+ | DRSUAPI_DRS_PER_SYNC
+ | DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS
+ | DRSUAPI_DRS_NEVER_SYNCED
+ | DRSUAPI_DRS_USE_COMPRESSION
+ | DRSUAPI_DRS_GET_ANC;
+ if (s->rodc_join) {
+ s->schema_part.replica_flags &= ~DRSUAPI_DRS_WRIT_REP;
+ }
+
+ s->schema_part.store_chunk = s->callbacks.schema_chunk;
+
+ becomeDC_drsuapi_pull_partition_send(s, &s->drsuapi2, &s->drsuapi3, &s->schema_part,
+ becomeDC_drsuapi3_pull_schema_recv);
+}
+
+static void becomeDC_drsuapi3_pull_config_send(struct libnet_BecomeDC_state *s);
+
+static void becomeDC_drsuapi3_pull_schema_recv(struct tevent_req *subreq)
+{
+ struct libnet_BecomeDC_state *s = tevent_req_callback_data(subreq,
+ struct libnet_BecomeDC_state);
+ struct composite_context *c = s->creq;
+ struct drsuapi_DsGetNCChanges *r = talloc_get_type_abort(s->ndr_struct_ptr,
+ struct drsuapi_DsGetNCChanges);
+ WERROR status;
+
+ s->ndr_struct_ptr = NULL;
+
+ c->status = dcerpc_drsuapi_DsGetNCChanges_r_recv(subreq, r);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ status = becomeDC_drsuapi_pull_partition_recv(s, &s->drsuapi2, &s->drsuapi3, &s->schema_part, r);
+ if (!W_ERROR_IS_OK(status)) {
+ composite_error(c, werror_to_ntstatus(status));
+ return;
+ }
+
+ talloc_free(r);
+
+ if (s->schema_part.more_data) {
+ becomeDC_drsuapi_pull_partition_send(s, &s->drsuapi2, &s->drsuapi3, &s->schema_part,
+ becomeDC_drsuapi3_pull_schema_recv);
+ return;
+ }
+
+ becomeDC_drsuapi3_pull_config_send(s);
+}
+
+static void becomeDC_drsuapi3_pull_config_recv(struct tevent_req *subreq);
+
+static void becomeDC_drsuapi3_pull_config_send(struct libnet_BecomeDC_state *s)
+{
+ s->config_part.nc.guid = GUID_zero();
+ s->config_part.nc.sid = s->zero_sid;
+ s->config_part.nc.dn = s->forest.config_dn_str;
+
+ s->config_part.destination_dsa_guid = s->drsuapi2.bind_guid;
+
+ s->config_part.replica_flags = DRSUAPI_DRS_WRIT_REP
+ | DRSUAPI_DRS_INIT_SYNC
+ | DRSUAPI_DRS_PER_SYNC
+ | DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS
+ | DRSUAPI_DRS_NEVER_SYNCED
+ | DRSUAPI_DRS_USE_COMPRESSION
+ | DRSUAPI_DRS_GET_ANC;
+ if (s->rodc_join) {
+ s->schema_part.replica_flags &= ~DRSUAPI_DRS_WRIT_REP;
+ }
+
+ s->config_part.store_chunk = s->callbacks.config_chunk;
+
+ becomeDC_drsuapi_pull_partition_send(s, &s->drsuapi2, &s->drsuapi3, &s->config_part,
+ becomeDC_drsuapi3_pull_config_recv);
+}
+
+static void becomeDC_drsuapi3_pull_config_recv(struct tevent_req *subreq)
+{
+ struct libnet_BecomeDC_state *s = tevent_req_callback_data(subreq,
+ struct libnet_BecomeDC_state);
+ struct composite_context *c = s->creq;
+ struct drsuapi_DsGetNCChanges *r = talloc_get_type_abort(s->ndr_struct_ptr,
+ struct drsuapi_DsGetNCChanges);
+ WERROR status;
+
+ s->ndr_struct_ptr = NULL;
+
+ c->status = dcerpc_drsuapi_DsGetNCChanges_r_recv(subreq, r);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ status = becomeDC_drsuapi_pull_partition_recv(s, &s->drsuapi2, &s->drsuapi3, &s->config_part, r);
+ if (!W_ERROR_IS_OK(status)) {
+ composite_error(c, werror_to_ntstatus(status));
+ return;
+ }
+
+ talloc_free(r);
+
+ if (s->config_part.more_data) {
+ becomeDC_drsuapi_pull_partition_send(s, &s->drsuapi2, &s->drsuapi3, &s->config_part,
+ becomeDC_drsuapi3_pull_config_recv);
+ return;
+ }
+
+ becomeDC_connect_ldap2(s);
+}
+
+static void becomeDC_drsuapi3_pull_domain_recv(struct tevent_req *subreq);
+
+static void becomeDC_drsuapi3_pull_domain_send(struct libnet_BecomeDC_state *s)
+{
+ s->domain_part.nc.guid = GUID_zero();
+ s->domain_part.nc.sid = s->zero_sid;
+ s->domain_part.nc.dn = s->domain.dn_str;
+
+ s->domain_part.destination_dsa_guid = s->drsuapi2.bind_guid;
+
+ s->domain_part.replica_flags = DRSUAPI_DRS_WRIT_REP
+ | DRSUAPI_DRS_INIT_SYNC
+ | DRSUAPI_DRS_PER_SYNC
+ | DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS
+ | DRSUAPI_DRS_NEVER_SYNCED
+ | DRSUAPI_DRS_USE_COMPRESSION
+ | DRSUAPI_DRS_GET_ANC;
+ if (s->critical_only) {
+ s->domain_part.replica_flags |= DRSUAPI_DRS_CRITICAL_ONLY;
+ }
+ if (s->rodc_join) {
+ s->schema_part.replica_flags &= ~DRSUAPI_DRS_WRIT_REP;
+ }
+
+ s->domain_part.store_chunk = s->callbacks.domain_chunk;
+
+ becomeDC_drsuapi_pull_partition_send(s, &s->drsuapi2, &s->drsuapi3, &s->domain_part,
+ becomeDC_drsuapi3_pull_domain_recv);
+}
+
+static void becomeDC_drsuapi_update_refs_send(struct libnet_BecomeDC_state *s,
+ struct becomeDC_drsuapi *drsuapi,
+ struct libnet_BecomeDC_Partition *partition,
+ void (*recv_fn)(struct tevent_req *subreq));
+static void becomeDC_drsuapi2_update_refs_schema_recv(struct tevent_req *subreq);
+
+static void becomeDC_drsuapi3_pull_domain_recv(struct tevent_req *subreq)
+{
+ struct libnet_BecomeDC_state *s = tevent_req_callback_data(subreq,
+ struct libnet_BecomeDC_state);
+ struct composite_context *c = s->creq;
+ struct drsuapi_DsGetNCChanges *r = talloc_get_type_abort(s->ndr_struct_ptr,
+ struct drsuapi_DsGetNCChanges);
+ WERROR status;
+
+ s->ndr_struct_ptr = NULL;
+
+ c->status = dcerpc_drsuapi_DsGetNCChanges_r_recv(subreq, r);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ status = becomeDC_drsuapi_pull_partition_recv(s, &s->drsuapi2, &s->drsuapi3, &s->domain_part, r);
+ if (!W_ERROR_IS_OK(status)) {
+ composite_error(c, werror_to_ntstatus(status));
+ return;
+ }
+
+ talloc_free(r);
+
+ if (s->domain_part.more_data) {
+ becomeDC_drsuapi_pull_partition_send(s, &s->drsuapi2, &s->drsuapi3, &s->domain_part,
+ becomeDC_drsuapi3_pull_domain_recv);
+ return;
+ }
+
+ if (s->critical_only) {
+ /* Remove the critical and ANC */
+ s->domain_part.replica_flags ^= DRSUAPI_DRS_CRITICAL_ONLY | DRSUAPI_DRS_GET_ANC;
+ s->critical_only = false;
+ becomeDC_drsuapi_pull_partition_send(s, &s->drsuapi2, &s->drsuapi3, &s->domain_part,
+ becomeDC_drsuapi3_pull_domain_recv);
+ return;
+ }
+ becomeDC_drsuapi_update_refs_send(s, &s->drsuapi2, &s->schema_part,
+ becomeDC_drsuapi2_update_refs_schema_recv);
+}
+
+static void becomeDC_drsuapi_update_refs_send(struct libnet_BecomeDC_state *s,
+ struct becomeDC_drsuapi *drsuapi,
+ struct libnet_BecomeDC_Partition *partition,
+ void (*recv_fn)(struct tevent_req *subreq))
+{
+ struct composite_context *c = s->creq;
+ struct drsuapi_DsReplicaUpdateRefs *r;
+ const char *ntds_guid_str;
+ const char *ntds_dns_name;
+ struct tevent_req *subreq;
+
+ r = talloc(s, struct drsuapi_DsReplicaUpdateRefs);
+ if (composite_nomem(r, c)) return;
+
+ ntds_guid_str = GUID_string(r, &s->dest_dsa.ntds_guid);
+ if (composite_nomem(ntds_guid_str, c)) return;
+
+ ntds_dns_name = talloc_asprintf(r, "%s._msdcs.%s",
+ ntds_guid_str,
+ s->forest.dns_name);
+ if (composite_nomem(ntds_dns_name, c)) return;
+
+ r->in.bind_handle = &drsuapi->bind_handle;
+ r->in.level = 1;
+ r->in.req.req1.naming_context = &partition->nc;
+ r->in.req.req1.dest_dsa_dns_name= ntds_dns_name;
+ r->in.req.req1.dest_dsa_guid = s->dest_dsa.ntds_guid;
+ r->in.req.req1.options = DRSUAPI_DRS_ADD_REF | DRSUAPI_DRS_DEL_REF;
+
+ /* I think this is how we mark ourselves as a RODC */
+ if (!lpcfg_parm_bool(s->libnet->lp_ctx, NULL, "repl", "RODC", false)) {
+ r->in.req.req1.options |= DRSUAPI_DRS_WRIT_REP;
+ }
+
+ s->ndr_struct_ptr = r;
+ subreq = dcerpc_drsuapi_DsReplicaUpdateRefs_r_send(s, c->event_ctx,
+ drsuapi->drsuapi_handle,
+ r);
+ if (composite_nomem(subreq, c)) return;
+ tevent_req_set_callback(subreq, recv_fn, s);
+}
+
+static void becomeDC_drsuapi2_update_refs_config_recv(struct tevent_req *subreq);
+
+static void becomeDC_drsuapi2_update_refs_schema_recv(struct tevent_req *subreq)
+{
+ struct libnet_BecomeDC_state *s = tevent_req_callback_data(subreq,
+ struct libnet_BecomeDC_state);
+ struct composite_context *c = s->creq;
+ struct drsuapi_DsReplicaUpdateRefs *r = talloc_get_type_abort(s->ndr_struct_ptr,
+ struct drsuapi_DsReplicaUpdateRefs);
+
+ s->ndr_struct_ptr = NULL;
+
+ c->status = dcerpc_drsuapi_DsReplicaUpdateRefs_r_recv(subreq, r);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ if (!W_ERROR_IS_OK(r->out.result)) {
+ composite_error(c, werror_to_ntstatus(r->out.result));
+ return;
+ }
+
+ talloc_free(r);
+
+ becomeDC_drsuapi_update_refs_send(s, &s->drsuapi2, &s->config_part,
+ becomeDC_drsuapi2_update_refs_config_recv);
+}
+
+static void becomeDC_drsuapi2_update_refs_domain_recv(struct tevent_req *subreq);
+
+static void becomeDC_drsuapi2_update_refs_config_recv(struct tevent_req *subreq)
+{
+ struct libnet_BecomeDC_state *s = tevent_req_callback_data(subreq,
+ struct libnet_BecomeDC_state);
+ struct composite_context *c = s->creq;
+ struct drsuapi_DsReplicaUpdateRefs *r = talloc_get_type(s->ndr_struct_ptr,
+ struct drsuapi_DsReplicaUpdateRefs);
+
+ s->ndr_struct_ptr = NULL;
+
+ c->status = dcerpc_drsuapi_DsReplicaUpdateRefs_r_recv(subreq, r);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ if (!W_ERROR_IS_OK(r->out.result)) {
+ composite_error(c, werror_to_ntstatus(r->out.result));
+ return;
+ }
+
+ talloc_free(r);
+
+ becomeDC_drsuapi_update_refs_send(s, &s->drsuapi2, &s->domain_part,
+ becomeDC_drsuapi2_update_refs_domain_recv);
+}
+
+static void becomeDC_drsuapi2_update_refs_domain_recv(struct tevent_req *subreq)
+{
+ struct libnet_BecomeDC_state *s = tevent_req_callback_data(subreq,
+ struct libnet_BecomeDC_state);
+ struct composite_context *c = s->creq;
+ struct drsuapi_DsReplicaUpdateRefs *r = talloc_get_type(s->ndr_struct_ptr,
+ struct drsuapi_DsReplicaUpdateRefs);
+
+ s->ndr_struct_ptr = NULL;
+
+ c->status = dcerpc_drsuapi_DsReplicaUpdateRefs_r_recv(subreq, r);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ if (!W_ERROR_IS_OK(r->out.result)) {
+ composite_error(c, werror_to_ntstatus(r->out.result));
+ return;
+ }
+
+ talloc_free(r);
+
+ /* TODO: use DDNS updates and register dns names */
+ composite_done(c);
+}
+
+static NTSTATUS becomeDC_ldap2_modify_computer(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_message *msg;
+ unsigned int i;
+ uint32_t user_account_control = UF_SERVER_TRUST_ACCOUNT |
+ UF_TRUSTED_FOR_DELEGATION;
+
+ /* as the value is already as we want it to be, we're done */
+ if (s->dest_dsa.user_account_control == user_account_control) {
+ return NT_STATUS_OK;
+ }
+
+ /* make a 'modify' msg, and only for serverReference */
+ msg = ldb_msg_new(s);
+ NT_STATUS_HAVE_NO_MEMORY(msg);
+ msg->dn = ldb_dn_new(msg, s->ldap2.ldb, s->dest_dsa.computer_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(msg->dn);
+
+ ret = samdb_msg_add_uint(s->ldap2.ldb, msg, msg, "userAccountControl",
+ user_account_control);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* mark all the message elements (should be just one)
+ as LDB_FLAG_MOD_REPLACE */
+ for (i=0;i<msg->num_elements;i++) {
+ msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ ret = ldb_modify(s->ldap2.ldb, msg);
+ talloc_free(msg);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ }
+
+ s->dest_dsa.user_account_control = user_account_control;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_ldap2_move_computer(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_dn *old_dn;
+ struct ldb_dn *new_dn;
+
+ ret = dsdb_wellknown_dn(s->ldap2.ldb, s,
+ ldb_get_default_basedn(s->ldap2.ldb),
+ DS_GUID_DOMAIN_CONTROLLERS_CONTAINER,
+ &new_dn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ }
+
+ if (!ldb_dn_add_child_fmt(new_dn, "CN=%s", s->dest_dsa.netbios_name)) {
+ talloc_free(new_dn);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ old_dn = ldb_dn_new(new_dn, s->ldap2.ldb, s->dest_dsa.computer_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(old_dn);
+
+ if (ldb_dn_compare(old_dn, new_dn) == 0) {
+ /* we don't need to rename if the old and new dn match */
+ talloc_free(new_dn);
+ return NT_STATUS_OK;
+ }
+
+ ret = ldb_rename(s->ldap2.ldb, old_dn, new_dn);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(new_dn);
+ return NT_STATUS_LDAP(ret);
+ }
+
+ s->dest_dsa.computer_dn_str = ldb_dn_alloc_linearized(s, new_dn);
+ NT_STATUS_HAVE_NO_MEMORY(s->dest_dsa.computer_dn_str);
+
+ talloc_free(new_dn);
+
+ return NT_STATUS_OK;
+}
+
+static void becomeDC_connect_ldap2(struct libnet_BecomeDC_state *s)
+{
+ struct composite_context *c = s->creq;
+
+ c->status = becomeDC_ldap_connect(s, &s->ldap2);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap2_modify_computer(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap2_move_computer(s);
+ if (!composite_is_ok(c)) return;
+
+ s->critical_only = true;
+ becomeDC_drsuapi3_pull_domain_send(s);
+}
+
+struct composite_context *libnet_BecomeDC_send(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_BecomeDC *r)
+{
+ struct composite_context *c;
+ struct libnet_BecomeDC_state *s;
+ char *tmp_name;
+
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct libnet_BecomeDC_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+ s->creq = c;
+ s->libnet = ctx;
+
+ /* Domain input */
+ s->domain.dns_name = talloc_strdup(s, r->in.domain_dns_name);
+ if (composite_nomem(s->domain.dns_name, c)) return c;
+ s->domain.netbios_name = talloc_strdup(s, r->in.domain_netbios_name);
+ if (composite_nomem(s->domain.netbios_name, c)) return c;
+ s->domain.sid = dom_sid_dup(s, r->in.domain_sid);
+ if (composite_nomem(s->domain.sid, c)) return c;
+
+ /* Source DSA input */
+ s->source_dsa.address = talloc_strdup(s, r->in.source_dsa_address);
+ if (composite_nomem(s->source_dsa.address, c)) return c;
+
+ /* Destination DSA input */
+ s->dest_dsa.netbios_name= talloc_strdup(s, r->in.dest_dsa_netbios_name);
+ if (composite_nomem(s->dest_dsa.netbios_name, c)) return c;
+
+ /* Destination DSA dns_name construction */
+ tmp_name = strlower_talloc(s, s->dest_dsa.netbios_name);
+ if (composite_nomem(tmp_name, c)) return c;
+ tmp_name = talloc_asprintf_append_buffer(tmp_name, ".%s",s->domain.dns_name);
+ if (composite_nomem(tmp_name, c)) return c;
+ s->dest_dsa.dns_name = tmp_name;
+
+ /* Callback function pointers */
+ s->callbacks = r->in.callbacks;
+
+ /* RODC join*/
+ s->rodc_join = r->in.rodc_join;
+
+ becomeDC_send_cldap(s);
+ return c;
+}
+
+NTSTATUS libnet_BecomeDC_recv(struct composite_context *c, TALLOC_CTX *mem_ctx, struct libnet_BecomeDC *r)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ ZERO_STRUCT(r->out);
+
+ talloc_free(c);
+ return status;
+}
+
+NTSTATUS libnet_BecomeDC(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_BecomeDC *r)
+{
+ NTSTATUS status;
+ struct composite_context *c;
+ c = libnet_BecomeDC_send(ctx, mem_ctx, r);
+ status = libnet_BecomeDC_recv(c, mem_ctx, r);
+ return status;
+}
diff --git a/source4/libnet/libnet_become_dc.h b/source4/libnet/libnet_become_dc.h
new file mode 100644
index 0000000..f050c22
--- /dev/null
+++ b/source4/libnet/libnet_become_dc.h
@@ -0,0 +1,152 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 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/>.
+*/
+
+#ifndef _LIBNET_BECOME_DC_H
+#define _LIBNET_BECOME_DC_H
+
+#include "librpc/gen_ndr/drsuapi.h"
+
+struct libnet_BecomeDC_Domain {
+ /* input */
+ const char *dns_name;
+ const char *netbios_name;
+ const struct dom_sid *sid;
+
+ /* constructed */
+ struct GUID guid;
+ const char *dn_str;
+ uint32_t behavior_version;
+ uint32_t w2k3_update_revision;
+};
+
+struct libnet_BecomeDC_Forest {
+ /* constructed */
+ const char *dns_name;
+ const char *root_dn_str;
+ const char *config_dn_str;
+ uint32_t crossref_behavior_version;
+ const char *schema_dn_str;
+ uint32_t schema_object_version;
+};
+
+struct libnet_BecomeDC_SourceDSA {
+ /* input */
+ const char *address;
+
+ /* constructed */
+ const char *dns_name;
+ const char *netbios_name;
+ const char *site_name;
+ const char *server_dn_str;
+ const char *ntds_dn_str;
+};
+
+struct libnet_BecomeDC_CheckOptions {
+ const struct libnet_BecomeDC_Domain *domain;
+ const struct libnet_BecomeDC_Forest *forest;
+ const struct libnet_BecomeDC_SourceDSA *source_dsa;
+};
+
+struct libnet_BecomeDC_DestDSA {
+ /* input */
+ const char *netbios_name;
+
+ /* constructed */
+ const char *dns_name;
+ const char *site_name;
+ struct GUID site_guid;
+ const char *computer_dn_str;
+ const char *server_dn_str;
+ const char *ntds_dn_str;
+ struct GUID ntds_guid;
+ struct GUID invocation_id;
+ uint32_t user_account_control;
+};
+
+struct libnet_BecomeDC_PrepareDB {
+ const struct libnet_BecomeDC_Domain *domain;
+ const struct libnet_BecomeDC_Forest *forest;
+ const struct libnet_BecomeDC_SourceDSA *source_dsa;
+ const struct libnet_BecomeDC_DestDSA *dest_dsa;
+};
+
+struct libnet_BecomeDC_StoreChunk;
+
+struct libnet_BecomeDC_Partition {
+ struct drsuapi_DsReplicaObjectIdentifier nc;
+ struct GUID destination_dsa_guid;
+ struct GUID source_dsa_guid;
+ struct GUID source_dsa_invocation_id;
+ struct drsuapi_DsReplicaHighWaterMark highwatermark;
+ bool more_data;
+ uint32_t replica_flags;
+
+ WERROR (*store_chunk)(void *private_data,
+ const struct libnet_BecomeDC_StoreChunk *info);
+};
+
+struct libnet_BecomeDC_StoreChunk {
+ const struct libnet_BecomeDC_Domain *domain;
+ const struct libnet_BecomeDC_Forest *forest;
+ const struct libnet_BecomeDC_SourceDSA *source_dsa;
+ const struct libnet_BecomeDC_DestDSA *dest_dsa;
+ const struct libnet_BecomeDC_Partition *partition;
+ uint32_t req_level;
+ const struct drsuapi_DsGetNCChangesRequest5 *req5;
+ const struct drsuapi_DsGetNCChangesRequest8 *req8;
+ const struct drsuapi_DsGetNCChangesRequest10 *req10;
+ uint32_t ctr_level;
+ const struct drsuapi_DsGetNCChangesCtr1 *ctr1;
+ const struct drsuapi_DsGetNCChangesCtr6 *ctr6;
+ const DATA_BLOB *gensec_skey;
+};
+
+struct libnet_BecomeDC_Callbacks {
+ void *private_data;
+ NTSTATUS (*check_options)(void *private_data,
+ const struct libnet_BecomeDC_CheckOptions *info);
+ NTSTATUS (*prepare_db)(void *private_data,
+ const struct libnet_BecomeDC_PrepareDB *info);
+ WERROR (*schema_chunk)(void *private_data,
+ const struct libnet_BecomeDC_StoreChunk *info);
+ WERROR (*config_chunk)(void *private_data,
+ const struct libnet_BecomeDC_StoreChunk *info);
+ WERROR (*domain_chunk)(void *private_data,
+ const struct libnet_BecomeDC_StoreChunk *info);
+};
+
+struct libnet_BecomeDC {
+ struct {
+ const char *domain_dns_name;
+ const char *domain_netbios_name;
+ const struct dom_sid *domain_sid;
+ const char *source_dsa_address;
+ const char *dest_dsa_netbios_name;
+
+ struct libnet_BecomeDC_Callbacks callbacks;
+
+ bool rodc_join;
+ } in;
+
+ struct {
+ const char *error_string;
+ } out;
+};
+
+#endif /* _LIBNET_BECOME_DC_H */
diff --git a/source4/libnet/libnet_domain.c b/source4/libnet/libnet_domain.c
new file mode 100644
index 0000000..a7499b8
--- /dev/null
+++ b/source4/libnet/libnet_domain.c
@@ -0,0 +1,1304 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Rafal Szczesniak 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/>.
+*/
+
+/*
+ a composite function for domain handling on samr and lsa pipes
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "libnet/libnet.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+
+
+struct domain_open_samr_state {
+ struct libnet_context *ctx;
+ struct dcerpc_pipe *pipe;
+ struct libnet_RpcConnect rpcconn;
+ struct samr_Connect connect;
+ struct samr_LookupDomain lookup;
+ struct samr_OpenDomain open;
+ struct samr_Close close;
+ struct lsa_String domain_name;
+ uint32_t access_mask;
+ struct policy_handle connect_handle;
+ struct policy_handle domain_handle;
+ struct dom_sid2 *domain_sid;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg*);
+};
+
+
+static void continue_domain_open_close(struct tevent_req *subreq);
+static void continue_domain_open_connect(struct tevent_req *subreq);
+static void continue_domain_open_lookup(struct tevent_req *subreq);
+static void continue_domain_open_open(struct tevent_req *subreq);
+
+
+/**
+ * Stage 0.5 (optional): Connect to samr rpc pipe
+ */
+static void continue_domain_open_rpc_connect(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct domain_open_samr_state *s;
+ struct tevent_req *subreq;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct domain_open_samr_state);
+
+ c->status = libnet_RpcConnect_recv(ctx, s->ctx, c, &s->rpcconn);
+ if (!composite_is_ok(c)) return;
+
+ s->pipe = s->rpcconn.out.dcerpc_pipe;
+
+ /* preparing parameters for samr_Connect rpc call */
+ s->connect.in.system_name = 0;
+ s->connect.in.access_mask = s->access_mask;
+ s->connect.out.connect_handle = &s->connect_handle;
+
+ /* send request */
+ subreq = dcerpc_samr_Connect_r_send(s, c->event_ctx,
+ s->pipe->binding_handle,
+ &s->connect);
+ if (composite_nomem(subreq, c)) return;
+
+ /* callback handler */
+ tevent_req_set_callback(subreq, continue_domain_open_connect, c);
+}
+
+
+/**
+ * Stage 0.5 (optional): Close existing (in libnet context) domain
+ * handle
+ */
+static void continue_domain_open_close(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct domain_open_samr_state *s;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct domain_open_samr_state);
+
+ /* receive samr_Close reply */
+ c->status = dcerpc_samr_Close_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+
+ msg.type = mon_SamrClose;
+ msg.data = NULL;
+ msg.data_size = 0;
+ s->monitor_fn(&msg);
+ }
+
+ /* reset domain handle and associated data in libnet_context */
+ s->ctx->samr.name = NULL;
+ s->ctx->samr.access_mask = 0;
+ ZERO_STRUCT(s->ctx->samr.handle);
+
+ /* preparing parameters for samr_Connect rpc call */
+ s->connect.in.system_name = 0;
+ s->connect.in.access_mask = s->access_mask;
+ s->connect.out.connect_handle = &s->connect_handle;
+
+ /* send request */
+ subreq = dcerpc_samr_Connect_r_send(s, c->event_ctx,
+ s->pipe->binding_handle,
+ &s->connect);
+ if (composite_nomem(subreq, c)) return;
+
+ /* callback handler */
+ tevent_req_set_callback(subreq, continue_domain_open_connect, c);
+}
+
+
+/**
+ * Stage 1: Connect to SAM server.
+ */
+static void continue_domain_open_connect(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct domain_open_samr_state *s;
+ struct samr_LookupDomain *r;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct domain_open_samr_state);
+
+ /* receive samr_Connect reply */
+ c->status = dcerpc_samr_Connect_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+
+ msg.type = mon_SamrConnect;
+ msg.data = NULL;
+ msg.data_size = 0;
+ s->monitor_fn(&msg);
+ }
+
+ r = &s->lookup;
+
+ /* prepare for samr_LookupDomain call */
+ r->in.connect_handle = &s->connect_handle;
+ r->in.domain_name = &s->domain_name;
+ r->out.sid = talloc(s, struct dom_sid2 *);
+ if (composite_nomem(r->out.sid, c)) return;
+
+ subreq = dcerpc_samr_LookupDomain_r_send(s, c->event_ctx,
+ s->pipe->binding_handle,
+ r);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_domain_open_lookup, c);
+}
+
+
+/**
+ * Stage 2: Lookup domain by name.
+ */
+static void continue_domain_open_lookup(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct domain_open_samr_state *s;
+ struct samr_OpenDomain *r;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct domain_open_samr_state);
+
+ /* receive samr_LookupDomain reply */
+ c->status = dcerpc_samr_LookupDomain_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+ struct msg_rpc_lookup_domain data;
+
+ data.domain_name = s->domain_name.string;
+
+ msg.type = mon_SamrLookupDomain;
+ msg.data = (void*)&data;
+ msg.data_size = sizeof(data);
+ s->monitor_fn(&msg);
+ }
+
+ r = &s->open;
+
+ /* check the rpc layer status */
+ if (!composite_is_ok(c)) return;
+
+ /* check the rpc call itself status */
+ if (!NT_STATUS_IS_OK(s->lookup.out.result)) {
+ composite_error(c, s->lookup.out.result);
+ return;
+ }
+
+ /* prepare for samr_OpenDomain call */
+ r->in.connect_handle = &s->connect_handle;
+ r->in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r->in.sid = *s->lookup.out.sid;
+ r->out.domain_handle = &s->domain_handle;
+
+ subreq = dcerpc_samr_OpenDomain_r_send(s, c->event_ctx,
+ s->pipe->binding_handle,
+ r);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_domain_open_open, c);
+}
+
+
+/*
+ * Stage 3: Open domain.
+ */
+static void continue_domain_open_open(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct domain_open_samr_state *s;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct domain_open_samr_state);
+
+ /* receive samr_OpenDomain reply */
+ c->status = dcerpc_samr_OpenDomain_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+
+ msg.type = mon_SamrOpenDomain;
+ msg.data = NULL;
+ msg.data_size = 0;
+ s->monitor_fn(&msg);
+ }
+
+ composite_done(c);
+}
+
+
+/**
+ * Sends asynchronous DomainOpenSamr request
+ *
+ * @param ctx initialised libnet context
+ * @param io arguments and results of the call
+ * @param monitor pointer to monitor function that is passed monitor message
+ */
+
+struct composite_context *libnet_DomainOpenSamr_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_DomainOpen *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct domain_open_samr_state *s;
+ struct composite_context *rpcconn_req;
+ struct tevent_req *subreq;
+
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct domain_open_samr_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+ s->monitor_fn = monitor;
+
+ s->ctx = ctx;
+ s->pipe = ctx->samr.pipe;
+ s->access_mask = io->in.access_mask;
+ s->domain_name.string = talloc_strdup(c, io->in.domain_name);
+
+ /* check, if there's samr pipe opened already, before opening a domain */
+ if (ctx->samr.pipe == NULL) {
+
+ /* attempting to connect a domain controller */
+ s->rpcconn.level = LIBNET_RPC_CONNECT_DC;
+ s->rpcconn.in.name = io->in.domain_name;
+ s->rpcconn.in.dcerpc_iface = &ndr_table_samr;
+
+ /* send rpc pipe connect request */
+ rpcconn_req = libnet_RpcConnect_send(ctx, c, &s->rpcconn, s->monitor_fn);
+ if (composite_nomem(rpcconn_req, c)) return c;
+
+ composite_continue(c, rpcconn_req, continue_domain_open_rpc_connect, c);
+ return c;
+ }
+
+ /* libnet context's domain handle is not empty, so check out what
+ was opened first, before doing anything */
+ if (!ndr_policy_handle_empty(&ctx->samr.handle)) {
+ if (strequal(ctx->samr.name, io->in.domain_name) &&
+ ctx->samr.access_mask == io->in.access_mask) {
+
+ /* this domain is already opened */
+ composite_done(c);
+ return c;
+
+ } else {
+ /* another domain or access rights have been
+ requested - close the existing handle first */
+ s->close.in.handle = &ctx->samr.handle;
+
+ /* send request to close domain handle */
+ subreq = dcerpc_samr_Close_r_send(s, c->event_ctx,
+ s->pipe->binding_handle,
+ &s->close);
+ if (composite_nomem(subreq, c)) return c;
+
+ /* callback handler */
+ tevent_req_set_callback(subreq, continue_domain_open_close, c);
+ return c;
+ }
+ }
+
+ /* preparing parameters for samr_Connect rpc call */
+ s->connect.in.system_name = 0;
+ s->connect.in.access_mask = s->access_mask;
+ s->connect.out.connect_handle = &s->connect_handle;
+
+ /* send request */
+ subreq = dcerpc_samr_Connect_r_send(s, c->event_ctx,
+ s->pipe->binding_handle,
+ &s->connect);
+ if (composite_nomem(subreq, c)) return c;
+
+ /* callback handler */
+ tevent_req_set_callback(subreq, continue_domain_open_connect, c);
+ return c;
+}
+
+
+/**
+ * Waits for and receives result of asynchronous DomainOpenSamr call
+ *
+ * @param c composite context returned by asynchronous DomainOpen call
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of the call
+ * @param io pointer to results (and arguments) of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_DomainOpenSamr_recv(struct composite_context *c, struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx, struct libnet_DomainOpen *io)
+{
+ NTSTATUS status;
+ struct domain_open_samr_state *s;
+
+ /* wait for results of sending request */
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status) && io) {
+ s = talloc_get_type_abort(c->private_data, struct domain_open_samr_state);
+ io->out.domain_handle = s->domain_handle;
+
+ /* store the resulting handle and related data for use by other
+ libnet functions */
+ ctx->samr.connect_handle = s->connect_handle;
+ ctx->samr.handle = s->domain_handle;
+ ctx->samr.sid = talloc_steal(ctx, *s->lookup.out.sid);
+ ctx->samr.name = talloc_steal(ctx, s->domain_name.string);
+ ctx->samr.access_mask = s->access_mask;
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+struct domain_open_lsa_state {
+ const char *name;
+ uint32_t access_mask;
+ struct libnet_context *ctx;
+ struct libnet_RpcConnect rpcconn;
+ struct lsa_OpenPolicy2 openpol;
+ struct policy_handle handle;
+ struct dcerpc_pipe *pipe;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg*);
+};
+
+
+static void continue_rpc_connect_lsa(struct composite_context *ctx);
+static void continue_lsa_policy_open(struct tevent_req *subreq);
+
+
+/**
+ * Sends asynchronous DomainOpenLsa request
+ *
+ * @param ctx initialised libnet context
+ * @param io arguments and results of the call
+ * @param monitor pointer to monitor function that is passed monitor message
+ */
+
+struct composite_context* libnet_DomainOpenLsa_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_DomainOpen *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct domain_open_lsa_state *s;
+ struct composite_context *rpcconn_req;
+ struct tevent_req *subreq;
+ struct lsa_QosInfo *qos;
+
+ /* create composite context and state */
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return c;
+
+ s = talloc_zero(c, struct domain_open_lsa_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ /* store arguments in the state structure */
+ s->name = talloc_strdup(c, io->in.domain_name);
+ s->access_mask = io->in.access_mask;
+ s->ctx = ctx;
+
+ /* check, if there's lsa pipe opened already, before opening a handle */
+ if (ctx->lsa.pipe == NULL) {
+
+ ZERO_STRUCT(s->rpcconn);
+
+ /* attempting to connect a domain controller */
+ s->rpcconn.level = LIBNET_RPC_CONNECT_DC;
+ s->rpcconn.in.name = talloc_strdup(c, io->in.domain_name);
+ s->rpcconn.in.dcerpc_iface = &ndr_table_lsarpc;
+
+ /* send rpc pipe connect request */
+ rpcconn_req = libnet_RpcConnect_send(ctx, c, &s->rpcconn, s->monitor_fn);
+ if (composite_nomem(rpcconn_req, c)) return c;
+
+ composite_continue(c, rpcconn_req, continue_rpc_connect_lsa, c);
+ return c;
+ }
+
+ s->pipe = ctx->lsa.pipe;
+
+ /* preparing parameters for lsa_OpenPolicy2 rpc call */
+ s->openpol.in.system_name = s->name;
+ s->openpol.in.access_mask = s->access_mask;
+ s->openpol.in.attr = talloc_zero(c, struct lsa_ObjectAttribute);
+
+ qos = talloc_zero(c, struct lsa_QosInfo);
+ qos->len = 0;
+ qos->impersonation_level = 2;
+ qos->context_mode = 1;
+ qos->effective_only = 0;
+
+ s->openpol.in.attr->sec_qos = qos;
+ s->openpol.out.handle = &s->handle;
+
+ /* send rpc request */
+ subreq = dcerpc_lsa_OpenPolicy2_r_send(s, c->event_ctx,
+ s->pipe->binding_handle,
+ &s->openpol);
+ if (composite_nomem(subreq, c)) return c;
+
+ tevent_req_set_callback(subreq, continue_lsa_policy_open, c);
+ return c;
+}
+
+
+/*
+ Stage 0.5 (optional): Rpc pipe connected, send lsa open policy request
+ */
+static void continue_rpc_connect_lsa(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct domain_open_lsa_state *s;
+ struct lsa_QosInfo *qos;
+ struct tevent_req *subreq;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct domain_open_lsa_state);
+
+ /* receive rpc connection */
+ c->status = libnet_RpcConnect_recv(ctx, s->ctx, c, &s->rpcconn);
+ if (!composite_is_ok(c)) return;
+
+ /* RpcConnect function leaves the pipe in libnet context,
+ so get it from there */
+ s->pipe = s->ctx->lsa.pipe;
+
+ /* prepare lsa_OpenPolicy2 call */
+ s->openpol.in.system_name = s->name;
+ s->openpol.in.access_mask = s->access_mask;
+ s->openpol.in.attr = talloc_zero(c, struct lsa_ObjectAttribute);
+
+ qos = talloc_zero(c, struct lsa_QosInfo);
+ qos->len = 0;
+ qos->impersonation_level = 2;
+ qos->context_mode = 1;
+ qos->effective_only = 0;
+
+ s->openpol.in.attr->sec_qos = qos;
+ s->openpol.out.handle = &s->handle;
+
+ /* send rpc request */
+ subreq = dcerpc_lsa_OpenPolicy2_r_send(s, c->event_ctx,
+ s->pipe->binding_handle,
+ &s->openpol);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_lsa_policy_open, c);
+}
+
+
+/*
+ Stage 1: Lsa policy opened - we're done, if successfully
+ */
+static void continue_lsa_policy_open(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct domain_open_lsa_state *s;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct domain_open_lsa_state);
+
+ c->status = dcerpc_lsa_OpenPolicy2_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+
+ msg.type = mon_LsaOpenPolicy;
+ msg.data = NULL;
+ msg.data_size = 0;
+ s->monitor_fn(&msg);
+ }
+
+ composite_done(c);
+}
+
+
+/**
+ * Receives result of asynchronous DomainOpenLsa call
+ *
+ * @param c composite context returned by asynchronous DomainOpenLsa call
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of the call
+ * @param io pointer to results (and arguments) of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_DomainOpenLsa_recv(struct composite_context *c, struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx, struct libnet_DomainOpen *io)
+{
+ NTSTATUS status;
+ struct domain_open_lsa_state *s;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status) && io) {
+ /* everything went fine - get the results and
+ return the error string */
+ s = talloc_get_type_abort(c->private_data, struct domain_open_lsa_state);
+ io->out.domain_handle = s->handle;
+
+ ctx->lsa.handle = s->handle;
+ ctx->lsa.name = talloc_steal(ctx, s->name);
+ ctx->lsa.access_mask = s->access_mask;
+
+ io->out.error_string = talloc_strdup(mem_ctx, "Success");
+
+ } else if (!NT_STATUS_IS_OK(status)) {
+ /* there was an error, so provide nt status code description */
+ io->out.error_string = talloc_asprintf(mem_ctx,
+ "Failed to open domain: %s",
+ nt_errstr(status));
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Sends a request to open a domain in desired service
+ *
+ * @param ctx initialised libnet context
+ * @param io arguments and results of the call
+ * @param monitor pointer to monitor function that is passed monitor message
+ */
+
+struct composite_context* libnet_DomainOpen_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_DomainOpen *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+
+ switch (io->in.type) {
+ case DOMAIN_LSA:
+ /* reques to open a policy handle on \pipe\lsarpc */
+ c = libnet_DomainOpenLsa_send(ctx, mem_ctx, io, monitor);
+ break;
+
+ case DOMAIN_SAMR:
+ default:
+ /* request to open a domain policy handle on \pipe\samr */
+ c = libnet_DomainOpenSamr_send(ctx, mem_ctx, io, monitor);
+ break;
+ }
+
+ return c;
+}
+
+
+/**
+ * Receive result of domain open request
+ *
+ * @param c composite context returned by DomainOpen_send function
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of the call
+ * @param io results and arguments of the call
+ */
+
+NTSTATUS libnet_DomainOpen_recv(struct composite_context *c, struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx, struct libnet_DomainOpen *io)
+{
+ NTSTATUS status;
+
+ switch (io->in.type) {
+ case DOMAIN_LSA:
+ status = libnet_DomainOpenLsa_recv(c, ctx, mem_ctx, io);
+ break;
+
+ case DOMAIN_SAMR:
+ default:
+ status = libnet_DomainOpenSamr_recv(c, ctx, mem_ctx, io);
+ break;
+ }
+
+ return status;
+}
+
+
+/**
+ * Synchronous version of DomainOpen call
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context for the call
+ * @param io arguments and results of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_DomainOpen(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_DomainOpen *io)
+{
+ struct composite_context *c = libnet_DomainOpen_send(ctx, mem_ctx, io, NULL);
+ return libnet_DomainOpen_recv(c, ctx, mem_ctx, io);
+}
+
+
+struct domain_close_lsa_state {
+ struct dcerpc_pipe *pipe;
+ struct lsa_Close close;
+ struct policy_handle handle;
+
+ void (*monitor_fn)(struct monitor_msg*);
+};
+
+
+static void continue_lsa_close(struct tevent_req *subreq);
+
+
+struct composite_context* libnet_DomainCloseLsa_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_DomainClose *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct domain_close_lsa_state *s;
+ struct tevent_req *subreq;
+
+ /* composite context and state structure allocation */
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return c;
+
+ s = talloc_zero(c, struct domain_close_lsa_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+ s->monitor_fn = monitor;
+
+ /* TODO: check if lsa pipe pointer is non-null */
+
+ if (!strequal(ctx->lsa.name, io->in.domain_name)) {
+ composite_error(c, NT_STATUS_INVALID_PARAMETER);
+ return c;
+ }
+
+ /* get opened lsarpc pipe pointer */
+ s->pipe = ctx->lsa.pipe;
+
+ /* prepare close handle call arguments */
+ s->close.in.handle = &ctx->lsa.handle;
+ s->close.out.handle = &s->handle;
+
+ /* send the request */
+ subreq = dcerpc_lsa_Close_r_send(s, c->event_ctx,
+ s->pipe->binding_handle,
+ &s->close);
+ if (composite_nomem(subreq, c)) return c;
+
+ tevent_req_set_callback(subreq, continue_lsa_close, c);
+ return c;
+}
+
+
+/*
+ Stage 1: Receive result of lsa close call
+*/
+static void continue_lsa_close(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct domain_close_lsa_state *s;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct domain_close_lsa_state);
+
+ c->status = dcerpc_lsa_Close_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+
+ msg.type = mon_LsaClose;
+ msg.data = NULL;
+ msg.data_size = 0;
+ s->monitor_fn(&msg);
+ }
+
+ composite_done(c);
+}
+
+
+NTSTATUS libnet_DomainCloseLsa_recv(struct composite_context *c, struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx, struct libnet_DomainClose *io)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status) && io) {
+ /* policy handle closed successfully */
+
+ ctx->lsa.name = NULL;
+ ZERO_STRUCT(ctx->lsa.handle);
+
+ io->out.error_string = talloc_asprintf(mem_ctx, "Success");
+
+ } else if (!NT_STATUS_IS_OK(status)) {
+ /* there was an error, so return description of the status code */
+ io->out.error_string = talloc_asprintf(mem_ctx, "Error: %s", nt_errstr(status));
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+struct domain_close_samr_state {
+ struct samr_Close close;
+ struct policy_handle handle;
+
+ void (*monitor_fn)(struct monitor_msg*);
+};
+
+
+static void continue_samr_close(struct tevent_req *subreq);
+
+
+struct composite_context* libnet_DomainCloseSamr_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_DomainClose *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct domain_close_samr_state *s;
+ struct tevent_req *subreq;
+
+ /* composite context and state structure allocation */
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return c;
+
+ s = talloc_zero(c, struct domain_close_samr_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+ s->monitor_fn = monitor;
+
+ /* TODO: check if samr pipe pointer is non-null */
+
+ if (!strequal(ctx->samr.name, io->in.domain_name)) {
+ composite_error(c, NT_STATUS_INVALID_PARAMETER);
+ return c;
+ }
+
+ /* prepare close domain handle call arguments */
+ ZERO_STRUCT(s->close);
+ s->close.in.handle = &ctx->samr.handle;
+ s->close.out.handle = &s->handle;
+
+ /* send the request */
+ subreq = dcerpc_samr_Close_r_send(s, c->event_ctx,
+ ctx->samr.pipe->binding_handle,
+ &s->close);
+ if (composite_nomem(subreq, c)) return c;
+
+ tevent_req_set_callback(subreq, continue_samr_close, c);
+ return c;
+}
+
+
+/*
+ Stage 1: Receive result of samr close call
+*/
+static void continue_samr_close(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct domain_close_samr_state *s;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct domain_close_samr_state);
+
+ c->status = dcerpc_samr_Close_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+
+ msg.type = mon_SamrClose;
+ msg.data = NULL;
+ msg.data_size = 0;
+ s->monitor_fn(&msg);
+ }
+
+ composite_done(c);
+}
+
+
+NTSTATUS libnet_DomainCloseSamr_recv(struct composite_context *c, struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx, struct libnet_DomainClose *io)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status) && io) {
+ /* domain policy handle closed successfully */
+
+ ZERO_STRUCT(ctx->samr.handle);
+ talloc_free(discard_const_p(char, ctx->samr.name));
+ talloc_free(ctx->samr.sid);
+ ctx->samr.name = NULL;
+ ctx->samr.sid = NULL;
+
+ io->out.error_string = talloc_asprintf(mem_ctx, "Success");
+
+ } else if (!NT_STATUS_IS_OK(status)) {
+ /* there was an error, so return description of the status code */
+ io->out.error_string = talloc_asprintf(mem_ctx, "Error: %s", nt_errstr(status));
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+struct composite_context* libnet_DomainClose_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_DomainClose *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+
+ switch (io->in.type) {
+ case DOMAIN_LSA:
+ /* request to close policy handle on \pipe\lsarpc */
+ c = libnet_DomainCloseLsa_send(ctx, mem_ctx, io, monitor);
+ break;
+
+ case DOMAIN_SAMR:
+ default:
+ /* request to close domain policy handle on \pipe\samr */
+ c = libnet_DomainCloseSamr_send(ctx, mem_ctx, io, monitor);
+ break;
+ }
+
+ return c;
+}
+
+
+NTSTATUS libnet_DomainClose_recv(struct composite_context *c, struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx, struct libnet_DomainClose *io)
+{
+ NTSTATUS status;
+
+ switch (io->in.type) {
+ case DOMAIN_LSA:
+ /* receive result of closing lsa policy handle */
+ status = libnet_DomainCloseLsa_recv(c, ctx, mem_ctx, io);
+ break;
+
+ case DOMAIN_SAMR:
+ default:
+ /* receive result of closing samr domain policy handle */
+ status = libnet_DomainCloseSamr_recv(c, ctx, mem_ctx, io);
+ break;
+ }
+
+ return status;
+}
+
+
+NTSTATUS libnet_DomainClose(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_DomainClose *io)
+{
+ struct composite_context *c;
+
+ c = libnet_DomainClose_send(ctx, mem_ctx, io, NULL);
+ return libnet_DomainClose_recv(c, ctx, mem_ctx, io);
+}
+
+
+struct domain_list_state {
+ struct libnet_context *ctx;
+ struct libnet_RpcConnect rpcconn;
+ struct samr_Connect samrconn;
+ struct samr_EnumDomains enumdom;
+ struct samr_Close samrclose;
+ const char *hostname;
+ struct policy_handle connect_handle;
+ int buf_size;
+ struct domainlist *domains;
+ uint32_t resume_handle;
+ uint32_t count;
+
+ void (*monitor_fn)(struct monitor_msg*);
+};
+
+
+static void continue_rpc_connect(struct composite_context *c);
+static void continue_samr_connect(struct tevent_req *subreq);
+static void continue_samr_enum_domains(struct tevent_req *subreq);
+static void continue_samr_close_handle(struct tevent_req *subreq);
+
+static struct domainlist* get_domain_list(TALLOC_CTX *mem_ctx, struct domain_list_state *s);
+
+
+/*
+ Stage 1: Receive connected rpc pipe and send connection
+ request to SAMR service
+*/
+static void continue_rpc_connect(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct domain_list_state *s;
+ struct tevent_req *subreq;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct domain_list_state);
+
+ c->status = libnet_RpcConnect_recv(ctx, s->ctx, c, &s->rpcconn);
+ if (!composite_is_ok(c)) return;
+
+ s->samrconn.in.system_name = 0;
+ s->samrconn.in.access_mask = SEC_GENERIC_READ; /* should be enough */
+ s->samrconn.out.connect_handle = &s->connect_handle;
+
+ subreq = dcerpc_samr_Connect_r_send(s, c->event_ctx,
+ s->ctx->samr.pipe->binding_handle,
+ &s->samrconn);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_samr_connect, c);
+}
+
+
+/*
+ Stage 2: Receive policy handle to the connected SAMR service and issue
+ a request to enumerate domain databases available
+*/
+static void continue_samr_connect(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct domain_list_state *s;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct domain_list_state);
+
+ c->status = dcerpc_samr_Connect_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+
+ msg.type = mon_SamrConnect;
+ msg.data = NULL;
+ msg.data_size = 0;
+ s->monitor_fn(&msg);
+ }
+
+ s->enumdom.in.connect_handle = &s->connect_handle;
+ s->enumdom.in.resume_handle = &s->resume_handle;
+ s->enumdom.in.buf_size = s->buf_size;
+ s->enumdom.out.resume_handle = &s->resume_handle;
+ s->enumdom.out.num_entries = talloc(s, uint32_t);
+ if (composite_nomem(s->enumdom.out.num_entries, c)) return;
+ s->enumdom.out.sam = talloc(s, struct samr_SamArray *);
+ if (composite_nomem(s->enumdom.out.sam, c)) return;
+
+ subreq = dcerpc_samr_EnumDomains_r_send(s, c->event_ctx,
+ s->ctx->samr.pipe->binding_handle,
+ &s->enumdom);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_samr_enum_domains, c);
+}
+
+
+/*
+ Stage 3: Receive domain names available and repeat the request
+ enumeration is not complete yet. Close samr connection handle
+ upon completion.
+*/
+static void continue_samr_enum_domains(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct domain_list_state *s;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct domain_list_state);
+
+ c->status = dcerpc_samr_EnumDomains_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+
+ msg.type = mon_SamrEnumDomains;
+ msg.data = NULL;
+ msg.data_size = 0;
+ s->monitor_fn(&msg);
+ }
+
+ if (NT_STATUS_IS_OK(s->enumdom.out.result)) {
+
+ s->domains = get_domain_list(c, s);
+
+ } else if (NT_STATUS_EQUAL(s->enumdom.out.result, STATUS_MORE_ENTRIES)) {
+
+ s->domains = get_domain_list(c, s);
+
+ /* prepare next round of enumeration */
+ s->enumdom.in.connect_handle = &s->connect_handle;
+ s->enumdom.in.resume_handle = &s->resume_handle;
+ s->enumdom.in.buf_size = s->ctx->samr.buf_size;
+ s->enumdom.out.resume_handle = &s->resume_handle;
+
+ /* send the request */
+ subreq = dcerpc_samr_EnumDomains_r_send(s, c->event_ctx,
+ s->ctx->samr.pipe->binding_handle,
+ &s->enumdom);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_samr_enum_domains, c);
+
+ } else {
+ composite_error(c, s->enumdom.out.result);
+ return;
+ }
+
+ /* close samr connection handle */
+ s->samrclose.in.handle = &s->connect_handle;
+ s->samrclose.out.handle = &s->connect_handle;
+
+ /* send the request */
+ subreq = dcerpc_samr_Close_r_send(s, c->event_ctx,
+ s->ctx->samr.pipe->binding_handle,
+ &s->samrclose);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_samr_close_handle, c);
+}
+
+
+/*
+ Stage 4: Receive result of closing samr connection handle.
+*/
+static void continue_samr_close_handle(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct domain_list_state *s;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct domain_list_state);
+
+ c->status = dcerpc_samr_Close_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+
+ msg.type = mon_SamrClose;
+ msg.data = NULL;
+ msg.data_size = 0;
+ s->monitor_fn(&msg);
+ }
+
+ /* did everything go fine ? */
+ if (!NT_STATUS_IS_OK(s->samrclose.out.result)) {
+ composite_error(c, s->samrclose.out.result);
+ return;
+ }
+
+ composite_done(c);
+}
+
+
+/*
+ Utility function to copy domain names from result of samr_EnumDomains call
+*/
+static struct domainlist* get_domain_list(TALLOC_CTX *mem_ctx, struct domain_list_state *s)
+{
+ uint32_t i;
+ if (mem_ctx == NULL || s == NULL) return NULL;
+
+ /* prepare domains array */
+ if (s->domains == NULL) {
+ s->domains = talloc_array(mem_ctx, struct domainlist,
+ *s->enumdom.out.num_entries);
+ } else {
+ s->domains = talloc_realloc(mem_ctx, s->domains, struct domainlist,
+ s->count + *s->enumdom.out.num_entries);
+ }
+
+ /* copy domain names returned from samr_EnumDomains call */
+ for (i = s->count; i < s->count + *s->enumdom.out.num_entries; i++)
+ {
+ struct lsa_String *domain_name = &(*s->enumdom.out.sam)->entries[i - s->count].name;
+
+ /* strdup name as a child of allocated array to make it follow the array
+ in case of talloc_steal or talloc_free */
+ s->domains[i].name = talloc_strdup(s->domains, domain_name->string);
+ s->domains[i].sid = NULL; /* this is to be filled out later */
+ }
+
+ /* number of entries returned (domains enumerated) */
+ s->count += *s->enumdom.out.num_entries;
+
+ return s->domains;
+}
+
+
+/**
+ * Sends a request to list domains on given host
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context
+ * @param io arguments and results of the call
+ * @param monitor pointer to monitor function that is passed monitor messages
+ */
+
+struct composite_context* libnet_DomainList_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_DomainList *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct domain_list_state *s;
+ struct composite_context *rpcconn_req;
+ struct tevent_req *subreq;
+
+ /* composite context and state structure allocation */
+ c = composite_create(ctx, ctx->event_ctx);
+ if (c == NULL) return c;
+
+ s = talloc_zero(c, struct domain_list_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+ s->monitor_fn = monitor;
+
+ s->ctx = ctx;
+ s->hostname = talloc_strdup(c, io->in.hostname);
+ if (composite_nomem(s->hostname, c)) return c;
+
+ /* check whether samr pipe has already been opened */
+ if (ctx->samr.pipe == NULL) {
+ ZERO_STRUCT(s->rpcconn);
+
+ /* prepare rpc connect call */
+ s->rpcconn.level = LIBNET_RPC_CONNECT_SERVER;
+ s->rpcconn.in.name = s->hostname;
+ s->rpcconn.in.dcerpc_iface = &ndr_table_samr;
+
+ rpcconn_req = libnet_RpcConnect_send(ctx, c, &s->rpcconn, s->monitor_fn);
+ if (composite_nomem(rpcconn_req, c)) return c;
+
+ composite_continue(c, rpcconn_req, continue_rpc_connect, c);
+
+ } else {
+ /* prepare samr_Connect call */
+ s->samrconn.in.system_name = 0;
+ s->samrconn.in.access_mask = SEC_GENERIC_READ;
+ s->samrconn.out.connect_handle = &s->connect_handle;
+
+ subreq = dcerpc_samr_Connect_r_send(s, c->event_ctx,
+ s->ctx->samr.pipe->binding_handle,
+ &s->samrconn);
+ if (composite_nomem(subreq, c)) return c;
+
+ tevent_req_set_callback(subreq, continue_samr_connect, c);
+ }
+
+ return c;
+}
+
+
+/**
+ * Receive result of domain list request
+ *
+ * @param c composite context returned by DomainList_send function
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of the call
+ * @param io results and arguments of the call
+ */
+
+NTSTATUS libnet_DomainList_recv(struct composite_context *c, struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx, struct libnet_DomainList *io)
+{
+ NTSTATUS status;
+ struct domain_list_state *s;
+
+ status = composite_wait(c);
+
+ s = talloc_get_type_abort(c->private_data, struct domain_list_state);
+
+ if (NT_STATUS_IS_OK(status) && ctx && mem_ctx && io) {
+ /* fetch the results to be returned by io structure */
+ io->out.count = s->count;
+ io->out.domains = talloc_steal(mem_ctx, s->domains);
+ io->out.error_string = talloc_asprintf(mem_ctx, "Success");
+
+ } else if (!NT_STATUS_IS_OK(status)) {
+ /* there was an error, so return description of the status code */
+ io->out.error_string = talloc_asprintf(mem_ctx, "Error: %s", nt_errstr(status));
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Synchronous version of DomainList call
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context for the call
+ * @param io arguments and results of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_DomainList(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_DomainList *io)
+{
+ struct composite_context *c;
+
+ c = libnet_DomainList_send(ctx, mem_ctx, io, NULL);
+ return libnet_DomainList_recv(c, ctx, mem_ctx, io);
+}
diff --git a/source4/libnet/libnet_domain.h b/source4/libnet/libnet_domain.h
new file mode 100644
index 0000000..ae698e3
--- /dev/null
+++ b/source4/libnet/libnet_domain.h
@@ -0,0 +1,70 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Rafal Szczesniak 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/>.
+*/
+
+
+enum service_type { DOMAIN_SAMR, DOMAIN_LSA };
+
+/*
+ * struct definition for opening a domain
+ */
+
+struct libnet_DomainOpen {
+ struct {
+ enum service_type type;
+ const char *domain_name;
+ uint32_t access_mask;
+ } in;
+ struct {
+ struct policy_handle domain_handle;
+ const char *error_string;
+ } out;
+};
+
+
+struct libnet_DomainClose {
+ struct {
+ enum service_type type;
+ const char *domain_name;
+ } in;
+ struct {
+ const char *error_string;
+ } out;
+};
+
+
+struct libnet_DomainList {
+ struct {
+ const char *hostname;
+ } in;
+ struct {
+ int count;
+
+ struct domainlist {
+ const char *sid;
+ const char *name;
+ } *domains;
+
+ const char *error_string;
+ } out;
+};
+
+
+struct msg_rpc_lookup_domain {
+ const char *domain_name;
+};
diff --git a/source4/libnet/libnet_export_keytab.c b/source4/libnet/libnet_export_keytab.c
new file mode 100644
index 0000000..4c96546
--- /dev/null
+++ b/source4/libnet/libnet_export_keytab.c
@@ -0,0 +1,207 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2009
+ Copyright (C) Andreas Schneider <asn@samba.org> 2016
+
+ 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/kerberos.h"
+#include "auth/kerberos/kerberos.h"
+#include "kdc/samba_kdc.h"
+#include "libnet/libnet_export_keytab.h"
+
+#include "kdc/db-glue.h"
+#include "kdc/sdb.h"
+
+static NTSTATUS sdb_kt_copy(TALLOC_CTX *mem_ctx,
+ krb5_context context,
+ struct samba_kdc_db_context *db_ctx,
+ const char *keytab_name,
+ const char *principal,
+ const char **error_string)
+{
+ struct sdb_entry sentry = {};
+ krb5_keytab keytab;
+ krb5_error_code code = 0;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ char *entry_principal = NULL;
+ bool copy_one_principal = (principal != NULL);
+ krb5_data password;
+
+ code = smb_krb5_kt_open_relative(context,
+ keytab_name,
+ true, /* write_access */
+ &keytab);
+ if (code != 0) {
+ *error_string = talloc_asprintf(mem_ctx,
+ "Failed to open keytab: %s",
+ keytab_name);
+ status = NT_STATUS_NO_SUCH_FILE;
+ goto done;
+ }
+
+ if (copy_one_principal) {
+ krb5_principal k5_princ;
+
+ code = smb_krb5_parse_name(context, principal, &k5_princ);
+ if (code != 0) {
+ *error_string = smb_get_krb5_error_message(context,
+ code,
+ mem_ctx);
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ code = samba_kdc_fetch(context, db_ctx, k5_princ,
+ SDB_F_GET_ANY | SDB_F_ADMIN_DATA,
+ 0, &sentry);
+
+ krb5_free_principal(context, k5_princ);
+ } else {
+ code = samba_kdc_firstkey(context, db_ctx, &sentry);
+ }
+
+ for (; code == 0; code = samba_kdc_nextkey(context, db_ctx, &sentry)) {
+ int i;
+
+ code = krb5_unparse_name(context,
+ sentry.principal,
+ &entry_principal);
+ if (code != 0) {
+ *error_string = smb_get_krb5_error_message(context,
+ code,
+ mem_ctx);
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (sentry.keys.len == 0) {
+ SAFE_FREE(entry_principal);
+ sdb_entry_free(&sentry);
+
+ continue;
+ }
+
+ for (i = 0; i < sentry.keys.len; i++) {
+ struct sdb_key *s = &(sentry.keys.val[i]);
+ krb5_enctype enctype;
+
+ enctype = KRB5_KEY_TYPE(&(s->key));
+ password.length = KRB5_KEY_LENGTH(&s->key);
+ password.data = (char *)KRB5_KEY_DATA(&s->key);
+
+ DBG_INFO("smb_krb5_kt_add_entry for enctype=0x%04x\n",
+ (int)enctype);
+ code = smb_krb5_kt_add_entry(context,
+ keytab,
+ sentry.kvno,
+ entry_principal,
+ NULL,
+ enctype,
+ &password,
+ true, /* no_salt */
+ false); /* keeyp_old_entries */
+ if (code != 0) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ *error_string = smb_get_krb5_error_message(context,
+ code,
+ mem_ctx);
+ DEBUG(0, ("smb_krb5_kt_add_entry failed code=%d, error = %s\n",
+ code, *error_string));
+ goto done;
+ }
+ }
+
+ if (copy_one_principal) {
+ break;
+ }
+
+ SAFE_FREE(entry_principal);
+ sdb_entry_free(&sentry);
+ }
+
+ if (code != 0 && code != SDB_ERR_NOENTRY) {
+ *error_string = smb_get_krb5_error_message(context,
+ code,
+ mem_ctx);
+ status = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+
+ status = NT_STATUS_OK;
+done:
+ SAFE_FREE(entry_principal);
+ sdb_entry_free(&sentry);
+
+ return status;
+}
+
+NTSTATUS libnet_export_keytab(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_export_keytab *r)
+{
+ krb5_error_code ret;
+ struct smb_krb5_context *smb_krb5_context;
+ struct samba_kdc_base_context *base_ctx;
+ struct samba_kdc_db_context *db_ctx = NULL;
+ const char *error_string = NULL;
+ NTSTATUS status;
+
+ ret = smb_krb5_init_context(ctx, ctx->lp_ctx, &smb_krb5_context);
+ if (ret) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ base_ctx = talloc_zero(mem_ctx, struct samba_kdc_base_context);
+ if (base_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ base_ctx->ev_ctx = ctx->event_ctx;
+ base_ctx->lp_ctx = ctx->lp_ctx;
+
+ status = samba_kdc_setup_db_ctx(mem_ctx, base_ctx, &db_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (r->in.principal != NULL) {
+ DEBUG(0, ("Export one principal to %s\n", r->in.keytab_name));
+ status = sdb_kt_copy(mem_ctx,
+ smb_krb5_context->krb5_context,
+ db_ctx,
+ r->in.keytab_name,
+ r->in.principal,
+ &error_string);
+ } else {
+ unlink(r->in.keytab_name);
+ DEBUG(0, ("Export complete keytab to %s\n", r->in.keytab_name));
+ status = sdb_kt_copy(mem_ctx,
+ smb_krb5_context->krb5_context,
+ db_ctx,
+ r->in.keytab_name,
+ NULL,
+ &error_string);
+ }
+
+ talloc_free(db_ctx);
+ talloc_free(base_ctx);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = error_string;
+ }
+
+ return status;
+}
diff --git a/source4/libnet/libnet_export_keytab.h b/source4/libnet/libnet_export_keytab.h
new file mode 100644
index 0000000..2b4bdcd
--- /dev/null
+++ b/source4/libnet/libnet_export_keytab.h
@@ -0,0 +1,32 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 "libnet/libnet.h"
+
+struct libnet_export_keytab {
+ struct {
+ const char *keytab_name;
+ const char *principal;
+ } in;
+ struct {
+ const char *error_string;
+ } out;
+};
+
+NTSTATUS libnet_export_keytab(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_export_keytab *r);
diff --git a/source4/libnet/libnet_group.c b/source4/libnet/libnet_group.c
new file mode 100644
index 0000000..512d6fb
--- /dev/null
+++ b/source4/libnet/libnet_group.c
@@ -0,0 +1,764 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Rafal Szczesniak 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 "libnet/libnet.h"
+#include "libcli/composite/composite.h"
+#include "librpc/gen_ndr/lsa.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "librpc/gen_ndr/samr.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "libcli/security/security.h"
+
+
+struct create_group_state {
+ struct libnet_context *ctx;
+ struct libnet_CreateGroup r;
+ struct libnet_DomainOpen domain_open;
+ struct libnet_rpc_groupadd group_add;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static void continue_domain_opened(struct composite_context *ctx);
+static void continue_rpc_group_added(struct composite_context *ctx);
+
+
+struct composite_context* libnet_CreateGroup_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_CreateGroup *r,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct create_group_state *s;
+ struct composite_context *create_req;
+ bool prereq_met = false;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct create_group_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ s->ctx = ctx;
+ s->r = *r;
+ ZERO_STRUCT(s->r.out);
+
+ /* prerequisite: make sure we have a valid samr domain handle */
+ prereq_met = samr_domain_opened(ctx, c, s->r.in.domain_name, &c, &s->domain_open,
+ continue_domain_opened, monitor);
+ if (!prereq_met) return c;
+
+ /* prepare arguments of rpc group add call */
+ s->group_add.in.groupname = r->in.group_name;
+ s->group_add.in.domain_handle = ctx->samr.handle;
+
+ /* send the request */
+ create_req = libnet_rpc_groupadd_send(s, s->ctx->event_ctx,
+ ctx->samr.samr_handle,
+ &s->group_add, monitor);
+ if (composite_nomem(create_req, c)) return c;
+
+ composite_continue(c, create_req, continue_rpc_group_added, c);
+ return c;
+}
+
+
+static void continue_domain_opened(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct create_group_state *s;
+ struct composite_context *create_req;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct create_group_state);
+
+ c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domain_open);
+ if (!composite_is_ok(c)) return;
+
+ /* prepare arguments of groupadd call */
+ s->group_add.in.groupname = s->r.in.group_name;
+ s->group_add.in.domain_handle = s->ctx->samr.handle;
+
+ /* send the request */
+ create_req = libnet_rpc_groupadd_send(s, s->ctx->event_ctx,
+ s->ctx->samr.samr_handle,
+ &s->group_add, s->monitor_fn);
+ if (composite_nomem(create_req, c)) return;
+
+ composite_continue(c, create_req, continue_rpc_group_added, c);
+}
+
+
+static void continue_rpc_group_added(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct create_group_state *s;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct create_group_state);
+
+ /* receive result of group add call */
+ c->status = libnet_rpc_groupadd_recv(ctx, c, &s->group_add);
+ if (!composite_is_ok(c)) return;
+
+ /* we're done */
+ composite_done(c);
+}
+
+
+/**
+ * Receive result of CreateGroup call
+ *
+ * @param c composite context returned by send request routine
+ * @param mem_ctx memory context of this call
+ * @param r pointer to a structure containing arguments and result of this call
+ * @return nt status
+ */
+NTSTATUS libnet_CreateGroup_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_CreateGroup *r)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_strdup(mem_ctx, nt_errstr(status));
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Create domain group
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param io pointer to structure containing arguments and result of this call
+ * @return nt status
+ */
+NTSTATUS libnet_CreateGroup(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_CreateGroup *io)
+{
+ struct composite_context *c;
+
+ c = libnet_CreateGroup_send(ctx, mem_ctx, io, NULL);
+ return libnet_CreateGroup_recv(c, mem_ctx, io);
+}
+
+
+struct group_info_state {
+ struct libnet_context *ctx;
+ const char *domain_name;
+ enum libnet_GroupInfo_level level;
+ const char *group_name;
+ const char *sid_string;
+ struct libnet_LookupName lookup;
+ struct libnet_DomainOpen domopen;
+ struct libnet_rpc_groupinfo info;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static void continue_domain_open_info(struct composite_context *ctx);
+static void continue_name_found(struct composite_context *ctx);
+static void continue_group_info(struct composite_context *ctx);
+
+/**
+ * Sends request to get group information
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param io pointer to structure containing arguments the call
+ * @param monitor function pointer for receiving monitor messages
+ * @return composite context of this request
+ */
+struct composite_context* libnet_GroupInfo_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_GroupInfo *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct group_info_state *s;
+ bool prereq_met = false;
+ struct composite_context *lookup_req, *info_req;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct group_info_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ /* store arguments in the state structure */
+ s->monitor_fn = monitor;
+ s->ctx = ctx;
+ s->domain_name = talloc_strdup(c, io->in.domain_name);
+ s->level = io->in.level;
+ switch(s->level) {
+ case GROUP_INFO_BY_NAME:
+ s->group_name = talloc_strdup(c, io->in.data.group_name);
+ s->sid_string = NULL;
+ break;
+ case GROUP_INFO_BY_SID:
+ s->group_name = NULL;
+ s->sid_string = dom_sid_string(c, io->in.data.group_sid);
+ break;
+ }
+
+ /* prerequisite: make sure the domain is opened */
+ prereq_met = samr_domain_opened(ctx, c, s->domain_name, &c, &s->domopen,
+ continue_domain_open_info, monitor);
+ if (!prereq_met) return c;
+
+ switch(s->level) {
+ case GROUP_INFO_BY_NAME:
+ /* prepare arguments for LookupName call */
+ s->lookup.in.name = s->group_name;
+ s->lookup.in.domain_name = s->domain_name;
+
+ /* send the request */
+ lookup_req = libnet_LookupName_send(s->ctx, c, &s->lookup, s->monitor_fn);
+ if (composite_nomem(lookup_req, c)) return c;
+
+ /* set the next stage */
+ composite_continue(c, lookup_req, continue_name_found, c);
+ break;
+ case GROUP_INFO_BY_SID:
+ /* prepare arguments for groupinfo call */
+ s->info.in.domain_handle = s->ctx->samr.handle;
+ s->info.in.sid = s->sid_string;
+ /* we're looking for all information available */
+ s->info.in.level = GROUPINFOALL;
+
+ /* send the request */
+ info_req = libnet_rpc_groupinfo_send(s, s->ctx->event_ctx,
+ s->ctx->samr.samr_handle,
+ &s->info, s->monitor_fn);
+ if (composite_nomem(info_req, c)) return c;
+
+ /* set the next stage */
+ composite_continue(c, info_req, continue_group_info, c);
+ break;
+ }
+
+ return c;
+}
+
+
+/*
+ * Stage 0.5 (optional): receive opened domain and send lookup name request
+ */
+static void continue_domain_open_info(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct group_info_state *s;
+ struct composite_context *lookup_req, *info_req;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct group_info_state);
+
+ /* receive domain handle */
+ c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domopen);
+ if (!composite_is_ok(c)) return;
+
+ switch(s->level) {
+ case GROUP_INFO_BY_NAME:
+ /* prepare arguments for LookupName call */
+ s->lookup.in.name = s->group_name;
+ s->lookup.in.domain_name = s->domain_name;
+
+ /* send the request */
+ lookup_req = libnet_LookupName_send(s->ctx, c, &s->lookup, s->monitor_fn);
+ if (composite_nomem(lookup_req, c)) return;
+
+ /* set the next stage */
+ composite_continue(c, lookup_req, continue_name_found, c);
+ break;
+ case GROUP_INFO_BY_SID:
+ /* prepare arguments for groupinfo call */
+ s->info.in.domain_handle = s->ctx->samr.handle;
+ s->info.in.sid = s->sid_string;
+ /* we're looking for all information available */
+ s->info.in.level = GROUPINFOALL;
+
+ /* send the request */
+ info_req = libnet_rpc_groupinfo_send(s, s->ctx->event_ctx,
+ s->ctx->samr.samr_handle,
+ &s->info, s->monitor_fn);
+ if (composite_nomem(info_req, c)) return;
+
+ /* set the next stage */
+ composite_continue(c, info_req, continue_group_info, c);
+ break;
+
+ }
+}
+
+
+/*
+ * Stage 1: Receive SID found and send request for group info
+ */
+static void continue_name_found(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct group_info_state *s;
+ struct composite_context *info_req;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct group_info_state);
+
+ /* receive SID assiociated with name found */
+ c->status = libnet_LookupName_recv(ctx, c, &s->lookup);
+ if (!composite_is_ok(c)) return;
+
+ /* Is is a group SID actually ? */
+ if (s->lookup.out.sid_type != SID_NAME_DOM_GRP &&
+ s->lookup.out.sid_type != SID_NAME_ALIAS) {
+ composite_error(c, NT_STATUS_NO_SUCH_GROUP);
+ return;
+ }
+
+ /* prepare arguments for groupinfo call */
+ s->info.in.domain_handle = s->ctx->samr.handle;
+ s->info.in.groupname = s->group_name;
+ s->info.in.sid = s->lookup.out.sidstr;
+ /* we're looking for all information available */
+ s->info.in.level = GROUPINFOALL;
+
+ /* send the request */
+ info_req = libnet_rpc_groupinfo_send(s, s->ctx->event_ctx,
+ s->ctx->samr.samr_handle,
+ &s->info, s->monitor_fn);
+ if (composite_nomem(info_req, c)) return;
+
+ /* set the next stage */
+ composite_continue(c, info_req, continue_group_info, c);
+}
+
+
+/*
+ * Stage 2: Receive group information
+ */
+static void continue_group_info(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct group_info_state *s;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct group_info_state);
+
+ /* receive group information */
+ c->status = libnet_rpc_groupinfo_recv(ctx, c, &s->info);
+ if (!composite_is_ok(c)) return;
+
+ /* we're done */
+ composite_done(c);
+}
+
+
+/*
+ * Receive group information
+ *
+ * @param c composite context returned by libnet_GroupInfo_send
+ * @param mem_ctx memory context of this call
+ * @param io pointer to structure receiving results of the call
+ * @result nt status
+ */
+NTSTATUS libnet_GroupInfo_recv(struct composite_context* c, TALLOC_CTX *mem_ctx,
+ struct libnet_GroupInfo *io)
+{
+ NTSTATUS status;
+ struct group_info_state *s;
+
+ status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ /* put the results into io structure if everything went fine */
+ s = talloc_get_type_abort(c->private_data, struct group_info_state);
+
+ io->out.group_name = talloc_steal(mem_ctx,
+ s->info.out.info.all.name.string);
+ io->out.group_sid = talloc_steal(mem_ctx, s->lookup.out.sid);
+ io->out.num_members = s->info.out.info.all.num_members;
+ io->out.description = talloc_steal(mem_ctx, s->info.out.info.all.description.string);
+
+ io->out.error_string = talloc_strdup(mem_ctx, "Success");
+
+ } else {
+ io->out.error_string = talloc_asprintf(mem_ctx, "Error: %s", nt_errstr(status));
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Obtains specified group information
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of the call
+ * @param io pointer to a structure containing arguments and results of the call
+ */
+NTSTATUS libnet_GroupInfo(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_GroupInfo *io)
+{
+ struct composite_context *c = libnet_GroupInfo_send(ctx, mem_ctx,
+ io, NULL);
+ return libnet_GroupInfo_recv(c, mem_ctx, io);
+}
+
+
+struct grouplist_state {
+ struct libnet_context *ctx;
+ const char *domain_name;
+ struct lsa_DomainInfo dominfo;
+ int page_size;
+ uint32_t resume_index;
+ struct grouplist *groups;
+ uint32_t count;
+
+ struct libnet_DomainOpen domain_open;
+ struct lsa_QueryInfoPolicy query_domain;
+ struct samr_EnumDomainGroups group_list;
+
+ void (*monitor_fn)(struct monitor_msg*);
+};
+
+
+static void continue_lsa_domain_opened(struct composite_context *ctx);
+static void continue_domain_queried(struct tevent_req *subreq);
+static void continue_samr_domain_opened(struct composite_context *ctx);
+static void continue_groups_enumerated(struct tevent_req *subreq);
+
+
+/**
+ * Sends request to list (enumerate) group accounts
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param io pointer to structure containing arguments and results of this call
+ * @param monitor function pointer for receiving monitor messages
+ * @return compostite context of this request
+ */
+struct composite_context *libnet_GroupList_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_GroupList *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct grouplist_state *s;
+ struct tevent_req *subreq;
+ bool prereq_met = false;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct grouplist_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ /* store the arguments in the state structure */
+ s->ctx = ctx;
+ s->page_size = io->in.page_size;
+ s->resume_index = io->in.resume_index;
+ s->domain_name = talloc_strdup(c, io->in.domain_name);
+ s->monitor_fn = monitor;
+
+ /* make sure we have lsa domain handle before doing anything */
+ prereq_met = lsa_domain_opened(ctx, c, s->domain_name, &c, &s->domain_open,
+ continue_lsa_domain_opened, monitor);
+ if (!prereq_met) return c;
+
+ /* prepare arguments of QueryDomainInfo call */
+ s->query_domain.in.handle = &ctx->lsa.handle;
+ s->query_domain.in.level = LSA_POLICY_INFO_DOMAIN;
+ s->query_domain.out.info = talloc_zero(c, union lsa_PolicyInformation *);
+ if (composite_nomem(s->query_domain.out.info, c)) return c;
+
+ /* send the request */
+ subreq = dcerpc_lsa_QueryInfoPolicy_r_send(s, c->event_ctx,
+ ctx->lsa.pipe->binding_handle,
+ &s->query_domain);
+ if (composite_nomem(subreq, c)) return c;
+
+ tevent_req_set_callback(subreq, continue_domain_queried, c);
+ return c;
+}
+
+
+/*
+ * Stage 0.5 (optional): receive lsa domain handle and send
+ * request to query domain info
+ */
+static void continue_lsa_domain_opened(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct grouplist_state *s;
+ struct tevent_req *subreq;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct grouplist_state);
+
+ /* receive lsa domain handle */
+ c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domain_open);
+ if (!composite_is_ok(c)) return;
+
+ /* prepare arguments of QueryDomainInfo call */
+ s->query_domain.in.handle = &s->ctx->lsa.handle;
+ s->query_domain.in.level = LSA_POLICY_INFO_DOMAIN;
+ s->query_domain.out.info = talloc_zero(c, union lsa_PolicyInformation *);
+ if (composite_nomem(s->query_domain.out.info, c)) return;
+
+ /* send the request */
+ subreq = dcerpc_lsa_QueryInfoPolicy_r_send(s, c->event_ctx,
+ s->ctx->lsa.pipe->binding_handle,
+ &s->query_domain);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_domain_queried, c);
+}
+
+
+/*
+ * Stage 1: receive domain info and request to enum groups
+ * provided a valid samr handle is opened
+ */
+static void continue_domain_queried(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct grouplist_state *s;
+ bool prereq_met = false;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct grouplist_state);
+
+ /* receive result of rpc request */
+ c->status = dcerpc_lsa_QueryInfoPolicy_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ /* get the returned domain info */
+ s->dominfo = (*s->query_domain.out.info)->domain;
+
+ /* make sure we have samr domain handle before continuing */
+ prereq_met = samr_domain_opened(s->ctx, c, s->domain_name, &c, &s->domain_open,
+ continue_samr_domain_opened, s->monitor_fn);
+ if (!prereq_met) return;
+
+ /* prepare arguments od EnumDomainGroups call */
+ s->group_list.in.domain_handle = &s->ctx->samr.handle;
+ s->group_list.in.max_size = s->page_size;
+ s->group_list.in.resume_handle = &s->resume_index;
+ s->group_list.out.resume_handle = &s->resume_index;
+ s->group_list.out.num_entries = talloc(s, uint32_t);
+ if (composite_nomem(s->group_list.out.num_entries, c)) return;
+ s->group_list.out.sam = talloc(s, struct samr_SamArray *);
+ if (composite_nomem(s->group_list.out.sam, c)) return;
+
+ /* send the request */
+ subreq = dcerpc_samr_EnumDomainGroups_r_send(s, c->event_ctx,
+ s->ctx->samr.pipe->binding_handle,
+ &s->group_list);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_groups_enumerated, c);
+}
+
+
+/*
+ * Stage 1.5 (optional): receive samr domain handle
+ * and request to enumerate accounts
+ */
+static void continue_samr_domain_opened(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct grouplist_state *s;
+ struct tevent_req *subreq;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct grouplist_state);
+
+ /* receive samr domain handle */
+ c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domain_open);
+ if (!composite_is_ok(c)) return;
+
+ /* prepare arguments of EnumDomainGroups call */
+ s->group_list.in.domain_handle = &s->ctx->samr.handle;
+ s->group_list.in.max_size = s->page_size;
+ s->group_list.in.resume_handle = &s->resume_index;
+ s->group_list.out.resume_handle = &s->resume_index;
+ s->group_list.out.num_entries = talloc(s, uint32_t);
+ if (composite_nomem(s->group_list.out.num_entries, c)) return;
+ s->group_list.out.sam = talloc(s, struct samr_SamArray *);
+ if (composite_nomem(s->group_list.out.sam, c)) return;
+
+ /* send the request */
+ subreq = dcerpc_samr_EnumDomainGroups_r_send(s, c->event_ctx,
+ s->ctx->samr.pipe->binding_handle,
+ &s->group_list);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_groups_enumerated, c);
+}
+
+
+/*
+ * Stage 2: receive enumerated groups and their rids
+ */
+static void continue_groups_enumerated(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct grouplist_state *s;
+ uint32_t i;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct grouplist_state);
+
+ /* receive result of rpc request */
+ c->status = dcerpc_samr_EnumDomainGroups_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ /* get the actual status of the rpc call result
+ (instead of rpc layer) */
+ c->status = s->group_list.out.result;
+
+ /* we're interested in status "ok" as well as two
+ enum-specific status codes */
+ if (NT_STATUS_IS_OK(c->status) ||
+ NT_STATUS_EQUAL(c->status, STATUS_MORE_ENTRIES) ||
+ NT_STATUS_EQUAL(c->status, NT_STATUS_NO_MORE_ENTRIES)) {
+
+ /* get enumerated accounts counter and resume handle (the latter allows
+ making subsequent call to continue enumeration) */
+ s->resume_index = *s->group_list.out.resume_handle;
+ s->count = *s->group_list.out.num_entries;
+
+ /* prepare returned group accounts array */
+ s->groups = talloc_array(c, struct grouplist, (*s->group_list.out.sam)->count);
+ if (composite_nomem(s->groups, c)) return;
+
+ for (i = 0; i < (*s->group_list.out.sam)->count; i++) {
+ struct dom_sid *group_sid;
+ struct samr_SamEntry *entry = &(*s->group_list.out.sam)->entries[i];
+ struct dom_sid *domain_sid = (*s->query_domain.out.info)->domain.sid;
+
+ /* construct group sid from returned rid and queried domain sid */
+ group_sid = dom_sid_add_rid(c, domain_sid, entry->idx);
+ if (composite_nomem(group_sid, c)) return;
+
+ /* groupname */
+ s->groups[i].groupname = talloc_strdup(s->groups, entry->name.string);
+ if (composite_nomem(s->groups[i].groupname, c)) return;
+
+ /* sid string */
+ s->groups[i].sid = dom_sid_string(s->groups, group_sid);
+ if (composite_nomem(s->groups[i].sid, c)) return;
+ }
+
+ /* that's it */
+ composite_done(c);
+ return;
+ } else {
+ /* something went wrong */
+ composite_error(c, c->status);
+ return;
+ }
+}
+
+
+/**
+ * Receive result of GroupList call
+ *
+ * @param c composite context returned by send request routine
+ * @param mem_ctx memory context of this call
+ * @param io pointer to structure containing arguments and result of this call
+ * @param nt status
+ */
+NTSTATUS libnet_GroupList_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_GroupList *io)
+{
+ NTSTATUS status;
+ struct grouplist_state *s;
+
+ if (c == NULL || mem_ctx == NULL || io == NULL) {
+ talloc_free(c);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status) ||
+ NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
+
+ s = talloc_get_type_abort(c->private_data, struct grouplist_state);
+
+ /* get results from composite context */
+ io->out.count = s->count;
+ io->out.resume_index = s->resume_index;
+ io->out.groups = talloc_steal(mem_ctx, s->groups);
+
+ if (NT_STATUS_IS_OK(status)) {
+ io->out.error_string = talloc_asprintf(mem_ctx, "Success");
+ } else {
+ /* success, but we're not done yet */
+ io->out.error_string = talloc_asprintf(mem_ctx, "Success (status: %s)",
+ nt_errstr(status));
+ }
+
+ } else {
+ io->out.error_string = talloc_asprintf(mem_ctx, "Error: %s", nt_errstr(status));
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Enumerate domain groups
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param io pointer to structure containing arguments and result of this call
+ * @return nt status
+ */
+NTSTATUS libnet_GroupList(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_GroupList *io)
+{
+ struct composite_context *c;
+
+ c = libnet_GroupList_send(ctx, mem_ctx, io, NULL);
+ return libnet_GroupList_recv(c, mem_ctx, io);
+}
diff --git a/source4/libnet/libnet_group.h b/source4/libnet/libnet_group.h
new file mode 100644
index 0000000..8ac4743
--- /dev/null
+++ b/source4/libnet/libnet_group.h
@@ -0,0 +1,74 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Rafal Szczesniak 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.
+*/
+
+
+struct libnet_CreateGroup {
+ struct {
+ const char *group_name;
+ const char *domain_name;
+ } in;
+ struct {
+ const char *error_string;
+ } out;
+};
+
+enum libnet_GroupInfo_level {
+ GROUP_INFO_BY_NAME=0,
+ GROUP_INFO_BY_SID
+};
+
+struct libnet_GroupInfo {
+ struct {
+ const char *domain_name;
+ enum libnet_GroupInfo_level level;
+ union {
+ const char *group_name;
+ const struct dom_sid *group_sid;
+ } data;
+ } in;
+ struct {
+ const char *group_name;
+ struct dom_sid *group_sid;
+ uint32_t num_members;
+ const char *description;
+
+ const char *error_string;
+ } out;
+};
+
+
+struct libnet_GroupList {
+ struct {
+ const char *domain_name;
+ int page_size;
+ uint32_t resume_index;
+ } in;
+ struct {
+ int count;
+ uint32_t resume_index;
+
+ struct grouplist {
+ const char *sid;
+ const char *groupname;
+ } *groups;
+
+ const char *error_string;
+ } out;
+};
diff --git a/source4/libnet/libnet_join.c b/source4/libnet/libnet_join.c
new file mode 100644
index 0000000..d1afb4f
--- /dev/null
+++ b/source4/libnet/libnet_join.c
@@ -0,0 +1,1021 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+ Copyright (C) Brad Henry 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 "libnet/libnet.h"
+#include "librpc/gen_ndr/ndr_drsuapi_c.h"
+#include <ldb.h>
+#include <ldb_errors.h>
+#include "dsdb/samdb/samdb.h"
+#include "ldb_wrap.h"
+#include "libcli/security/security.h"
+#include "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_krb5.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "param/param.h"
+#include "param/provision.h"
+#include "system/kerberos.h"
+#include "auth/kerberos/kerberos.h"
+
+/*
+ * complete a domain join, when joining to a AD domain:
+ * 1.) connect and bind to the DRSUAPI pipe
+ * 2.) do a DsCrackNames() to find the machine account dn
+ * 3.) connect to LDAP
+ * 4.) do an ldap search to find the "msDS-KeyVersionNumber" of the machine account
+ * 5.) set the servicePrincipalName's of the machine account via LDAP, (maybe we should use DsWriteAccountSpn()...)
+ * 6.) do a DsCrackNames() to find the domain dn
+ * 7.) find out Site specific stuff, look at libnet_JoinSite() for details
+ */
+static NTSTATUS libnet_JoinADSDomain(struct libnet_context *ctx, struct libnet_JoinDomain *r)
+{
+ NTSTATUS status;
+
+ TALLOC_CTX *tmp_ctx;
+
+ const char *realm = r->out.realm;
+
+ const struct dcerpc_binding *samr_binding = r->out.samr_binding;
+
+ struct dcerpc_pipe *drsuapi_pipe;
+ struct dcerpc_binding *drsuapi_binding;
+ enum dcerpc_transport_t transport;
+ struct drsuapi_DsBind r_drsuapi_bind;
+ struct drsuapi_DsCrackNames r_crack_names;
+ struct drsuapi_DsNameString names[1];
+ struct policy_handle drsuapi_bind_handle;
+ struct GUID drsuapi_bind_guid;
+
+ struct ldb_context *remote_ldb;
+ struct ldb_dn *account_dn;
+ const char *account_dn_str;
+ const char *remote_ldb_url;
+ struct ldb_result *res;
+ struct ldb_message *msg;
+
+ int ret, rtn;
+
+ const char * const attrs[] = {
+ "msDS-KeyVersionNumber",
+ "servicePrincipalName",
+ "dNSHostName",
+ "objectGUID",
+ NULL,
+ };
+
+ r->out.error_string = NULL;
+
+ /* We need to convert between a samAccountName and domain to a
+ * DN in the directory. The correct way to do this is with
+ * DRSUAPI CrackNames */
+
+ /* Fiddle with the bindings, so get to DRSUAPI on
+ * NCACN_IP_TCP, sealed */
+ tmp_ctx = talloc_named(r, 0, "libnet_JoinADSDomain temp context");
+ if (!tmp_ctx) {
+ r->out.error_string = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ drsuapi_binding = dcerpc_binding_dup(tmp_ctx, samr_binding);
+ if (!drsuapi_binding) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ transport = dcerpc_binding_get_transport(drsuapi_binding);
+
+ /* DRSUAPI is only available on IP_TCP, and locally on NCALRPC */
+ if (transport != NCALRPC) {
+ status = dcerpc_binding_set_transport(drsuapi_binding, NCACN_IP_TCP);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(r,
+ "dcerpc_binding_set_transport failed: %s",
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+ }
+
+ status = dcerpc_binding_set_string_option(drsuapi_binding, "endpoint", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(r,
+ "dcerpc_binding_set_string_option failed: %s",
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ status = dcerpc_binding_set_flags(drsuapi_binding, DCERPC_SEAL, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(r,
+ "dcerpc_binding_set_flags failed: %s",
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ status = dcerpc_pipe_connect_b(tmp_ctx,
+ &drsuapi_pipe,
+ drsuapi_binding,
+ &ndr_table_drsuapi,
+ ctx->cred,
+ ctx->event_ctx,
+ ctx->lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(r,
+ "Connection to DRSUAPI pipe of PDC of domain '%s' failed: %s",
+ r->out.domain_name,
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ /* get a DRSUAPI pipe handle */
+ GUID_from_string(DRSUAPI_DS_BIND_GUID, &drsuapi_bind_guid);
+
+ r_drsuapi_bind.in.bind_guid = &drsuapi_bind_guid;
+ r_drsuapi_bind.in.bind_info = NULL;
+ r_drsuapi_bind.out.bind_handle = &drsuapi_bind_handle;
+
+ status = dcerpc_drsuapi_DsBind_r(drsuapi_pipe->binding_handle, tmp_ctx, &r_drsuapi_bind);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "dcerpc_drsuapi_DsBind failed - %s",
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ } else if (!W_ERROR_IS_OK(r_drsuapi_bind.out.result)) {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "DsBind failed - %s",
+ win_errstr(r_drsuapi_bind.out.result));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Actually 'crack' the names */
+ ZERO_STRUCT(r_crack_names);
+ r_crack_names.in.bind_handle = &drsuapi_bind_handle;
+ r_crack_names.in.level = 1;
+ r_crack_names.in.req = talloc(r, union drsuapi_DsNameRequest);
+ if (!r_crack_names.in.req) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ r_crack_names.in.req->req1.codepage = 1252; /* western european */
+ r_crack_names.in.req->req1.language = 0x00000407; /* german */
+ r_crack_names.in.req->req1.count = 1;
+ r_crack_names.in.req->req1.names = names;
+ r_crack_names.in.req->req1.format_flags = DRSUAPI_DS_NAME_FLAG_NO_FLAGS;
+ r_crack_names.in.req->req1.format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY;
+ r_crack_names.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
+ names[0].str = dom_sid_string(tmp_ctx, r->out.account_sid);
+ if (!names[0].str) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r_crack_names.out.ctr = talloc(r, union drsuapi_DsNameCtr);
+ r_crack_names.out.level_out = talloc(r, uint32_t);
+ if (!r_crack_names.out.ctr || !r_crack_names.out.level_out) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dcerpc_drsuapi_DsCrackNames_r(drsuapi_pipe->binding_handle, tmp_ctx, &r_crack_names);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s",
+ names[0].str,
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ } else if (!W_ERROR_IS_OK(r_crack_names.out.result)) {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "DsCrackNames failed - %s", win_errstr(r_crack_names.out.result));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ } else if (*r_crack_names.out.level_out != 1
+ || !r_crack_names.out.ctr->ctr1
+ || r_crack_names.out.ctr->ctr1->count != 1) {
+ r->out.error_string = talloc_asprintf(r, "DsCrackNames failed");
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INVALID_PARAMETER;
+ } else if (r_crack_names.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ r->out.error_string = talloc_asprintf(r, "DsCrackNames failed: %d", r_crack_names.out.ctr->ctr1->array[0].status);
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ } else if (r_crack_names.out.ctr->ctr1->array[0].result_name == NULL) {
+ r->out.error_string = talloc_asprintf(r, "DsCrackNames failed: no result name");
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* Store the DN of our machine account. */
+ account_dn_str = r_crack_names.out.ctr->ctr1->array[0].result_name;
+
+ /* Now we know the user's DN, open with LDAP, read and modify a few things */
+
+ remote_ldb_url = talloc_asprintf(tmp_ctx, "ldap://%s",
+ dcerpc_binding_get_string_option(drsuapi_binding, "target_hostname"));
+ if (!remote_ldb_url) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ remote_ldb = ldb_wrap_connect(tmp_ctx, ctx->event_ctx, ctx->lp_ctx,
+ remote_ldb_url,
+ NULL, ctx->cred, 0);
+ if (!remote_ldb) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ account_dn = ldb_dn_new(tmp_ctx, remote_ldb, account_dn_str);
+ if (account_dn == NULL) {
+ r->out.error_string = talloc_asprintf(r, "Invalid account dn: %s",
+ account_dn_str);
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* search for the user's record */
+ ret = ldb_search(remote_ldb, tmp_ctx, &res,
+ account_dn, LDB_SCOPE_BASE, attrs, NULL);
+ if (ret != LDB_SUCCESS) {
+ r->out.error_string = talloc_asprintf(r, "ldb_search for %s failed - %s",
+ account_dn_str, ldb_errstring(remote_ldb));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (res->count != 1) {
+ r->out.error_string = talloc_asprintf(r, "ldb_search for %s failed - found %d entries",
+ account_dn_str, res->count);
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Prepare a new message, for the modify */
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ msg->dn = res->msgs[0]->dn;
+
+ {
+ unsigned int i;
+ const char *service_principal_name[2];
+ const char *dns_host_name = strlower_talloc(msg,
+ talloc_asprintf(msg,
+ "%s.%s",
+ r->in.netbios_name,
+ realm));
+
+ if (!dns_host_name) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ service_principal_name[0] = talloc_asprintf(msg, "HOST/%s",
+ dns_host_name);
+ service_principal_name[1] = talloc_asprintf(msg, "HOST/%s",
+ r->in.netbios_name);
+
+ for (i=0; i < ARRAY_SIZE(service_principal_name); i++) {
+ if (!service_principal_name[i]) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ rtn = ldb_msg_add_string(msg, "servicePrincipalName",
+ service_principal_name[i]);
+ if (rtn != LDB_SUCCESS) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ rtn = ldb_msg_add_string(msg, "dNSHostName", dns_host_name);
+ if (rtn != LDB_SUCCESS) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rtn = dsdb_replace(remote_ldb, msg, 0);
+ if (rtn != LDB_SUCCESS) {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "Failed to replace entries on %s",
+ ldb_dn_get_linearized(msg->dn));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ msg->dn = res->msgs[0]->dn;
+
+ rtn = samdb_msg_add_uint(remote_ldb, msg, msg,
+ "msDS-SupportedEncryptionTypes", ENC_ALL_TYPES);
+ if (rtn != LDB_SUCCESS) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rtn = dsdb_replace(remote_ldb, msg, 0);
+ /* The remote server may not support this attribute, if it
+ * isn't a modern schema */
+ if (rtn != LDB_SUCCESS && rtn != LDB_ERR_NO_SUCH_ATTRIBUTE) {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "Failed to replace msDS-SupportedEncryptionTypes on %s",
+ ldb_dn_get_linearized(msg->dn));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* DsCrackNames to find out the DN of the domain. */
+ r_crack_names.in.req->req1.format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT;
+ r_crack_names.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
+ names[0].str = talloc_asprintf(tmp_ctx, "%s\\", r->out.domain_name);
+ if (!names[0].str) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dcerpc_drsuapi_DsCrackNames_r(drsuapi_pipe->binding_handle, tmp_ctx, &r_crack_names);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s",
+ r->in.domain_name,
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ } else if (!W_ERROR_IS_OK(r_crack_names.out.result)) {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "DsCrackNames failed - %s", win_errstr(r_crack_names.out.result));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ } else if (*r_crack_names.out.level_out != 1
+ || !r_crack_names.out.ctr->ctr1
+ || r_crack_names.out.ctr->ctr1->count != 1
+ || !r_crack_names.out.ctr->ctr1->array[0].result_name
+ || r_crack_names.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ r->out.error_string = talloc_asprintf(r, "DsCrackNames failed");
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Store the account DN. */
+ r->out.account_dn_str = account_dn_str;
+ talloc_steal(r, account_dn_str);
+
+ /* Store the domain DN. */
+ r->out.domain_dn_str = r_crack_names.out.ctr->ctr1->array[0].result_name;
+ talloc_steal(r, r_crack_names.out.ctr->ctr1->array[0].result_name);
+
+ /* Store the KVNO of the account, critical for some kerberos
+ * operations */
+ r->out.kvno = ldb_msg_find_attr_as_uint(res->msgs[0], "msDS-KeyVersionNumber", 0);
+
+ /* Store the account GUID. */
+ r->out.account_guid = samdb_result_guid(res->msgs[0], "objectGUID");
+
+ if (r->in.acct_type == ACB_SVRTRUST) {
+ status = libnet_JoinSite(ctx, remote_ldb, r);
+ }
+ talloc_free(tmp_ctx);
+
+ return status;
+}
+
+/*
+ * do a domain join using DCERPC/SAMR calls
+ * - connect to the LSA pipe, to try and find out information about the domain
+ * - create a secondary connection to SAMR pipe
+ * - do a samr_Connect to get a policy handle
+ * - do a samr_LookupDomain to get the domain sid
+ * - do a samr_OpenDomain to get a domain handle
+ * - do a samr_CreateAccount to try and get a new account
+ *
+ * If that fails, do:
+ * - do a samr_LookupNames to get the users rid
+ * - do a samr_OpenUser to get a user handle
+ * - potentially delete and recreate the user
+ * - assert the account is of the right type with samrQueryUserInfo
+ *
+ * - call libnet_SetPassword_samr_handle to set the password,
+ * and pass a samr_UserInfo21 struct to set full_name and the account flags
+ *
+ * - do some ADS specific things when we join as Domain Controller,
+ * look at libnet_joinADSDomain() for the details
+ */
+NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_JoinDomain *r)
+{
+ TALLOC_CTX *tmp_ctx;
+
+ NTSTATUS status, cu_status;
+
+ struct libnet_RpcConnect *connect_with_info;
+ struct dcerpc_pipe *samr_pipe;
+
+ struct samr_Connect sc;
+ struct policy_handle p_handle;
+ struct samr_OpenDomain od;
+ struct policy_handle d_handle;
+ struct samr_LookupNames ln;
+ struct samr_Ids rids, types;
+ struct samr_OpenUser ou;
+ struct samr_CreateUser2 cu;
+ struct policy_handle *u_handle = NULL;
+ struct samr_QueryUserInfo qui;
+ union samr_UserInfo *uinfo;
+ struct samr_UserInfo21 u_info21;
+ union libnet_SetPassword r2;
+ struct samr_GetUserPwInfo pwp;
+ struct samr_PwInfo info;
+ struct lsa_String samr_account_name;
+
+ uint32_t acct_flags, old_acct_flags;
+ uint32_t rid, access_granted;
+ int policy_min_pw_len = 0;
+
+ struct dom_sid *account_sid = NULL;
+ const char *password_str = NULL;
+
+ r->out.error_string = NULL;
+ ZERO_STRUCT(r2);
+
+ tmp_ctx = talloc_named(mem_ctx, 0, "libnet_Join temp context");
+ if (!tmp_ctx) {
+ r->out.error_string = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ u_handle = talloc(tmp_ctx, struct policy_handle);
+ if (!u_handle) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ connect_with_info = talloc_zero(tmp_ctx, struct libnet_RpcConnect);
+ if (!connect_with_info) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* prepare connect to the SAMR pipe of PDC */
+ if (r->in.level == LIBNET_JOINDOMAIN_AUTOMATIC) {
+ connect_with_info->in.binding = NULL;
+ connect_with_info->in.name = r->in.domain_name;
+ } else {
+ connect_with_info->in.binding = r->in.binding;
+ connect_with_info->in.name = NULL;
+ }
+
+ /* This level makes a connection to the LSA pipe on the way,
+ * to get some useful bits of information about the domain */
+ connect_with_info->level = LIBNET_RPC_CONNECT_DC_INFO;
+ connect_with_info->in.dcerpc_iface = &ndr_table_samr;
+
+ /*
+ establish the SAMR connection
+ */
+ status = libnet_RpcConnect(ctx, tmp_ctx, connect_with_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (r->in.binding) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "Connection to SAMR pipe of DC %s failed: %s",
+ r->in.binding, connect_with_info->out.error_string);
+ } else {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "Connection to SAMR pipe of PDC for %s failed: %s",
+ r->in.domain_name, connect_with_info->out.error_string);
+ }
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ samr_pipe = connect_with_info->out.dcerpc_pipe;
+
+ /* prepare samr_Connect */
+ ZERO_STRUCT(p_handle);
+ sc.in.system_name = NULL;
+ sc.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ sc.out.connect_handle = &p_handle;
+
+ /* 2. do a samr_Connect to get a policy handle */
+ status = dcerpc_samr_Connect_r(samr_pipe->binding_handle, tmp_ctx, &sc);
+ if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(sc.out.result)) {
+ status = sc.out.result;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "samr_Connect failed: %s",
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ /* If this is a connection on ncacn_ip_tcp to Win2k3 SP1, we don't get back this useful info */
+ if (!connect_with_info->out.domain_name) {
+ if (r->in.level == LIBNET_JOINDOMAIN_AUTOMATIC) {
+ connect_with_info->out.domain_name = talloc_strdup(tmp_ctx, r->in.domain_name);
+ } else {
+ /* Bugger, we just lost our way to automatically find the domain name */
+ connect_with_info->out.domain_name = talloc_strdup(tmp_ctx, lpcfg_workgroup(ctx->lp_ctx));
+ connect_with_info->out.realm = talloc_strdup(tmp_ctx, lpcfg_realm(ctx->lp_ctx));
+ }
+ }
+
+ /* Perhaps we didn't get a SID above, because we are against ncacn_ip_tcp */
+ if (!connect_with_info->out.domain_sid) {
+ struct lsa_String name;
+ struct samr_LookupDomain l;
+ struct dom_sid2 *sid = NULL;
+ name.string = connect_with_info->out.domain_name;
+ l.in.connect_handle = &p_handle;
+ l.in.domain_name = &name;
+ l.out.sid = &sid;
+
+ status = dcerpc_samr_LookupDomain_r(samr_pipe->binding_handle, tmp_ctx, &l);
+ if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(l.out.result)) {
+ status = l.out.result;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "SAMR LookupDomain failed: %s",
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+ connect_with_info->out.domain_sid = *l.out.sid;
+ }
+
+ /* prepare samr_OpenDomain */
+ ZERO_STRUCT(d_handle);
+ od.in.connect_handle = &p_handle;
+ od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ od.in.sid = connect_with_info->out.domain_sid;
+ od.out.domain_handle = &d_handle;
+
+ /* do a samr_OpenDomain to get a domain handle */
+ status = dcerpc_samr_OpenDomain_r(samr_pipe->binding_handle, tmp_ctx, &od);
+ if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(od.out.result)) {
+ status = od.out.result;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ struct dom_sid_buf buf;
+ r->out.error_string = talloc_asprintf(
+ mem_ctx,
+ "samr_OpenDomain for [%s] failed: %s",
+ dom_sid_str_buf(connect_with_info->out.domain_sid,
+ &buf),
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ /* prepare samr_CreateUser2 */
+ ZERO_STRUCTP(u_handle);
+ cu.in.domain_handle = &d_handle;
+ cu.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ samr_account_name.string = r->in.account_name;
+ cu.in.account_name = &samr_account_name;
+ cu.in.acct_flags = r->in.acct_type;
+ cu.out.user_handle = u_handle;
+ cu.out.rid = &rid;
+ cu.out.access_granted = &access_granted;
+
+ /* do a samr_CreateUser2 to get an account handle, or an error */
+ cu_status = dcerpc_samr_CreateUser2_r(samr_pipe->binding_handle, tmp_ctx, &cu);
+ if (NT_STATUS_IS_OK(cu_status) && !NT_STATUS_IS_OK(cu.out.result)) {
+ cu_status = cu.out.result;
+ }
+ status = cu_status;
+ if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
+ /* prepare samr_LookupNames */
+ ln.in.domain_handle = &d_handle;
+ ln.in.num_names = 1;
+ ln.in.names = talloc_array(tmp_ctx, struct lsa_String, 1);
+ ln.out.rids = &rids;
+ ln.out.types = &types;
+ if (!ln.in.names) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ln.in.names[0].string = r->in.account_name;
+
+ /* 5. do a samr_LookupNames to get the users rid */
+ status = dcerpc_samr_LookupNames_r(samr_pipe->binding_handle, tmp_ctx, &ln);
+ if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(ln.out.result)) {
+ status = ln.out.result;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "samr_LookupNames for [%s] failed: %s",
+ r->in.account_name,
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ /* check if we got one RID for the user */
+ if (ln.out.rids->count != 1) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "samr_LookupNames for [%s] returns %d RIDs",
+ r->in.account_name, ln.out.rids->count);
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ if (ln.out.types->count != 1) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "samr_LookupNames for [%s] returns %d RID TYPEs",
+ r->in.account_name, ln.out.types->count);
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ /* prepare samr_OpenUser */
+ ZERO_STRUCTP(u_handle);
+ ou.in.domain_handle = &d_handle;
+ ou.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ ou.in.rid = ln.out.rids->ids[0];
+ rid = ou.in.rid;
+ ou.out.user_handle = u_handle;
+
+ /* 6. do a samr_OpenUser to get a user handle */
+ status = dcerpc_samr_OpenUser_r(samr_pipe->binding_handle, tmp_ctx, &ou);
+ if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(ou.out.result)) {
+ status = ou.out.result;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "samr_OpenUser for [%s] failed: %s",
+ r->in.account_name,
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ if (r->in.recreate_account) {
+ struct samr_DeleteUser d;
+ d.in.user_handle = u_handle;
+ d.out.user_handle = u_handle;
+ status = dcerpc_samr_DeleteUser_r(samr_pipe->binding_handle, mem_ctx, &d);
+ if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(d.out.result)) {
+ status = d.out.result;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "samr_DeleteUser (for recreate) of [%s] failed: %s",
+ r->in.account_name,
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ /* We want to recreate, so delete and another samr_CreateUser2 */
+
+ /* &cu filled in above */
+ status = dcerpc_samr_CreateUser2_r(samr_pipe->binding_handle, tmp_ctx, &cu);
+ if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(cu.out.result)) {
+ status = cu.out.result;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "samr_CreateUser2 (recreate) for [%s] failed: %s",
+ r->in.account_name, nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+ }
+ } else if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "samr_CreateUser2 for [%s] failed: %s",
+ r->in.account_name, nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ /* prepare samr_QueryUserInfo (get flags) */
+ qui.in.user_handle = u_handle;
+ qui.in.level = 16;
+ qui.out.info = &uinfo;
+
+ status = dcerpc_samr_QueryUserInfo_r(samr_pipe->binding_handle, tmp_ctx, &qui);
+ if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(qui.out.result)) {
+ status = qui.out.result;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "samr_QueryUserInfo for [%s] failed: %s",
+ r->in.account_name,
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ if (!uinfo) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ r->out.error_string
+ = talloc_asprintf(mem_ctx,
+ "samr_QueryUserInfo failed to return qui.out.info for [%s]: %s",
+ r->in.account_name, nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ old_acct_flags = (uinfo->info16.acct_flags & (ACB_WSTRUST | ACB_SVRTRUST | ACB_DOMTRUST));
+ /* Possibly bail if the account is of the wrong type */
+ if (old_acct_flags
+ != r->in.acct_type) {
+ const char *old_account_type, *new_account_type;
+ switch (old_acct_flags) {
+ case ACB_WSTRUST:
+ old_account_type = "domain member (member)";
+ break;
+ case ACB_SVRTRUST:
+ old_account_type = "domain controller (bdc)";
+ break;
+ case ACB_DOMTRUST:
+ old_account_type = "trusted domain";
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ switch (r->in.acct_type) {
+ case ACB_WSTRUST:
+ new_account_type = "domain member (member)";
+ break;
+ case ACB_SVRTRUST:
+ new_account_type = "domain controller (bdc)";
+ break;
+ case ACB_DOMTRUST:
+ new_account_type = "trusted domain";
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!NT_STATUS_EQUAL(cu_status, NT_STATUS_USER_EXISTS)) {
+ /* We created a new user, but they didn't come out the right type?!? */
+ r->out.error_string
+ = talloc_asprintf(mem_ctx,
+ "We asked to create a new machine account (%s) of type %s, "
+ "but we got an account of type %s. This is unexpected. "
+ "Perhaps delete the account and try again.",
+ r->in.account_name, new_account_type, old_account_type);
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INVALID_PARAMETER;
+ } else {
+ /* The account is of the wrong type, so bail */
+
+ /* TODO: We should allow a --force option to override, and redo this from the top setting r.in.recreate_account */
+ r->out.error_string
+ = talloc_asprintf(mem_ctx,
+ "The machine account (%s) already exists in the domain %s, "
+ "but is a %s. You asked to join as a %s. Please delete "
+ "the account and try again.",
+ r->in.account_name, connect_with_info->out.domain_name, old_account_type, new_account_type);
+ talloc_free(tmp_ctx);
+ return NT_STATUS_USER_EXISTS;
+ }
+ } else {
+ acct_flags = uinfo->info16.acct_flags;
+ }
+
+ acct_flags = (acct_flags & ~(ACB_DISABLED|ACB_PWNOTREQ));
+
+ /* Find out what password policy this user has */
+ pwp.in.user_handle = u_handle;
+ pwp.out.info = &info;
+
+ status = dcerpc_samr_GetUserPwInfo_r(samr_pipe->binding_handle, tmp_ctx, &pwp);
+ if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(pwp.out.result)) {
+ status = pwp.out.result;
+ }
+ if (NT_STATUS_IS_OK(status)) {
+ policy_min_pw_len = pwp.out.info->min_password_length;
+ }
+
+ if (r->in.account_pass != NULL) {
+ password_str = talloc_strdup(tmp_ctx, r->in.account_pass);
+ } else {
+ /* Grab a password of that minimum length */
+ password_str = generate_random_password(tmp_ctx,
+ MAX(8, policy_min_pw_len), 255);
+ }
+ if (!password_str) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* set full_name and reset flags */
+ ZERO_STRUCT(u_info21);
+ u_info21.full_name.string = r->in.account_name;
+ u_info21.acct_flags = acct_flags;
+ u_info21.fields_present = SAMR_FIELD_FULL_NAME | SAMR_FIELD_ACCT_FLAGS;
+
+ r2.samr_handle.level = LIBNET_SET_PASSWORD_SAMR_HANDLE;
+ r2.samr_handle.in.account_name = r->in.account_name;
+ r2.samr_handle.in.newpassword = password_str;
+ r2.samr_handle.in.user_handle = u_handle;
+ r2.samr_handle.in.dcerpc_pipe = samr_pipe;
+ r2.samr_handle.in.info21 = &u_info21;
+
+ status = libnet_SetPassword(ctx, tmp_ctx, &r2);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_steal(mem_ctx, r2.samr_handle.out.error_string);
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ account_sid = dom_sid_add_rid(mem_ctx, connect_with_info->out.domain_sid, rid);
+ if (!account_sid) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Finish out by pushing various bits of status data out for the caller to use */
+ r->out.join_password = password_str;
+ talloc_steal(mem_ctx, r->out.join_password);
+
+ r->out.domain_sid = connect_with_info->out.domain_sid;
+ talloc_steal(mem_ctx, r->out.domain_sid);
+
+ r->out.account_sid = account_sid;
+ talloc_steal(mem_ctx, r->out.account_sid);
+
+ r->out.domain_name = connect_with_info->out.domain_name;
+ talloc_steal(mem_ctx, r->out.domain_name);
+ r->out.realm = connect_with_info->out.realm;
+ talloc_steal(mem_ctx, r->out.realm);
+ r->out.samr_pipe = samr_pipe;
+ talloc_reparent(tmp_ctx, mem_ctx, samr_pipe);
+ r->out.samr_binding = samr_pipe->binding;
+ r->out.user_handle = u_handle;
+ talloc_steal(mem_ctx, u_handle);
+ r->out.error_string = r2.samr_handle.out.error_string;
+ talloc_steal(mem_ctx, r2.samr_handle.out.error_string);
+ r->out.kvno = 0;
+ r->out.server_dn_str = NULL;
+ talloc_free(tmp_ctx);
+
+ /* Now, if it was AD, then we want to start looking changing a
+ * few more things. Otherwise, we are done. */
+ if (r->out.realm) {
+ status = libnet_JoinADSDomain(ctx, r);
+ return status;
+ }
+
+ return status;
+}
+
+NTSTATUS libnet_Join_member(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_Join_member *r)
+{
+ NTSTATUS status;
+ TALLOC_CTX *tmp_mem;
+ struct libnet_JoinDomain *r2;
+ struct provision_store_self_join_settings *set_secrets;
+ uint32_t acct_type = 0;
+ const char *account_name;
+ const char *netbios_name;
+ const char *error_string = NULL;
+
+ r->out.error_string = NULL;
+
+ tmp_mem = talloc_new(mem_ctx);
+ if (!tmp_mem) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r2 = talloc_zero(tmp_mem, struct libnet_JoinDomain);
+ if (!r2) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ acct_type = ACB_WSTRUST;
+
+ if (r->in.netbios_name != NULL) {
+ netbios_name = r->in.netbios_name;
+ } else {
+ netbios_name = talloc_strdup(tmp_mem, lpcfg_netbios_name(ctx->lp_ctx));
+ if (!netbios_name) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ }
+
+ account_name = talloc_asprintf(tmp_mem, "%s$", netbios_name);
+ if (!account_name) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ /*
+ * join the domain
+ */
+ r2->in.domain_name = r->in.domain_name;
+ r2->in.account_name = account_name;
+ r2->in.netbios_name = netbios_name;
+ r2->in.level = LIBNET_JOINDOMAIN_AUTOMATIC;
+ r2->in.acct_type = acct_type;
+ r2->in.recreate_account = false;
+ r2->in.account_pass = r->in.account_pass;
+ status = libnet_JoinDomain(ctx, r2, r2);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_steal(mem_ctx, r2->out.error_string);
+ goto out;
+ }
+
+ set_secrets = talloc_zero(tmp_mem,
+ struct provision_store_self_join_settings);
+ if (!set_secrets) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ set_secrets->domain_name = r2->out.domain_name;
+ set_secrets->realm = r2->out.realm;
+ set_secrets->netbios_name = netbios_name;
+ set_secrets->secure_channel_type = SEC_CHAN_WKSTA;
+ set_secrets->machine_password = r2->out.join_password;
+ set_secrets->key_version_number = r2->out.kvno;
+ set_secrets->domain_sid = r2->out.domain_sid;
+
+ status = provision_store_self_join(ctx, ctx->lp_ctx, ctx->event_ctx, set_secrets, &error_string);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (error_string) {
+ r->out.error_string = talloc_steal(mem_ctx, error_string);
+ } else {
+ r->out.error_string
+ = talloc_asprintf(mem_ctx,
+ "provision_store_self_join failed with %s",
+ nt_errstr(status));
+ }
+ goto out;
+ }
+
+ /* move all out parameter to the callers TALLOC_CTX */
+ r->out.join_password = talloc_move(mem_ctx, &r2->out.join_password);
+ r->out.domain_sid = talloc_move(mem_ctx, &r2->out.domain_sid);
+ r->out.domain_name = talloc_move(mem_ctx, &r2->out.domain_name);
+ status = NT_STATUS_OK;
+out:
+ talloc_free(tmp_mem);
+ return status;
+}
+
diff --git a/source4/libnet/libnet_join.h b/source4/libnet/libnet_join.h
new file mode 100644
index 0000000..c8a4a40
--- /dev/null
+++ b/source4/libnet/libnet_join.h
@@ -0,0 +1,101 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+ Copyright (C) Brad Henry 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 __LIBNET_JOIN_H__
+#define __LIBNET_JOIN_H__
+
+#include "librpc/gen_ndr/netlogon.h"
+
+enum libnet_Join_level {
+ LIBNET_JOIN_AUTOMATIC,
+ LIBNET_JOIN_SPECIFIED,
+};
+
+enum libnet_JoinDomain_level {
+ LIBNET_JOINDOMAIN_AUTOMATIC,
+ LIBNET_JOINDOMAIN_SPECIFIED,
+};
+
+struct libnet_JoinDomain {
+ struct {
+ const char *domain_name;
+ const char *account_name;
+ const char *netbios_name;
+ const char *binding;
+ enum libnet_JoinDomain_level level;
+ uint32_t acct_type;
+ bool recreate_account;
+ const char *account_pass;
+ } in;
+
+ struct {
+ const char *error_string;
+ const char *join_password;
+ struct dom_sid *domain_sid;
+ const char *domain_name;
+ const char *realm;
+ const char *domain_dn_str;
+ const char *account_dn_str;
+ const char *server_dn_str;
+ uint32_t kvno; /* msDS-KeyVersionNumber */
+ struct dcerpc_pipe *samr_pipe;
+ const struct dcerpc_binding *samr_binding;
+ struct policy_handle *user_handle;
+ struct dom_sid *account_sid;
+ struct GUID account_guid;
+ } out;
+};
+
+struct libnet_Join_member {
+ struct {
+ const char *domain_name;
+ const char *netbios_name;
+ enum libnet_Join_level level;
+ const char *account_pass;
+ } in;
+
+ struct {
+ const char *error_string;
+ const char *join_password;
+ struct dom_sid *domain_sid;
+ const char *domain_name;
+ } out;
+};
+
+struct libnet_set_join_secrets {
+ struct {
+ const char *domain_name;
+ const char *realm;
+ const char *netbios_name;
+ const char *account_name;
+ enum netr_SchannelType join_type;
+ const char *join_password;
+ int kvno;
+ struct dom_sid *domain_sid;
+ } in;
+
+ struct {
+ const char *error_string;
+ } out;
+};
+
+
+#endif /* __LIBNET_JOIN_H__ */
diff --git a/source4/libnet/libnet_lookup.c b/source4/libnet/libnet_lookup.c
new file mode 100644
index 0000000..63ce601
--- /dev/null
+++ b/source4/libnet/libnet_lookup.c
@@ -0,0 +1,448 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Rafal Szczesniak 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/>.
+*/
+
+/*
+ a composite function for name resolving
+*/
+
+#include "includes.h"
+#include "libnet/libnet.h"
+#include "libcli/composite/composite.h"
+#include "auth/credentials/credentials.h"
+#include "libcli/resolve/resolve.h"
+#include "libcli/finddc.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "param/param.h"
+
+struct lookup_state {
+ struct nbt_name hostname;
+ const char *address;
+};
+
+
+static void continue_name_resolved(struct composite_context *ctx);
+
+
+/**
+ * Sends asynchronous Lookup request
+ *
+ * @param io arguments and result of the call
+ */
+
+struct composite_context *libnet_Lookup_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_Lookup *io)
+{
+ struct composite_context *c;
+ struct lookup_state *s;
+ struct composite_context *cresolve_req;
+ struct resolve_context *resolve_ctx;
+
+ /* allocate context and state structures */
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct lookup_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ if (io == NULL || io->in.hostname == NULL) {
+ composite_error(c, NT_STATUS_INVALID_PARAMETER);
+ return c;
+ }
+
+ /* parameters */
+ s->hostname.name = talloc_strdup(s, io->in.hostname);
+ if (composite_nomem(s->hostname.name, c)) return c;
+
+ s->hostname.type = io->in.type;
+ s->hostname.scope = NULL;
+
+ /* name resolution methods */
+ if (io->in.resolve_ctx) {
+ resolve_ctx = io->in.resolve_ctx;
+ } else {
+ resolve_ctx = ctx->resolve_ctx;
+ }
+
+ /* send resolve request */
+ cresolve_req = resolve_name_send(resolve_ctx, s, &s->hostname, c->event_ctx);
+ if (composite_nomem(cresolve_req, c)) return c;
+
+ composite_continue(c, cresolve_req, continue_name_resolved, c);
+ return c;
+}
+
+
+static void continue_name_resolved(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct lookup_state *s;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct lookup_state);
+
+ c->status = resolve_name_recv(ctx, s, &s->address);
+
+ composite_done(c);
+}
+
+
+/**
+ * Waits for and receives results of asynchronous Lookup call
+ *
+ * @param c composite context returned by asynchronous Lookup call
+ * @param mem_ctx memory context of the call
+ * @param io pointer to results (and arguments) of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_Lookup_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_Lookup *io)
+{
+ NTSTATUS status;
+ struct lookup_state *s;
+
+ status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ char **address;
+
+ s = talloc_get_type(c->private_data, struct lookup_state);
+
+ address = str_list_make_single(mem_ctx, s->address);
+ NT_STATUS_HAVE_NO_MEMORY(address);
+ io->out.address = discard_const_p(const char *, address);
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Synchronous version of Lookup call
+ *
+ * @param mem_ctx memory context for the call
+ * @param io arguments and results of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_Lookup(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_Lookup *io)
+{
+ struct composite_context *c = libnet_Lookup_send(ctx, mem_ctx, io);
+ return libnet_Lookup_recv(c, mem_ctx, io);
+}
+
+
+/*
+ * Shortcut functions to find common types of name
+ * (and skip nbt name type argument)
+ */
+
+
+/**
+ * Sends asynchronous LookupHost request
+ */
+struct composite_context* libnet_LookupHost_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_Lookup *io)
+{
+ io->in.type = NBT_NAME_SERVER;
+ return libnet_Lookup_send(ctx, mem_ctx, io);
+}
+
+
+
+/**
+ * Synchronous version of LookupHost call
+ */
+NTSTATUS libnet_LookupHost(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_Lookup *io)
+{
+ struct composite_context *c = libnet_LookupHost_send(ctx, mem_ctx, io);
+ return libnet_Lookup_recv(c, mem_ctx, io);
+}
+
+
+/**
+ * Sends asynchronous LookupDCs request
+ */
+struct tevent_req *libnet_LookupDCs_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_LookupDCs *io)
+{
+ struct tevent_req *req;
+ struct finddcs finddcs_io;
+
+ ZERO_STRUCT(finddcs_io);
+
+ if (strcasecmp_m(io->in.domain_name, lpcfg_workgroup(ctx->lp_ctx)) == 0) {
+ finddcs_io.in.domain_name = lpcfg_dnsdomain(ctx->lp_ctx);
+ } else {
+ finddcs_io.in.domain_name = io->in.domain_name;
+ }
+ finddcs_io.in.minimum_dc_flags = NBT_SERVER_LDAP | NBT_SERVER_DS | NBT_SERVER_WRITABLE;
+ finddcs_io.in.server_address = ctx->server_address;
+
+ req = finddcs_cldap_send(mem_ctx, &finddcs_io, ctx->resolve_ctx, ctx->event_ctx);
+ return req;
+}
+
+/**
+ * Waits for and receives results of asynchronous Lookup call
+ *
+ * @param c composite context returned by asynchronous Lookup call
+ * @param mem_ctx memory context of the call
+ * @param io pointer to results (and arguments) of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_LookupDCs_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct libnet_LookupDCs *io)
+{
+ NTSTATUS status;
+ struct finddcs finddcs_io;
+ status = finddcs_cldap_recv(req, mem_ctx, &finddcs_io);
+ talloc_free(req);
+ io->out.num_dcs = 1;
+ io->out.dcs = talloc(mem_ctx, struct nbt_dc_name);
+ NT_STATUS_HAVE_NO_MEMORY(io->out.dcs);
+ io->out.dcs[0].address = finddcs_io.out.address;
+ io->out.dcs[0].name = finddcs_io.out.netlogon.data.nt5_ex.pdc_dns_name;
+ return status;
+}
+
+
+/**
+ * Synchronous version of LookupDCs
+ */
+NTSTATUS libnet_LookupDCs(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_LookupDCs *io)
+{
+ struct tevent_req *req = libnet_LookupDCs_send(ctx, mem_ctx, io);
+ return libnet_LookupDCs_recv(req, mem_ctx, io);
+}
+
+
+struct lookup_name_state {
+ struct libnet_context *ctx;
+ const char *name;
+ uint32_t count;
+ struct libnet_DomainOpen domopen;
+ struct lsa_LookupNames lookup;
+ struct lsa_TransSidArray sids;
+ struct lsa_String *names;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static bool prepare_lookup_params(struct libnet_context *ctx,
+ struct composite_context *c,
+ struct lookup_name_state *s);
+static void continue_lookup_name(struct composite_context *ctx);
+static void continue_name_found(struct tevent_req *subreq);
+
+
+struct composite_context* libnet_LookupName_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_LookupName *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct lookup_name_state *s;
+ struct tevent_req *subreq;
+ bool prereq_met = false;
+
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct lookup_name_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ s->name = talloc_strdup(c, io->in.name);
+ s->monitor_fn = monitor;
+ s->ctx = ctx;
+
+ prereq_met = lsa_domain_opened(ctx, c, io->in.domain_name, &c, &s->domopen,
+ continue_lookup_name, monitor);
+ if (!prereq_met) return c;
+
+ if (!prepare_lookup_params(ctx, c, s)) return c;
+
+ subreq = dcerpc_lsa_LookupNames_r_send(s, c->event_ctx,
+ ctx->lsa.pipe->binding_handle,
+ &s->lookup);
+ if (composite_nomem(subreq, c)) return c;
+
+ tevent_req_set_callback(subreq, continue_name_found, c);
+ return c;
+}
+
+
+static bool prepare_lookup_params(struct libnet_context *ctx,
+ struct composite_context *c,
+ struct lookup_name_state *s)
+{
+ const int single_name = 1;
+
+ s->sids.count = 0;
+ s->sids.sids = NULL;
+
+ s->names = talloc_array(s, struct lsa_String, single_name);
+ if (composite_nomem(s->names, c)) return false;
+ s->names[0].string = s->name;
+
+ s->lookup.in.handle = &ctx->lsa.handle;
+ s->lookup.in.num_names = single_name;
+ s->lookup.in.names = s->names;
+ s->lookup.in.sids = &s->sids;
+ s->lookup.in.level = 1;
+ s->lookup.in.count = &s->count;
+ s->lookup.out.count = &s->count;
+ s->lookup.out.sids = &s->sids;
+ s->lookup.out.domains = talloc_zero(s, struct lsa_RefDomainList *);
+ if (composite_nomem(s->lookup.out.domains, c)) return false;
+
+ return true;
+}
+
+
+static void continue_lookup_name(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct lookup_name_state *s;
+ struct tevent_req *subreq;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct lookup_name_state);
+
+ c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domopen);
+ if (!composite_is_ok(c)) return;
+
+ if (!prepare_lookup_params(s->ctx, c, s)) return;
+
+ subreq = dcerpc_lsa_LookupNames_r_send(s, c->event_ctx,
+ s->ctx->lsa.pipe->binding_handle,
+ &s->lookup);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_name_found, c);
+}
+
+
+static void continue_name_found(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct lookup_name_state *s;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type(c->private_data, struct lookup_name_state);
+
+ c->status = dcerpc_lsa_LookupNames_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ c->status = s->lookup.out.result;
+ if (!composite_is_ok(c)) return;
+
+ if (s->lookup.out.sids->count != s->lookup.in.num_names) {
+ composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ composite_done(c);
+}
+
+
+NTSTATUS libnet_LookupName_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_LookupName *io)
+{
+ NTSTATUS status;
+ struct lookup_name_state *s = NULL;
+ struct lsa_RefDomainList *domains = NULL;
+ struct lsa_TransSidArray *sids = NULL;
+
+ status = composite_wait(c);
+ ZERO_STRUCT(io->out);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ io->out.error_string = talloc_asprintf(mem_ctx, "Error: %s",
+ nt_errstr(status));
+ goto done;
+ }
+
+ s = talloc_get_type(c->private_data, struct lookup_name_state);
+
+ if (*s->lookup.out.count == 0) {
+ goto success;
+ }
+
+ domains = *s->lookup.out.domains;
+ sids = s->lookup.out.sids;
+
+ if (domains == NULL || sids == NULL) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ io->out.error_string = talloc_asprintf(mem_ctx, "Error: %s",
+ nt_errstr(status));
+ goto done;
+ }
+
+ if (sids->count == 0) {
+ goto success;
+ }
+
+ io->out.rid = sids->sids[0].rid;
+ io->out.sid_type = sids->sids[0].sid_type;
+ if (domains->count > 0) {
+ io->out.sid = dom_sid_add_rid(mem_ctx, domains->domains[0].sid,
+ io->out.rid);
+ if (io->out.sid == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ io->out.sidstr = dom_sid_string(mem_ctx, io->out.sid);
+ if (io->out.sidstr == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ }
+
+success:
+ io->out.error_string = talloc_strdup(mem_ctx, "Success");
+done:
+ talloc_free(c);
+ return status;
+}
+
+
+NTSTATUS libnet_LookupName(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_LookupName *io)
+{
+ struct composite_context *c;
+
+ c = libnet_LookupName_send(ctx, mem_ctx, io, NULL);
+ return libnet_LookupName_recv(c, mem_ctx, io);
+}
diff --git a/source4/libnet/libnet_lookup.h b/source4/libnet/libnet_lookup.h
new file mode 100644
index 0000000..189ae58
--- /dev/null
+++ b/source4/libnet/libnet_lookup.h
@@ -0,0 +1,69 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Rafal Szczesniak 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 libnet_Lookup {
+ struct {
+ const char *hostname;
+ int type;
+ struct resolve_context *resolve_ctx;
+ } in;
+ struct {
+ const char **address;
+ } out;
+};
+
+
+struct libnet_LookupDCs {
+ struct {
+ const char *domain_name;
+ int name_type;
+ } in;
+ struct {
+ int num_dcs;
+ struct nbt_dc_name *dcs;
+ } out;
+};
+
+
+struct libnet_LookupName {
+ struct {
+ const char *name;
+ const char *domain_name;
+ } in;
+ struct {
+ struct dom_sid *sid;
+ int rid;
+ enum lsa_SidType sid_type;
+ const char *sidstr;
+ const char *error_string;
+ } out;
+};
+
+
+/*
+ * Monitor messages sent from libnet_lookup.c functions
+ */
+
+struct msg_net_lookup_dc {
+ const char *domain_name;
+ const char *hostname;
+ const char *address;
+};
+
diff --git a/source4/libnet/libnet_passwd.c b/source4/libnet/libnet_passwd.c
new file mode 100644
index 0000000..d7e9400
--- /dev/null
+++ b/source4/libnet/libnet_passwd.c
@@ -0,0 +1,1112 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Andrew Bartlett <abartlet@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 "includes.h"
+#include "libnet/libnet.h"
+#include "libcli/auth/libcli_auth.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "source4/librpc/rpc/dcerpc.h"
+#include "auth/credentials/credentials.h"
+#include "libcli/smb/smb_constants.h"
+#include "librpc/rpc/dcerpc_samr.h"
+#include "source3/rpc_client/init_samr.h"
+#include "lib/param/loadparm.h"
+#include "lib/param/param.h"
+
+#include "lib/crypto/gnutls_helpers.h"
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+static NTSTATUS libnet_ChangePassword_samr_aes(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct lsa_String *server,
+ struct lsa_String *account,
+ const char *old_password,
+ const char *new_password,
+ const char **error_string)
+{
+#ifdef HAVE_GNUTLS_PBKDF2
+ struct samr_ChangePasswordUser4 r;
+ uint8_t old_nt_key_data[16] = {0};
+ gnutls_datum_t old_nt_key = {
+ .data = old_nt_key_data,
+ .size = sizeof(old_nt_key_data),
+ };
+ uint8_t cek_data[16] = {0};
+ DATA_BLOB cek = {
+ .data = cek_data,
+ .length = sizeof(cek_data),
+ };
+ struct samr_EncryptedPasswordAES pwd_buf = {
+ .cipher_len = 0
+ };
+ DATA_BLOB salt = {
+ .data = pwd_buf.salt,
+ .length = sizeof(pwd_buf.salt),
+ };
+ gnutls_datum_t salt_datum = {
+ .data = pwd_buf.salt,
+ .size = sizeof(pwd_buf.salt),
+ };
+ uint64_t pbkdf2_iterations = generate_random_u64_range(5000, 1000000);
+ NTSTATUS status;
+ int rc;
+
+ E_md4hash(old_password, old_nt_key_data);
+
+ generate_nonce_buffer(salt.data, salt.length);
+
+ rc = gnutls_pbkdf2(GNUTLS_MAC_SHA512,
+ &old_nt_key,
+ &salt_datum,
+ pbkdf2_iterations,
+ cek.data,
+ cek.length);
+ BURN_DATA(old_nt_key_data);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ }
+
+ status = init_samr_CryptPasswordAES(mem_ctx,
+ new_password,
+ &salt,
+ &cek,
+ &pwd_buf);
+ data_blob_clear(&cek);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ pwd_buf.PBKDF2Iterations = pbkdf2_iterations;
+
+ r.in.server = server;
+ r.in.account = account;
+ r.in.password = &pwd_buf;
+
+ status = dcerpc_samr_ChangePasswordUser4_r(h, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ status = r.out.result;
+ *error_string = talloc_asprintf(mem_ctx,
+ "samr_ChangePasswordUser4 for "
+ "'%s\\%s' failed: %s",
+ server->string,
+ account->string,
+ nt_errstr(status));
+ goto done;
+ }
+
+done:
+ BURN_DATA(pwd_buf);
+
+ return status;
+#else /* HAVE_GNUTLS_PBKDF2 */
+ return NT_STATUS_NOT_IMPLEMENTED;
+#endif /* HAVE_GNUTLS_PBKDF2 */
+}
+
+static NTSTATUS libnet_ChangePassword_samr_rc4(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct lsa_String *server,
+ struct lsa_String *account,
+ const char *old_password,
+ const char *new_password,
+ const char **error_string)
+{
+ struct samr_OemChangePasswordUser2 oe2;
+ struct samr_ChangePasswordUser2 pw2;
+ struct samr_ChangePasswordUser3 pw3;
+ struct samr_CryptPassword nt_pass, lm_pass;
+ uint8_t old_nt_hash[16], new_nt_hash[16];
+ uint8_t old_lm_hash[16], new_lm_hash[16];
+ struct samr_Password nt_verifier, lm_verifier;
+ struct lsa_AsciiString a_server, a_account;
+ gnutls_cipher_hd_t cipher_hnd = NULL;
+ gnutls_datum_t nt_session_key = {
+ .data = old_nt_hash,
+ .size = sizeof(old_nt_hash),
+ };
+ gnutls_datum_t lm_session_key = {
+ .data = old_lm_hash,
+ .size = sizeof(old_lm_hash),
+ };
+ struct samr_DomInfo1 *dominfo = NULL;
+ struct userPwdChangeFailureInformation *reject = NULL;
+ NTSTATUS status;
+ int rc;
+
+ E_md4hash(old_password, old_nt_hash);
+ E_md4hash(new_password, new_nt_hash);
+
+ E_deshash(old_password, old_lm_hash);
+ E_deshash(new_password, new_lm_hash);
+
+ /* prepare samr_ChangePasswordUser3 */
+ encode_pw_buffer(lm_pass.data, new_password, STR_UNICODE);
+
+ rc = gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &nt_session_key,
+ NULL);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto done;
+ }
+
+ rc = gnutls_cipher_encrypt(cipher_hnd,
+ lm_pass.data,
+ 516);
+ gnutls_cipher_deinit(cipher_hnd);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto done;
+ }
+
+ rc = E_old_pw_hash(new_lm_hash, old_lm_hash, lm_verifier.hash);
+ if (rc != 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ goto done;
+ }
+
+ encode_pw_buffer(nt_pass.data, new_password, STR_UNICODE);
+
+ rc = gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &nt_session_key,
+ NULL);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto done;
+ }
+
+ rc = gnutls_cipher_encrypt(cipher_hnd,
+ nt_pass.data,
+ 516);
+ gnutls_cipher_deinit(cipher_hnd);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto done;
+ }
+
+ rc = E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash);
+ if (rc != 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ goto done;
+ }
+
+ pw3.in.server = server;
+ pw3.in.account = account;
+ pw3.in.nt_password = &nt_pass;
+ pw3.in.nt_verifier = &nt_verifier;
+ pw3.in.lm_change = 1;
+ pw3.in.lm_password = &lm_pass;
+ pw3.in.lm_verifier = &lm_verifier;
+ pw3.in.password3 = NULL;
+ pw3.out.dominfo = &dominfo;
+ pw3.out.reject = &reject;
+
+ /* 2. try samr_ChangePasswordUser3 */
+ status = dcerpc_samr_ChangePasswordUser3_r(h, mem_ctx, &pw3);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(pw3.out.result)) {
+ status = pw3.out.result;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ *error_string = talloc_asprintf(
+ mem_ctx,
+ "samr_ChangePasswordUser3 failed: %s",
+ nt_errstr(status));
+ *error_string =
+ talloc_asprintf(mem_ctx,
+ "samr_ChangePasswordUser3 for "
+ "'%s\\%s' failed: %s",
+ server->string,
+ account->string,
+ nt_errstr(status));
+ }
+ goto done;
+ }
+
+ /* prepare samr_ChangePasswordUser2 */
+ encode_pw_buffer(lm_pass.data, new_password, STR_ASCII | STR_TERMINATE);
+
+ rc = gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &lm_session_key,
+ NULL);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto done;
+ }
+
+ rc = gnutls_cipher_encrypt(cipher_hnd,
+ lm_pass.data,
+ 516);
+ gnutls_cipher_deinit(cipher_hnd);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto done;
+ }
+
+ rc = E_old_pw_hash(new_lm_hash, old_lm_hash, lm_verifier.hash);
+ if (rc != 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ goto done;
+ }
+
+ encode_pw_buffer(nt_pass.data, new_password, STR_UNICODE);
+
+ rc = gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &nt_session_key,
+ NULL);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto done;
+ }
+ rc = gnutls_cipher_encrypt(cipher_hnd,
+ nt_pass.data,
+ 516);
+ gnutls_cipher_deinit(cipher_hnd);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto done;
+ }
+
+ rc = E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash);
+ if (rc != 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ goto done;
+ }
+
+ pw2.in.server = server;
+ pw2.in.account = account;
+ pw2.in.nt_password = &nt_pass;
+ pw2.in.nt_verifier = &nt_verifier;
+ pw2.in.lm_change = 1;
+ pw2.in.lm_password = &lm_pass;
+ pw2.in.lm_verifier = &lm_verifier;
+
+ /* 3. try samr_ChangePasswordUser2 */
+ status = dcerpc_samr_ChangePasswordUser2_r(h, mem_ctx, &pw2);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(pw2.out.result)) {
+ status = pw2.out.result;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ *error_string =
+ talloc_asprintf(mem_ctx,
+ "samr_ChangePasswordUser2 for "
+ "'%s\\%s' failed: %s",
+ server->string,
+ account->string,
+ nt_errstr(status));
+ }
+ goto done;
+ }
+
+
+ /* prepare samr_OemChangePasswordUser2 */
+ a_server.string = server->string;
+ a_account.string = account->string;
+
+ encode_pw_buffer(lm_pass.data, new_password, STR_ASCII);
+
+ rc = gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &lm_session_key,
+ NULL);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto done;
+ }
+
+ rc = gnutls_cipher_encrypt(cipher_hnd,
+ lm_pass.data,
+ 516);
+ gnutls_cipher_deinit(cipher_hnd);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto done;
+ }
+
+ rc = E_old_pw_hash(new_lm_hash, old_lm_hash, lm_verifier.hash);
+ if (rc != 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ goto done;
+ }
+
+ oe2.in.server = &a_server;
+ oe2.in.account = &a_account;
+ oe2.in.password = &lm_pass;
+ oe2.in.hash = &lm_verifier;
+
+ /* 4. try samr_OemChangePasswordUser2 */
+ status = dcerpc_samr_OemChangePasswordUser2_r(h, mem_ctx, &oe2);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(oe2.out.result)) {
+ status = oe2.out.result;
+ }
+ if (!NT_STATUS_IS_OK(oe2.out.result)) {
+ *error_string =
+ talloc_asprintf(mem_ctx,
+ "samr_OemChangePasswordUser2 "
+ "for '%s\\%s' failed: %s",
+ server->string,
+ account->string,
+ nt_errstr(status));
+ }
+ goto done;
+ }
+
+ status = NT_STATUS_OK;
+done:
+ return status;
+}
+
+/*
+ * do a password change using DCERPC/SAMR calls
+ * 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation)
+ * 2. try samr_ChangePasswordUser3
+ * 3. try samr_ChangePasswordUser2
+ * 4. try samr_OemChangePasswordUser2
+ */
+static NTSTATUS libnet_ChangePassword_samr(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_ChangePassword *r)
+{
+ NTSTATUS status;
+ struct libnet_RpcConnect c;
+ struct lsa_String server, account;
+
+ ZERO_STRUCT(c);
+
+ /* prepare connect to the SAMR pipe of the users domain PDC */
+ c.level = LIBNET_RPC_CONNECT_PDC;
+ c.in.name = r->samr.in.domain_name;
+ c.in.dcerpc_iface = &ndr_table_samr;
+ c.in.dcerpc_flags = DCERPC_ANON_FALLBACK;
+
+ /* 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation) */
+ status = libnet_RpcConnect(ctx, mem_ctx, &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr.out.error_string = talloc_asprintf(mem_ctx,
+ "Connection to SAMR pipe of PDC of domain '%s' failed: %s",
+ r->samr.in.domain_name, nt_errstr(status));
+ return status;
+ }
+
+ /* prepare password change for account */
+ server.string = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(c.out.dcerpc_pipe));
+ account.string = r->samr.in.account_name;
+
+ status = libnet_ChangePassword_samr_aes(
+ mem_ctx,
+ c.out.dcerpc_pipe->binding_handle,
+ &server,
+ &account,
+ r->samr.in.oldpassword,
+ r->samr.in.newpassword,
+ &(r->samr.out.error_string));
+ if (NT_STATUS_IS_OK(status)) {
+ goto disconnect;
+ } else if (NT_STATUS_EQUAL(status,
+ NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
+ /*
+ * Don't fallback to RC4 based SAMR if weak crypto is not
+ * allowed.
+ */
+ if (lpcfg_weak_crypto(ctx->lp_ctx) ==
+ SAMBA_WEAK_CRYPTO_DISALLOWED) {
+ goto disconnect;
+ }
+ } else {
+ /* libnet_ChangePassword_samr_aes is implemented and failed */
+ goto disconnect;
+ }
+
+ status = libnet_ChangePassword_samr_rc4(
+ mem_ctx,
+ c.out.dcerpc_pipe->binding_handle,
+ &server,
+ &account,
+ r->samr.in.oldpassword,
+ r->samr.in.newpassword,
+ &(r->samr.out.error_string));
+ if (!NT_STATUS_IS_OK(status)) {
+ goto disconnect;
+ }
+
+disconnect:
+ /* close connection */
+ talloc_unlink(ctx, c.out.dcerpc_pipe);
+
+ return status;
+}
+
+static NTSTATUS libnet_ChangePassword_generic(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_ChangePassword *r)
+{
+ NTSTATUS status;
+ union libnet_ChangePassword r2;
+
+ r2.samr.level = LIBNET_CHANGE_PASSWORD_SAMR;
+ r2.samr.in.account_name = r->generic.in.account_name;
+ r2.samr.in.domain_name = r->generic.in.domain_name;
+ r2.samr.in.oldpassword = r->generic.in.oldpassword;
+ r2.samr.in.newpassword = r->generic.in.newpassword;
+
+ status = libnet_ChangePassword(ctx, mem_ctx, &r2);
+
+ r->generic.out.error_string = r2.samr.out.error_string;
+
+ return status;
+}
+
+NTSTATUS libnet_ChangePassword(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_ChangePassword *r)
+{
+ switch (r->generic.level) {
+ case LIBNET_CHANGE_PASSWORD_GENERIC:
+ return libnet_ChangePassword_generic(ctx, mem_ctx, r);
+ case LIBNET_CHANGE_PASSWORD_SAMR:
+ return libnet_ChangePassword_samr(ctx, mem_ctx, r);
+ case LIBNET_CHANGE_PASSWORD_KRB5:
+ return NT_STATUS_NOT_IMPLEMENTED;
+ case LIBNET_CHANGE_PASSWORD_LDAP:
+ return NT_STATUS_NOT_IMPLEMENTED;
+ case LIBNET_CHANGE_PASSWORD_RAP:
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+static NTSTATUS libnet_SetPassword_samr_handle_26(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
+{
+ NTSTATUS status;
+ struct samr_SetUserInfo2 sui;
+ union samr_UserInfo u_info;
+ DATA_BLOB session_key;
+
+ if (r->samr_handle.in.info21) {
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ /* prepare samr_SetUserInfo2 level 26 */
+ ZERO_STRUCT(u_info);
+ u_info.info26.password_expired = 0;
+
+ status = dcerpc_fetch_session_key(r->samr_handle.in.dcerpc_pipe, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr_handle.out.error_string = talloc_asprintf(mem_ctx,
+ "dcerpc_fetch_session_key failed: %s",
+ nt_errstr(status));
+ return status;
+ }
+
+ status = encode_rc4_passwd_buffer(r->samr_handle.in.newpassword,
+ &session_key,
+ &u_info.info26.password);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr_handle.out.error_string =
+ talloc_asprintf(mem_ctx,
+ "encode_rc4_passwd_buffer failed: %s",
+ nt_errstr(status));
+ return status;
+ }
+
+ sui.in.user_handle = r->samr_handle.in.user_handle;
+ sui.in.info = &u_info;
+ sui.in.level = 26;
+
+ /* 7. try samr_SetUserInfo2 level 26 to set the password */
+ status = dcerpc_samr_SetUserInfo2_r(r->samr_handle.in.dcerpc_pipe->binding_handle, mem_ctx, &sui);
+ /* check result of samr_SetUserInfo2 level 26 */
+ if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(sui.out.result)) {
+ status = sui.out.result;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr_handle.out.error_string
+ = talloc_asprintf(mem_ctx,
+ "SetUserInfo2 level 26 for [%s] failed: %s",
+ r->samr_handle.in.account_name, nt_errstr(status));
+ }
+
+ return status;
+}
+
+static NTSTATUS libnet_SetPassword_samr_handle_25(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
+{
+ NTSTATUS status;
+ struct samr_SetUserInfo2 sui;
+ union samr_UserInfo u_info;
+ DATA_BLOB session_key;
+
+ if (!r->samr_handle.in.info21) {
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ /* prepare samr_SetUserInfo2 level 25 */
+ ZERO_STRUCT(u_info);
+ u_info.info25.info = *r->samr_handle.in.info21;
+ u_info.info25.info.fields_present |= SAMR_FIELD_NT_PASSWORD_PRESENT;
+
+ status = dcerpc_fetch_session_key(r->samr_handle.in.dcerpc_pipe, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr_handle.out.error_string = talloc_asprintf(mem_ctx,
+ "dcerpc_fetch_session_key failed: %s",
+ nt_errstr(status));
+ return status;
+ }
+
+ status = encode_rc4_passwd_buffer(r->samr_handle.in.newpassword,
+ &session_key,
+ &u_info.info25.password);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr_handle.out.error_string =
+ talloc_asprintf(mem_ctx,
+ "encode_rc4_passwd_buffer failed: %s",
+ nt_errstr(status));
+ return status;
+ }
+
+
+ sui.in.user_handle = r->samr_handle.in.user_handle;
+ sui.in.info = &u_info;
+ sui.in.level = 25;
+
+ /* 8. try samr_SetUserInfo2 level 25 to set the password */
+ status = dcerpc_samr_SetUserInfo2_r(r->samr_handle.in.dcerpc_pipe->binding_handle, mem_ctx, &sui);
+ if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(sui.out.result)) {
+ status = sui.out.result;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr_handle.out.error_string
+ = talloc_asprintf(mem_ctx,
+ "SetUserInfo2 level 25 for [%s] failed: %s",
+ r->samr_handle.in.account_name, nt_errstr(status));
+ }
+
+ return status;
+}
+
+static NTSTATUS libnet_SetPassword_samr_handle_24(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
+{
+ NTSTATUS status;
+ struct samr_SetUserInfo2 sui;
+ union samr_UserInfo u_info;
+ DATA_BLOB session_key;
+ gnutls_cipher_hd_t cipher_hnd = NULL;
+ gnutls_datum_t enc_session_key;
+ int rc;
+
+ if (r->samr_handle.in.info21) {
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ /* prepare samr_SetUserInfo2 level 24 */
+ ZERO_STRUCT(u_info);
+ encode_pw_buffer(u_info.info24.password.data, r->samr_handle.in.newpassword, STR_UNICODE);
+ u_info.info24.password_expired = 0;
+
+ status = dcerpc_fetch_session_key(r->samr_handle.in.dcerpc_pipe, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr_handle.out.error_string = talloc_asprintf(mem_ctx,
+ "dcerpc_fetch_session_key failed: %s",
+ nt_errstr(status));
+ return status;
+ }
+
+ enc_session_key = (gnutls_datum_t) {
+ .data = session_key.data,
+ .size = session_key.length,
+ };
+
+ rc = gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &enc_session_key,
+ NULL);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto out;
+ }
+
+ rc = gnutls_cipher_encrypt(cipher_hnd,
+ u_info.info24.password.data,
+ 516);
+ gnutls_cipher_deinit(cipher_hnd);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto out;
+ }
+
+ sui.in.user_handle = r->samr_handle.in.user_handle;
+ sui.in.info = &u_info;
+ sui.in.level = 24;
+
+ /* 9. try samr_SetUserInfo2 level 24 to set the password */
+ status = dcerpc_samr_SetUserInfo2_r(r->samr_handle.in.dcerpc_pipe->binding_handle, mem_ctx, &sui);
+ if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(sui.out.result)) {
+ status = sui.out.result;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr_handle.out.error_string
+ = talloc_asprintf(mem_ctx,
+ "SetUserInfo2 level 24 for [%s] failed: %s",
+ r->samr_handle.in.account_name, nt_errstr(status));
+ }
+
+out:
+ data_blob_clear(&session_key);
+ return status;
+}
+
+static NTSTATUS libnet_SetPassword_samr_handle_23(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
+{
+ NTSTATUS status;
+ struct samr_SetUserInfo2 sui;
+ union samr_UserInfo u_info;
+ DATA_BLOB session_key;
+ gnutls_cipher_hd_t cipher_hnd = NULL;
+ gnutls_datum_t _session_key;
+ int rc;
+
+ if (!r->samr_handle.in.info21) {
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ /* prepare samr_SetUserInfo2 level 23 */
+ ZERO_STRUCT(u_info);
+ u_info.info23.info = *r->samr_handle.in.info21;
+ u_info.info23.info.fields_present |= SAMR_FIELD_NT_PASSWORD_PRESENT;
+ encode_pw_buffer(u_info.info23.password.data, r->samr_handle.in.newpassword, STR_UNICODE);
+
+ status = dcerpc_fetch_session_key(r->samr_handle.in.dcerpc_pipe, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr_handle.out.error_string
+ = talloc_asprintf(mem_ctx,
+ "dcerpc_fetch_session_key failed: %s",
+ nt_errstr(status));
+ return status;
+ }
+
+ _session_key = (gnutls_datum_t) {
+ .data = session_key.data,
+ .size = session_key.length,
+ };
+
+ rc = gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &_session_key,
+ NULL);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto out;
+ }
+
+ rc = gnutls_cipher_encrypt(cipher_hnd,
+ u_info.info23.password.data,
+ 516);
+ data_blob_clear_free(&session_key);
+ gnutls_cipher_deinit(cipher_hnd);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto out;
+ }
+
+ sui.in.user_handle = r->samr_handle.in.user_handle;
+ sui.in.info = &u_info;
+ sui.in.level = 23;
+
+ /* 10. try samr_SetUserInfo2 level 23 to set the password */
+ status = dcerpc_samr_SetUserInfo2_r(r->samr_handle.in.dcerpc_pipe->binding_handle, mem_ctx, &sui);
+ if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(sui.out.result)) {
+ status = sui.out.result;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr_handle.out.error_string
+ = talloc_asprintf(mem_ctx,
+ "SetUserInfo2 level 23 for [%s] failed: %s",
+ r->samr_handle.in.account_name, nt_errstr(status));
+ }
+
+out:
+ return status;
+}
+
+static NTSTATUS libnet_SetPassword_samr_handle_18(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
+{
+ NTSTATUS status;
+ struct samr_SetUserInfo2 sui;
+ union samr_UserInfo u_info;
+ struct samr_Password ntpwd;
+ DATA_BLOB ntpwd_in;
+ DATA_BLOB ntpwd_out;
+ DATA_BLOB session_key;
+ int rc;
+
+ if (r->samr_handle.in.info21) {
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ /* prepare samr_SetUserInfo2 level 18 (nt_hash) */
+ ZERO_STRUCT(u_info);
+ E_md4hash(r->samr_handle.in.newpassword, ntpwd.hash);
+ ntpwd_in = data_blob_const(ntpwd.hash, sizeof(ntpwd.hash));
+ ntpwd_out = data_blob_const(u_info.info18.nt_pwd.hash,
+ sizeof(u_info.info18.nt_pwd.hash));
+ u_info.info18.nt_pwd_active = 1;
+ u_info.info18.password_expired = 0;
+
+ status = dcerpc_fetch_session_key(r->samr_handle.in.dcerpc_pipe, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr_handle.out.error_string = talloc_asprintf(mem_ctx,
+ "dcerpc_fetch_session_key failed: %s",
+ nt_errstr(status));
+ return status;
+ }
+
+ rc = sess_crypt_blob(&ntpwd_out, &ntpwd_in,
+ &session_key, SAMBA_GNUTLS_ENCRYPT);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto out;
+ }
+
+ sui.in.user_handle = r->samr_handle.in.user_handle;
+ sui.in.info = &u_info;
+ sui.in.level = 18;
+
+ /* 9. try samr_SetUserInfo2 level 18 to set the password */
+ status = dcerpc_samr_SetUserInfo2_r(r->samr_handle.in.dcerpc_pipe->binding_handle, mem_ctx, &sui);
+ if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(sui.out.result)) {
+ status = sui.out.result;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr_handle.out.error_string
+ = talloc_asprintf(mem_ctx,
+ "SetUserInfo2 level 18 for [%s] failed: %s",
+ r->samr_handle.in.account_name, nt_errstr(status));
+ }
+
+out:
+ data_blob_clear(&session_key);
+ return status;
+}
+
+/*
+ * 1. try samr_SetUserInfo2 level 26 to set the password
+ * 2. try samr_SetUserInfo2 level 25 to set the password
+ * 3. try samr_SetUserInfo2 level 24 to set the password
+ * 4. try samr_SetUserInfo2 level 23 to set the password
+*/
+static NTSTATUS libnet_SetPassword_samr_handle(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
+{
+
+ NTSTATUS status;
+ enum libnet_SetPassword_level levels[] = {
+ LIBNET_SET_PASSWORD_SAMR_HANDLE_26,
+ LIBNET_SET_PASSWORD_SAMR_HANDLE_25,
+ LIBNET_SET_PASSWORD_SAMR_HANDLE_24,
+ LIBNET_SET_PASSWORD_SAMR_HANDLE_23,
+ };
+ unsigned int i;
+
+ if (r->samr_handle.samr_level != 0) {
+ r->generic.level = r->samr_handle.samr_level;
+ return libnet_SetPassword(ctx, mem_ctx, r);
+ }
+
+ for (i=0; i < ARRAY_SIZE(levels); i++) {
+ r->generic.level = levels[i];
+ status = libnet_SetPassword(ctx, mem_ctx, r);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)
+ || NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER_MIX)
+ || NT_STATUS_EQUAL(status, NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE)) {
+ /* Try another password set mechanism */
+ continue;
+ }
+ break;
+ }
+
+ return status;
+}
+/*
+ * set a password with DCERPC/SAMR calls
+ * 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation)
+ * is it correct to contact the the pdc of the domain of the user who's password should be set?
+ * 2. do a samr_Connect to get a policy handle
+ * 3. do a samr_LookupDomain to get the domain sid
+ * 4. do a samr_OpenDomain to get a domain handle
+ * 5. do a samr_LookupNames to get the users rid
+ * 6. do a samr_OpenUser to get a user handle
+ * 7 call libnet_SetPassword_samr_handle to set the password
+ */
+static NTSTATUS libnet_SetPassword_samr(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
+{
+ NTSTATUS status;
+ struct libnet_RpcConnect c;
+ struct samr_Connect sc;
+ struct policy_handle p_handle;
+ struct samr_LookupDomain ld;
+ struct dom_sid2 *sid = NULL;
+ struct lsa_String d_name;
+ struct samr_OpenDomain od;
+ struct policy_handle d_handle;
+ struct samr_LookupNames ln;
+ struct samr_Ids rids, types;
+ struct samr_OpenUser ou;
+ struct policy_handle u_handle;
+ union libnet_SetPassword r2;
+
+ ZERO_STRUCT(c);
+ /* prepare connect to the SAMR pipe of users domain PDC */
+ c.level = LIBNET_RPC_CONNECT_PDC;
+ c.in.name = r->samr.in.domain_name;
+ c.in.dcerpc_iface = &ndr_table_samr;
+
+ /* 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation) */
+ status = libnet_RpcConnect(ctx, mem_ctx, &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr.out.error_string = talloc_asprintf(mem_ctx,
+ "Connection to SAMR pipe of PDC of domain '%s' failed: %s",
+ r->samr.in.domain_name, nt_errstr(status));
+ return status;
+ }
+
+ /* prepare samr_Connect */
+ ZERO_STRUCT(p_handle);
+ sc.in.system_name = NULL;
+ sc.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ sc.out.connect_handle = &p_handle;
+
+ /* 2. do a samr_Connect to get a policy handle */
+ status = dcerpc_samr_Connect_r(c.out.dcerpc_pipe->binding_handle, mem_ctx, &sc);
+ if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(sc.out.result)) {
+ status = sc.out.result;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr.out.error_string = talloc_asprintf(mem_ctx,
+ "samr_Connect failed: %s",
+ nt_errstr(status));
+ goto disconnect;
+ }
+
+ /* prepare samr_LookupDomain */
+ d_name.string = r->samr.in.domain_name;
+ ld.in.connect_handle = &p_handle;
+ ld.in.domain_name = &d_name;
+ ld.out.sid = &sid;
+
+ /* 3. do a samr_LookupDomain to get the domain sid */
+ status = dcerpc_samr_LookupDomain_r(c.out.dcerpc_pipe->binding_handle, mem_ctx, &ld);
+ if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(ld.out.result)) {
+ status = ld.out.result;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr.out.error_string = talloc_asprintf(mem_ctx,
+ "samr_LookupDomain for [%s] failed: %s",
+ r->samr.in.domain_name, nt_errstr(status));
+ goto disconnect;
+ }
+
+ /* prepare samr_OpenDomain */
+ ZERO_STRUCT(d_handle);
+ od.in.connect_handle = &p_handle;
+ od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ od.in.sid = *ld.out.sid;
+ od.out.domain_handle = &d_handle;
+
+ /* 4. do a samr_OpenDomain to get a domain handle */
+ status = dcerpc_samr_OpenDomain_r(c.out.dcerpc_pipe->binding_handle, mem_ctx, &od);
+ if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(od.out.result)) {
+ status = od.out.result;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr.out.error_string = talloc_asprintf(mem_ctx,
+ "samr_OpenDomain for [%s] failed: %s",
+ r->samr.in.domain_name, nt_errstr(status));
+ goto disconnect;
+ }
+
+ /* prepare samr_LookupNames */
+ ln.in.domain_handle = &d_handle;
+ ln.in.num_names = 1;
+ ln.in.names = talloc_array(mem_ctx, struct lsa_String, 1);
+ ln.out.rids = &rids;
+ ln.out.types = &types;
+ if (!ln.in.names) {
+ r->samr.out.error_string = "Out of Memory";
+ return NT_STATUS_NO_MEMORY;
+ }
+ ln.in.names[0].string = r->samr.in.account_name;
+
+ /* 5. do a samr_LookupNames to get the users rid */
+ status = dcerpc_samr_LookupNames_r(c.out.dcerpc_pipe->binding_handle, mem_ctx, &ln);
+ if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(ln.out.result)) {
+ status = ln.out.result;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr.out.error_string = talloc_asprintf(mem_ctx,
+ "samr_LookupNames for [%s] failed: %s",
+ r->samr.in.account_name, nt_errstr(status));
+ goto disconnect;
+ }
+
+ /* check if we got one RID for the user */
+ if (ln.out.rids->count != 1) {
+ r->samr.out.error_string = talloc_asprintf(mem_ctx,
+ "samr_LookupNames for [%s] returns %d RIDs",
+ r->samr.in.account_name, ln.out.rids->count);
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto disconnect;
+ }
+
+ if (ln.out.types->count != 1) {
+ r->samr.out.error_string = talloc_asprintf(mem_ctx,
+ "samr_LookupNames for [%s] returns %d RID TYPEs",
+ r->samr.in.account_name, ln.out.types->count);
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto disconnect;
+ }
+
+ /* prepare samr_OpenUser */
+ ZERO_STRUCT(u_handle);
+ ou.in.domain_handle = &d_handle;
+ ou.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ ou.in.rid = ln.out.rids->ids[0];
+ ou.out.user_handle = &u_handle;
+
+ /* 6. do a samr_OpenUser to get a user handle */
+ status = dcerpc_samr_OpenUser_r(c.out.dcerpc_pipe->binding_handle, mem_ctx, &ou);
+ if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(ou.out.result)) {
+ status = ou.out.result;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr.out.error_string = talloc_asprintf(mem_ctx,
+ "samr_OpenUser for [%s] failed: %s",
+ r->samr.in.account_name, nt_errstr(status));
+ goto disconnect;
+ }
+
+ ZERO_STRUCT(r2);
+ r2.samr_handle.level = LIBNET_SET_PASSWORD_SAMR_HANDLE;
+ r2.samr_handle.samr_level = r->samr.samr_level;
+ r2.samr_handle.in.account_name = r->samr.in.account_name;
+ r2.samr_handle.in.newpassword = r->samr.in.newpassword;
+ r2.samr_handle.in.user_handle = &u_handle;
+ r2.samr_handle.in.dcerpc_pipe = c.out.dcerpc_pipe;
+ r2.samr_handle.in.info21 = NULL;
+
+ status = libnet_SetPassword(ctx, mem_ctx, &r2);
+
+ r->generic.out.error_string = r2.samr_handle.out.error_string;
+
+disconnect:
+ /* close connection */
+ talloc_unlink(ctx, c.out.dcerpc_pipe);
+
+ return status;
+}
+
+static NTSTATUS libnet_SetPassword_generic(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
+{
+ NTSTATUS status;
+ union libnet_SetPassword r2;
+
+ ZERO_STRUCT(r2);
+ r2.samr.level = LIBNET_SET_PASSWORD_SAMR;
+ r2.samr.samr_level = r->generic.samr_level;
+ r2.samr.in.account_name = r->generic.in.account_name;
+ r2.samr.in.domain_name = r->generic.in.domain_name;
+ r2.samr.in.newpassword = r->generic.in.newpassword;
+
+ r->generic.out.error_string = "Unknown Error";
+ status = libnet_SetPassword(ctx, mem_ctx, &r2);
+
+ r->generic.out.error_string = r2.samr.out.error_string;
+
+ return status;
+}
+
+NTSTATUS libnet_SetPassword(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
+{
+ enum smb_encryption_setting encryption_state =
+ cli_credentials_get_smb_encryption(ctx->cred);
+ NTSTATUS status = NT_STATUS_INVALID_LEVEL;
+
+ switch (r->generic.level) {
+ case LIBNET_SET_PASSWORD_GENERIC:
+ status = libnet_SetPassword_generic(ctx, mem_ctx, r);
+ break;
+ case LIBNET_SET_PASSWORD_SAMR:
+ status = libnet_SetPassword_samr(ctx, mem_ctx, r);
+ break;
+ case LIBNET_SET_PASSWORD_SAMR_HANDLE:
+ status = libnet_SetPassword_samr_handle(ctx, mem_ctx, r);
+ break;
+ case LIBNET_SET_PASSWORD_SAMR_HANDLE_26:
+ if (encryption_state == SMB_ENCRYPTION_REQUIRED) {
+ GNUTLS_FIPS140_SET_LAX_MODE();
+ }
+ status = libnet_SetPassword_samr_handle_26(ctx, mem_ctx, r);
+ break;
+ case LIBNET_SET_PASSWORD_SAMR_HANDLE_25:
+ if (encryption_state == SMB_ENCRYPTION_REQUIRED) {
+ GNUTLS_FIPS140_SET_LAX_MODE();
+ }
+ status = libnet_SetPassword_samr_handle_25(ctx, mem_ctx, r);
+ break;
+ case LIBNET_SET_PASSWORD_SAMR_HANDLE_24:
+ if (encryption_state == SMB_ENCRYPTION_REQUIRED) {
+ GNUTLS_FIPS140_SET_LAX_MODE();
+ }
+ status = libnet_SetPassword_samr_handle_24(ctx, mem_ctx, r);
+ break;
+ case LIBNET_SET_PASSWORD_SAMR_HANDLE_23:
+ if (encryption_state == SMB_ENCRYPTION_REQUIRED) {
+ GNUTLS_FIPS140_SET_LAX_MODE();
+ }
+ status = libnet_SetPassword_samr_handle_23(ctx, mem_ctx, r);
+ break;
+ case LIBNET_SET_PASSWORD_SAMR_HANDLE_18:
+ if (encryption_state == SMB_ENCRYPTION_REQUIRED) {
+ GNUTLS_FIPS140_SET_LAX_MODE();
+ }
+ status = libnet_SetPassword_samr_handle_18(ctx, mem_ctx, r);
+ break;
+ case LIBNET_SET_PASSWORD_KRB5:
+ status = NT_STATUS_NOT_IMPLEMENTED;
+ break;
+ case LIBNET_SET_PASSWORD_LDAP:
+ status = NT_STATUS_NOT_IMPLEMENTED;
+ break;
+ case LIBNET_SET_PASSWORD_RAP:
+ status = NT_STATUS_NOT_IMPLEMENTED;
+ break;
+ }
+
+ GNUTLS_FIPS140_SET_STRICT_MODE();
+ return status;
+}
diff --git a/source4/libnet/libnet_passwd.h b/source4/libnet/libnet_passwd.h
new file mode 100644
index 0000000..17e6aab
--- /dev/null
+++ b/source4/libnet/libnet_passwd.h
@@ -0,0 +1,144 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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/>.
+*/
+
+/* struct and enum for doing a remote password change */
+enum libnet_ChangePassword_level {
+ LIBNET_CHANGE_PASSWORD_GENERIC,
+ LIBNET_CHANGE_PASSWORD_SAMR,
+ LIBNET_CHANGE_PASSWORD_KRB5,
+ LIBNET_CHANGE_PASSWORD_LDAP,
+ LIBNET_CHANGE_PASSWORD_RAP
+};
+
+union libnet_ChangePassword {
+ struct {
+ enum libnet_ChangePassword_level level;
+
+ struct _libnet_ChangePassword_in {
+ const char *account_name;
+ const char *domain_name;
+ const char *oldpassword;
+ const char *newpassword;
+ } in;
+
+ struct _libnet_ChangePassword_out {
+ const char *error_string;
+ } out;
+ } generic;
+
+ struct {
+ enum libnet_ChangePassword_level level;
+ struct _libnet_ChangePassword_in in;
+ struct _libnet_ChangePassword_out out;
+ } samr;
+
+ struct {
+ enum libnet_ChangePassword_level level;
+ struct _libnet_ChangePassword_in in;
+ struct _libnet_ChangePassword_out out;
+ } krb5;
+
+ struct {
+ enum libnet_ChangePassword_level level;
+ struct _libnet_ChangePassword_in in;
+ struct _libnet_ChangePassword_out out;
+ } ldap;
+
+ struct {
+ enum libnet_ChangePassword_level level;
+ struct _libnet_ChangePassword_in in;
+ struct _libnet_ChangePassword_out out;
+ } rap;
+};
+
+/* struct and enum for doing a remote password set */
+enum libnet_SetPassword_level {
+ LIBNET_SET_PASSWORD_GENERIC,
+ LIBNET_SET_PASSWORD_SAMR,
+ LIBNET_SET_PASSWORD_SAMR_HANDLE,
+ LIBNET_SET_PASSWORD_SAMR_HANDLE_26,
+ LIBNET_SET_PASSWORD_SAMR_HANDLE_25,
+ LIBNET_SET_PASSWORD_SAMR_HANDLE_24,
+ LIBNET_SET_PASSWORD_SAMR_HANDLE_23,
+ LIBNET_SET_PASSWORD_SAMR_HANDLE_18,
+ LIBNET_SET_PASSWORD_KRB5,
+ LIBNET_SET_PASSWORD_LDAP,
+ LIBNET_SET_PASSWORD_RAP
+};
+
+union libnet_SetPassword {
+ struct {
+ enum libnet_SetPassword_level level;
+ enum libnet_SetPassword_level samr_level;
+
+ struct _libnet_SetPassword_in {
+ const char *account_name;
+ const char *domain_name;
+ const char *newpassword;
+ } in;
+
+ struct _libnet_SetPassword_out {
+ const char *error_string;
+ } out;
+ } generic;
+
+ struct {
+ enum libnet_SetPassword_level level;
+ enum libnet_SetPassword_level samr_level;
+ struct _libnet_SetPassword_samr_handle_in {
+ const char *account_name; /* for debug only */
+ struct policy_handle *user_handle;
+ struct dcerpc_pipe *dcerpc_pipe;
+ const char *newpassword;
+ struct samr_UserInfo21 *info21; /* can be NULL,
+ * for level 26,24 it must be NULL
+ * for level 25,23 it must be non-NULL
+ */
+ } in;
+ struct _libnet_SetPassword_out out;
+ } samr_handle;
+
+ struct {
+ enum libnet_SetPassword_level level;
+ enum libnet_SetPassword_level samr_level;
+ struct _libnet_SetPassword_in in;
+ struct _libnet_SetPassword_out out;
+ } samr;
+
+ struct {
+ enum libnet_SetPassword_level level;
+ enum libnet_SetPassword_level samr_level;
+ struct _libnet_SetPassword_in in;
+ struct _libnet_SetPassword_out out;
+ } krb5;
+
+ struct {
+ enum libnet_SetPassword_level level;
+ enum libnet_SetPassword_level samr_level;
+ struct _libnet_SetPassword_in in;
+ struct _libnet_SetPassword_out out;
+ } ldap;
+
+ struct {
+ enum libnet_ChangePassword_level level;
+ enum libnet_SetPassword_level samr_level;
+ struct _libnet_SetPassword_in in;
+ struct _libnet_SetPassword_out out;
+ } rap;
+};
diff --git a/source4/libnet/libnet_rpc.c b/source4/libnet/libnet_rpc.c
new file mode 100644
index 0000000..91c538f
--- /dev/null
+++ b/source4/libnet/libnet_rpc.c
@@ -0,0 +1,1037 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Rafal Szczesniak 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 "libnet/libnet.h"
+#include "libcli/libcli.h"
+#include "libcli/composite/composite.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "librpc/gen_ndr/ndr_samr.h"
+#include "auth/credentials/credentials.h"
+
+struct rpc_connect_srv_state {
+ struct libnet_context *ctx;
+ struct libnet_RpcConnect r;
+ const char *binding;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg*);
+};
+
+
+static void continue_pipe_connect(struct composite_context *ctx);
+
+
+/**
+ * Initiates connection to rpc pipe on remote server
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r data structure containing necessary parameters and return values
+ * @return composite context of this call
+ **/
+
+static struct composite_context* libnet_RpcConnectSrv_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_RpcConnect *r,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct rpc_connect_srv_state *s;
+ struct dcerpc_binding *b;
+ struct composite_context *pipe_connect_req;
+
+ /* composite context allocation and setup */
+ c = composite_create(ctx, ctx->event_ctx);
+ if (c == NULL) return c;
+
+ s = talloc_zero(c, struct rpc_connect_srv_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+ s->monitor_fn = monitor;
+
+ s->ctx = ctx;
+ s->r = *r;
+ ZERO_STRUCT(s->r.out);
+
+ /* prepare binding string */
+ switch (r->level) {
+ case LIBNET_RPC_CONNECT_SERVER:
+ s->binding = talloc_asprintf(s, "ncacn_np:%s", r->in.name);
+ break;
+ case LIBNET_RPC_CONNECT_SERVER_ADDRESS:
+ s->binding = talloc_asprintf(s, "ncacn_np:%s[target_hostname=%s]",
+ r->in.address, r->in.name);
+ break;
+
+ case LIBNET_RPC_CONNECT_BINDING:
+ s->binding = talloc_strdup(s, r->in.binding);
+ break;
+
+ case LIBNET_RPC_CONNECT_DC:
+ case LIBNET_RPC_CONNECT_PDC:
+ /* this should never happen - DC and PDC level has a separate
+ composite function */
+ case LIBNET_RPC_CONNECT_DC_INFO:
+ /* this should never happen - DC_INFO level has a separate
+ composite function */
+ composite_error(c, NT_STATUS_INVALID_LEVEL);
+ return c;
+ }
+
+ /* parse binding string to the structure */
+ c->status = dcerpc_parse_binding(c, s->binding, &b);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ DEBUG(0, ("Failed to parse dcerpc binding '%s'\n", s->binding));
+ composite_error(c, c->status);
+ return c;
+ }
+
+ switch (r->level) {
+ case LIBNET_RPC_CONNECT_SERVER:
+ case LIBNET_RPC_CONNECT_SERVER_ADDRESS:
+ c->status = dcerpc_binding_set_flags(b, r->in.dcerpc_flags, 0);
+ if (!composite_is_ok(c)) return c;
+ break;
+ default:
+ /* other types have already been checked before */
+ break;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ c->status = dcerpc_binding_set_flags(b, DCERPC_DEBUG_PRINT_BOTH, 0);
+ if (!composite_is_ok(c)) return c;
+ }
+
+ /* connect to remote dcerpc pipe */
+ pipe_connect_req = dcerpc_pipe_connect_b_send(c, b, r->in.dcerpc_iface,
+ ctx->cred, c->event_ctx,
+ ctx->lp_ctx);
+ if (composite_nomem(pipe_connect_req, c)) return c;
+
+ composite_continue(c, pipe_connect_req, continue_pipe_connect, c);
+ return c;
+}
+
+
+/*
+ Step 2 of RpcConnectSrv - get rpc connection
+*/
+static void continue_pipe_connect(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct rpc_connect_srv_state *s;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct rpc_connect_srv_state);
+
+ /* receive result of rpc pipe connection */
+ c->status = dcerpc_pipe_connect_b_recv(ctx, c, &s->r.out.dcerpc_pipe);
+
+ /* post monitor message */
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+ struct msg_net_rpc_connect data;
+ const struct dcerpc_binding *b = s->r.out.dcerpc_pipe->binding;
+
+ /* prepare monitor message and post it */
+ data.host = dcerpc_binding_get_string_option(b, "host");
+ data.endpoint = dcerpc_binding_get_string_option(b, "endpoint");
+ data.transport = dcerpc_binding_get_transport(b);
+ data.domain_name = dcerpc_binding_get_string_option(b, "target_hostname");
+
+ msg.type = mon_NetRpcConnect;
+ msg.data = (void*)&data;
+ msg.data_size = sizeof(data);
+ s->monitor_fn(&msg);
+ }
+
+ composite_done(c);
+}
+
+
+/**
+ * Receives result of connection to rpc pipe on remote server
+ *
+ * @param c composite context
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r data structure containing necessary parameters and return values
+ * @return nt status of rpc connection
+ **/
+
+static NTSTATUS libnet_RpcConnectSrv_recv(struct composite_context *c,
+ struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_RpcConnect *r)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ struct rpc_connect_srv_state *s;
+
+ /* move the returned rpc pipe between memory contexts */
+ s = talloc_get_type(c->private_data, struct rpc_connect_srv_state);
+ r->out.dcerpc_pipe = talloc_steal(mem_ctx, s->r.out.dcerpc_pipe);
+
+ /* reference created pipe structure to long-term libnet_context
+ so that it can be used by other api functions even after short-term
+ mem_ctx is freed */
+ if (r->in.dcerpc_iface == &ndr_table_samr) {
+ ctx->samr.pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
+ ctx->samr.samr_handle = ctx->samr.pipe->binding_handle;
+
+ } else if (r->in.dcerpc_iface == &ndr_table_lsarpc) {
+ ctx->lsa.pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
+ ctx->lsa.lsa_handle = ctx->lsa.pipe->binding_handle;
+ }
+
+ r->out.error_string = talloc_strdup(mem_ctx, "Success");
+
+ } else {
+ r->out.error_string = talloc_asprintf(mem_ctx, "Error: %s", nt_errstr(status));
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+struct rpc_connect_dc_state {
+ struct libnet_context *ctx;
+ struct libnet_RpcConnect r;
+ struct libnet_RpcConnect r2;
+ struct libnet_LookupDCs f;
+ const char *connect_name;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static void continue_lookup_dc(struct tevent_req *req);
+static void continue_rpc_connect(struct composite_context *ctx);
+
+
+/**
+ * Initiates connection to rpc pipe on domain pdc
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r data structure containing necessary parameters and return values
+ * @return composite context of this call
+ **/
+
+static struct composite_context* libnet_RpcConnectDC_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_RpcConnect *r,
+ void (*monitor)(struct monitor_msg *msg))
+{
+ struct composite_context *c;
+ struct rpc_connect_dc_state *s;
+ struct tevent_req *lookup_dc_req;
+
+ /* composite context allocation and setup */
+ c = composite_create(ctx, ctx->event_ctx);
+ if (c == NULL) return c;
+
+ s = talloc_zero(c, struct rpc_connect_dc_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+ s->monitor_fn = monitor;
+
+ s->ctx = ctx;
+ s->r = *r;
+ ZERO_STRUCT(s->r.out);
+
+ switch (r->level) {
+ case LIBNET_RPC_CONNECT_PDC:
+ s->f.in.name_type = NBT_NAME_PDC;
+ break;
+
+ case LIBNET_RPC_CONNECT_DC:
+ s->f.in.name_type = NBT_NAME_LOGON;
+ break;
+
+ default:
+ break;
+ }
+
+ s->f.in.domain_name = r->in.name;
+ s->f.out.num_dcs = 0;
+ s->f.out.dcs = NULL;
+
+ /* find the domain pdc first */
+ lookup_dc_req = libnet_LookupDCs_send(ctx, c, &s->f);
+ if (composite_nomem(lookup_dc_req, c)) return c;
+
+ tevent_req_set_callback(lookup_dc_req, continue_lookup_dc, c);
+ return c;
+}
+
+
+/*
+ Step 2 of RpcConnectDC: get domain controller name and
+ initiate RpcConnect to it
+*/
+static void continue_lookup_dc(struct tevent_req *req)
+{
+ struct composite_context *c;
+ struct rpc_connect_dc_state *s;
+ struct composite_context *rpc_connect_req;
+ struct monitor_msg msg;
+ struct msg_net_lookup_dc data;
+
+ c = tevent_req_callback_data(req, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct rpc_connect_dc_state);
+
+ /* receive result of domain controller lookup */
+ c->status = libnet_LookupDCs_recv(req, c, &s->f);
+ if (!composite_is_ok(c)) return;
+
+ /* decide on preferred address type depending on DC type */
+ s->connect_name = s->f.out.dcs[0].name;
+
+ /* post monitor message */
+ if (s->monitor_fn) {
+ /* prepare a monitor message and post it */
+ data.domain_name = s->f.in.domain_name;
+ data.hostname = s->f.out.dcs[0].name;
+ data.address = s->f.out.dcs[0].address;
+
+ msg.type = mon_NetLookupDc;
+ msg.data = &data;
+ msg.data_size = sizeof(data);
+ s->monitor_fn(&msg);
+ }
+
+ /* ok, pdc has been found so do attempt to rpc connect */
+ s->r2.level = LIBNET_RPC_CONNECT_SERVER_ADDRESS;
+
+ /* this will cause yet another name resolution, but at least
+ * we pass the right name down the stack now */
+ s->r2.in.name = talloc_strdup(s, s->connect_name);
+ s->r2.in.address = talloc_steal(s, s->f.out.dcs[0].address);
+ s->r2.in.dcerpc_iface = s->r.in.dcerpc_iface;
+ s->r2.in.dcerpc_flags = s->r.in.dcerpc_flags;
+
+ /* send rpc connect request to the server */
+ rpc_connect_req = libnet_RpcConnectSrv_send(s->ctx, c, &s->r2, s->monitor_fn);
+ if (composite_nomem(rpc_connect_req, c)) return;
+
+ composite_continue(c, rpc_connect_req, continue_rpc_connect, c);
+}
+
+
+/*
+ Step 3 of RpcConnectDC: get rpc connection to the server
+*/
+static void continue_rpc_connect(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct rpc_connect_dc_state *s;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct rpc_connect_dc_state);
+
+ c->status = libnet_RpcConnectSrv_recv(ctx, s->ctx, c, &s->r2);
+
+ /* error string is to be passed anyway */
+ s->r.out.error_string = s->r2.out.error_string;
+ if (!composite_is_ok(c)) return;
+
+ s->r.out.dcerpc_pipe = s->r2.out.dcerpc_pipe;
+
+ /* post monitor message */
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+ struct msg_net_rpc_connect data;
+ const struct dcerpc_binding *b = s->r.out.dcerpc_pipe->binding;
+
+ data.host = dcerpc_binding_get_string_option(b, "host");
+ data.endpoint = dcerpc_binding_get_string_option(b, "endpoint");
+ data.transport = dcerpc_binding_get_transport(b);
+ data.domain_name = dcerpc_binding_get_string_option(b, "target_hostname");
+
+ msg.type = mon_NetRpcConnect;
+ msg.data = (void*)&data;
+ msg.data_size = sizeof(data);
+ s->monitor_fn(&msg);
+ }
+
+ composite_done(c);
+}
+
+
+/**
+ * Receives result of connection to rpc pipe on domain pdc
+ *
+ * @param c composite context
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r data structure containing necessary parameters and return values
+ * @return nt status of rpc connection
+ **/
+
+static NTSTATUS libnet_RpcConnectDC_recv(struct composite_context *c,
+ struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_RpcConnect *r)
+{
+ NTSTATUS status;
+ struct rpc_connect_dc_state *s = talloc_get_type(c->private_data,
+ struct rpc_connect_dc_state);
+
+ status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ /* move connected rpc pipe between memory contexts
+
+ The use of talloc_reparent(talloc_parent(), ...) is
+ bizarre, but it is needed because of the absolutely
+ atrocious use of talloc in this code. We need to
+ force the original parent to change, but finding
+ the original parent is well nigh impossible at this
+ point in the code (yes, I tried).
+ */
+ r->out.dcerpc_pipe = talloc_reparent(talloc_parent(s->r.out.dcerpc_pipe),
+ mem_ctx, s->r.out.dcerpc_pipe);
+
+ /* reference created pipe structure to long-term libnet_context
+ so that it can be used by other api functions even after short-term
+ mem_ctx is freed */
+ if (r->in.dcerpc_iface == &ndr_table_samr) {
+ ctx->samr.pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
+ ctx->samr.samr_handle = ctx->samr.pipe->binding_handle;
+ } else if (r->in.dcerpc_iface == &ndr_table_lsarpc) {
+ ctx->lsa.pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
+ ctx->lsa.lsa_handle = ctx->lsa.pipe->binding_handle;
+ }
+
+ } else {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "Failed to rpc connect: %s",
+ nt_errstr(status));
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+
+struct rpc_connect_dci_state {
+ struct libnet_context *ctx;
+ struct libnet_RpcConnect r;
+ struct libnet_RpcConnect rpc_conn;
+ struct policy_handle lsa_handle;
+ struct lsa_QosInfo qos;
+ struct lsa_ObjectAttribute attr;
+ struct lsa_OpenPolicy2 lsa_open_policy;
+ struct dcerpc_pipe *lsa_pipe;
+ struct lsa_QueryInfoPolicy2 lsa_query_info2;
+ struct lsa_QueryInfoPolicy lsa_query_info;
+ struct dcerpc_binding *final_binding;
+ struct dcerpc_pipe *final_pipe;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg*);
+};
+
+
+static void continue_dci_rpc_connect(struct composite_context *ctx);
+static void continue_lsa_policy(struct tevent_req *subreq);
+static void continue_lsa_query_info(struct tevent_req *subreq);
+static void continue_lsa_query_info2(struct tevent_req *subreq);
+static void continue_epm_map_binding(struct composite_context *ctx);
+static void continue_secondary_conn(struct composite_context *ctx);
+static void continue_epm_map_binding_send(struct composite_context *c);
+
+
+/**
+ * Initiates connection to rpc pipe on remote server or pdc. Received result
+ * contains info on the domain name, domain sid and realm.
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r data structure containing necessary parameters and return values. Must be a talloc context
+ * @return composite context of this call
+ **/
+
+static struct composite_context* libnet_RpcConnectDCInfo_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_RpcConnect *r,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c, *conn_req;
+ struct rpc_connect_dci_state *s;
+
+ /* composite context allocation and setup */
+ c = composite_create(ctx, ctx->event_ctx);
+ if (c == NULL) return c;
+
+ s = talloc_zero(c, struct rpc_connect_dci_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+ s->monitor_fn = monitor;
+
+ s->ctx = ctx;
+ s->r = *r;
+ ZERO_STRUCT(s->r.out);
+
+
+ /* proceed to pure rpc connection if the binding string is provided,
+ otherwise try to connect domain controller */
+ if (r->in.binding == NULL) {
+ /* Pass on any binding flags (such as anonymous fallback) that have been set */
+ s->rpc_conn.in.dcerpc_flags = r->in.dcerpc_flags;
+
+ s->rpc_conn.in.name = r->in.name;
+ s->rpc_conn.level = LIBNET_RPC_CONNECT_DC;
+ } else {
+ s->rpc_conn.in.binding = r->in.binding;
+ s->rpc_conn.level = LIBNET_RPC_CONNECT_BINDING;
+ }
+
+ /* we need to query information on lsarpc interface first */
+ s->rpc_conn.in.dcerpc_iface = &ndr_table_lsarpc;
+
+ /* request connection to the lsa pipe on the pdc */
+ conn_req = libnet_RpcConnect_send(ctx, c, &s->rpc_conn, s->monitor_fn);
+ if (composite_nomem(c, conn_req)) return c;
+
+ composite_continue(c, conn_req, continue_dci_rpc_connect, c);
+ return c;
+}
+
+
+/*
+ Step 2 of RpcConnectDCInfo: receive opened rpc pipe and open
+ lsa policy handle
+*/
+static void continue_dci_rpc_connect(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct rpc_connect_dci_state *s;
+ struct tevent_req *subreq;
+ enum dcerpc_transport_t transport;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
+
+ c->status = libnet_RpcConnect_recv(ctx, s->ctx, c, &s->rpc_conn);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ /* post monitor message */
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+ struct msg_net_rpc_connect data;
+ const struct dcerpc_binding *b = s->r.out.dcerpc_pipe->binding;
+
+ data.host = dcerpc_binding_get_string_option(b, "host");
+ data.endpoint = dcerpc_binding_get_string_option(b, "endpoint");
+ data.transport = dcerpc_binding_get_transport(b);
+ data.domain_name = dcerpc_binding_get_string_option(b, "target_hostname");
+
+ msg.type = mon_NetRpcConnect;
+ msg.data = (void*)&data;
+ msg.data_size = sizeof(data);
+ s->monitor_fn(&msg);
+ }
+
+ /* prepare to open a policy handle on lsa pipe */
+ s->lsa_pipe = s->ctx->lsa.pipe;
+
+ s->qos.len = 0;
+ s->qos.impersonation_level = 2;
+ s->qos.context_mode = 1;
+ s->qos.effective_only = 0;
+
+ s->attr.sec_qos = &s->qos;
+
+ transport = dcerpc_binding_get_transport(s->lsa_pipe->binding);
+ if (transport == NCACN_IP_TCP) {
+ /*
+ * Skip to creating the actual connection. We can't open a
+ * policy handle over tcpip.
+ */
+ continue_epm_map_binding_send(c);
+ return;
+ }
+
+ s->lsa_open_policy.in.attr = &s->attr;
+ s->lsa_open_policy.in.system_name = talloc_asprintf(c, "\\");
+ if (composite_nomem(s->lsa_open_policy.in.system_name, c)) return;
+
+ s->lsa_open_policy.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ s->lsa_open_policy.out.handle = &s->lsa_handle;
+
+ subreq = dcerpc_lsa_OpenPolicy2_r_send(s, c->event_ctx,
+ s->lsa_pipe->binding_handle,
+ &s->lsa_open_policy);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_lsa_policy, c);
+}
+
+
+/*
+ Step 3 of RpcConnectDCInfo: Get policy handle and query lsa info
+ for kerberos realm (dns name) and guid. The query may fail.
+*/
+static void continue_lsa_policy(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct rpc_connect_dci_state *s;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
+
+ c->status = dcerpc_lsa_OpenPolicy2_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ if (NT_STATUS_EQUAL(s->lsa_open_policy.out.result, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) {
+ s->r.out.realm = NULL;
+ s->r.out.guid = NULL;
+ s->r.out.domain_name = NULL;
+ s->r.out.domain_sid = NULL;
+
+ /* Skip to the creating the actual connection, no info available on this transport */
+ continue_epm_map_binding_send(c);
+ return;
+
+ } else if (!NT_STATUS_IS_OK(s->lsa_open_policy.out.result)) {
+ composite_error(c, s->lsa_open_policy.out.result);
+ return;
+ }
+
+ /* post monitor message */
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+
+ msg.type = mon_LsaOpenPolicy;
+ msg.data = NULL;
+ msg.data_size = 0;
+ s->monitor_fn(&msg);
+ }
+
+ /* query lsa info for dns domain name and guid */
+ s->lsa_query_info2.in.handle = &s->lsa_handle;
+ s->lsa_query_info2.in.level = LSA_POLICY_INFO_DNS;
+ s->lsa_query_info2.out.info = talloc_zero(c, union lsa_PolicyInformation *);
+ if (composite_nomem(s->lsa_query_info2.out.info, c)) return;
+
+ subreq = dcerpc_lsa_QueryInfoPolicy2_r_send(s, c->event_ctx,
+ s->lsa_pipe->binding_handle,
+ &s->lsa_query_info2);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_lsa_query_info2, c);
+}
+
+
+/*
+ Step 4 of RpcConnectDCInfo: Get realm and guid if provided (rpc call
+ may result in failure) and query lsa info for domain name and sid.
+*/
+static void continue_lsa_query_info2(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct rpc_connect_dci_state *s;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
+
+ c->status = dcerpc_lsa_QueryInfoPolicy2_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+
+ /* In case of error just null the realm and guid and proceed
+ to the next step. After all, it doesn't have to be AD domain
+ controller we talking to - NT-style PDC also counts */
+
+ if (NT_STATUS_EQUAL(c->status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ s->r.out.realm = NULL;
+ s->r.out.guid = NULL;
+
+ } else {
+ if (!NT_STATUS_IS_OK(c->status)) {
+ s->r.out.error_string = talloc_asprintf(c,
+ "lsa_QueryInfoPolicy2 failed: %s",
+ nt_errstr(c->status));
+ composite_error(c, c->status);
+ return;
+ }
+
+ if (!NT_STATUS_IS_OK(s->lsa_query_info2.out.result)) {
+ s->r.out.error_string = talloc_asprintf(c,
+ "lsa_QueryInfoPolicy2 failed: %s",
+ nt_errstr(s->lsa_query_info2.out.result));
+ composite_error(c, s->lsa_query_info2.out.result);
+ return;
+ }
+
+ /* Copy the dns domain name and guid from the query result */
+
+ /* this should actually be a conversion from lsa_StringLarge */
+ s->r.out.realm = (*s->lsa_query_info2.out.info)->dns.dns_domain.string;
+ s->r.out.guid = talloc(c, struct GUID);
+ if (composite_nomem(s->r.out.guid, c)) {
+ s->r.out.error_string = NULL;
+ return;
+ }
+ *s->r.out.guid = (*s->lsa_query_info2.out.info)->dns.domain_guid;
+ }
+
+ /* post monitor message */
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+
+ msg.type = mon_LsaQueryPolicy;
+ msg.data = NULL;
+ msg.data_size = 0;
+ s->monitor_fn(&msg);
+ }
+
+ /* query lsa info for domain name and sid */
+ s->lsa_query_info.in.handle = &s->lsa_handle;
+ s->lsa_query_info.in.level = LSA_POLICY_INFO_DOMAIN;
+ s->lsa_query_info.out.info = talloc_zero(c, union lsa_PolicyInformation *);
+ if (composite_nomem(s->lsa_query_info.out.info, c)) return;
+
+ subreq = dcerpc_lsa_QueryInfoPolicy_r_send(s, c->event_ctx,
+ s->lsa_pipe->binding_handle,
+ &s->lsa_query_info);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_lsa_query_info, c);
+}
+
+
+/*
+ Step 5 of RpcConnectDCInfo: Get domain name and sid
+*/
+static void continue_lsa_query_info(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct rpc_connect_dci_state *s;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
+
+ c->status = dcerpc_lsa_QueryInfoPolicy_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ s->r.out.error_string = talloc_asprintf(c,
+ "lsa_QueryInfoPolicy failed: %s",
+ nt_errstr(c->status));
+ composite_error(c, c->status);
+ return;
+ }
+
+ /* post monitor message */
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+
+ msg.type = mon_LsaQueryPolicy;
+ msg.data = NULL;
+ msg.data_size = 0;
+ s->monitor_fn(&msg);
+ }
+
+ /* Copy the domain name and sid from the query result */
+ s->r.out.domain_sid = (*s->lsa_query_info.out.info)->domain.sid;
+ s->r.out.domain_name = (*s->lsa_query_info.out.info)->domain.name.string;
+
+ continue_epm_map_binding_send(c);
+}
+
+/*
+ Step 5 (continued) of RpcConnectDCInfo: request endpoint
+ map binding.
+
+ We may short-cut to this step if we don't support LSA OpenPolicy on this transport
+*/
+static void continue_epm_map_binding_send(struct composite_context *c)
+{
+ struct rpc_connect_dci_state *s;
+ struct composite_context *epm_map_req;
+ struct cli_credentials *epm_creds = NULL;
+
+ s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
+
+ /* prepare to get endpoint mapping for the requested interface */
+ s->final_binding = dcerpc_binding_dup(s, s->lsa_pipe->binding);
+ if (composite_nomem(s->final_binding, c)) return;
+
+ /*
+ * We don't want to inherit the assoc_group_id from the
+ * lsa_pipe here!
+ */
+ dcerpc_binding_set_assoc_group_id(s->final_binding, 0);
+
+ epm_creds = cli_credentials_init_anon(s);
+ if (composite_nomem(epm_creds, c)) return;
+
+ epm_map_req = dcerpc_epm_map_binding_send(c, s->final_binding, s->r.in.dcerpc_iface,
+ epm_creds,
+ s->ctx->event_ctx, s->ctx->lp_ctx);
+ if (composite_nomem(epm_map_req, c)) return;
+
+ composite_continue(c, epm_map_req, continue_epm_map_binding, c);
+}
+
+/*
+ Step 6 of RpcConnectDCInfo: Receive endpoint mapping and create secondary
+ rpc connection derived from already used pipe but connected to the requested
+ one (as specified in libnet_RpcConnect structure)
+*/
+static void continue_epm_map_binding(struct composite_context *ctx)
+{
+ struct composite_context *c, *sec_conn_req;
+ struct rpc_connect_dci_state *s;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
+
+ c->status = dcerpc_epm_map_binding_recv(ctx);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ s->r.out.error_string = talloc_asprintf(c,
+ "failed to map pipe with endpoint mapper - %s",
+ nt_errstr(c->status));
+ composite_error(c, c->status);
+ return;
+ }
+
+ /* create secondary connection derived from lsa pipe */
+ sec_conn_req = dcerpc_secondary_auth_connection_send(s->lsa_pipe,
+ s->final_binding,
+ s->r.in.dcerpc_iface,
+ s->ctx->cred,
+ s->ctx->lp_ctx);
+ if (composite_nomem(sec_conn_req, c)) return;
+
+ composite_continue(c, sec_conn_req, continue_secondary_conn, c);
+}
+
+
+/*
+ Step 7 of RpcConnectDCInfo: Get actual pipe to be returned
+ and complete this composite call
+*/
+static void continue_secondary_conn(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct rpc_connect_dci_state *s;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
+
+ c->status = dcerpc_secondary_auth_connection_recv(ctx, s->lsa_pipe,
+ &s->final_pipe);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ s->r.out.error_string = talloc_asprintf(c,
+ "secondary connection failed: %s",
+ nt_errstr(c->status));
+
+ composite_error(c, c->status);
+ return;
+ }
+
+ s->r.out.dcerpc_pipe = s->final_pipe;
+
+ /* post monitor message */
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+ struct msg_net_rpc_connect data;
+ const struct dcerpc_binding *b = s->r.out.dcerpc_pipe->binding;
+
+ /* prepare monitor message and post it */
+ data.host = dcerpc_binding_get_string_option(b, "host");
+ data.endpoint = dcerpc_binding_get_string_option(b, "endpoint");
+ data.transport = dcerpc_binding_get_transport(b);
+ data.domain_name = dcerpc_binding_get_string_option(b, "target_hostname");
+
+ msg.type = mon_NetRpcConnect;
+ msg.data = (void*)&data;
+ msg.data_size = sizeof(data);
+ s->monitor_fn(&msg);
+ }
+
+ composite_done(c);
+}
+
+
+/**
+ * Receives result of connection to rpc pipe and gets basic
+ * domain info (name, sid, realm, guid)
+ *
+ * @param c composite context
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r data structure containing return values
+ * @return nt status of rpc connection
+ **/
+
+static NTSTATUS libnet_RpcConnectDCInfo_recv(struct composite_context *c, struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx, struct libnet_RpcConnect *r)
+{
+ NTSTATUS status;
+ struct rpc_connect_dci_state *s = talloc_get_type(c->private_data,
+ struct rpc_connect_dci_state);
+
+ status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ r->out.realm = talloc_steal(mem_ctx, s->r.out.realm);
+ r->out.guid = talloc_steal(mem_ctx, s->r.out.guid);
+ r->out.domain_name = talloc_steal(mem_ctx, s->r.out.domain_name);
+ r->out.domain_sid = talloc_steal(mem_ctx, s->r.out.domain_sid);
+
+ r->out.dcerpc_pipe = talloc_steal(mem_ctx, s->r.out.dcerpc_pipe);
+
+ /* reference created pipe structure to long-term libnet_context
+ so that it can be used by other api functions even after short-term
+ mem_ctx is freed */
+ if (r->in.dcerpc_iface == &ndr_table_samr) {
+ ctx->samr.pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
+ ctx->samr.samr_handle = ctx->samr.pipe->binding_handle;
+
+ } else if (r->in.dcerpc_iface == &ndr_table_lsarpc) {
+ ctx->lsa.pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
+ ctx->lsa.lsa_handle = ctx->lsa.pipe->binding_handle;
+ }
+
+ } else {
+ if (s->r.out.error_string) {
+ r->out.error_string = talloc_steal(mem_ctx, s->r.out.error_string);
+ } else if (r->in.binding == NULL) {
+ r->out.error_string = talloc_asprintf(mem_ctx, "Connection to DC failed: %s", nt_errstr(status));
+ } else {
+ r->out.error_string = talloc_asprintf(mem_ctx, "Connection to DC %s failed: %s",
+ r->in.binding, nt_errstr(status));
+ }
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Initiates connection to rpc pipe on remote server or pdc, optionally
+ * providing domain info
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r data structure containing necessary parameters and return values
+ * @return composite context of this call
+ **/
+
+struct composite_context* libnet_RpcConnect_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_RpcConnect *r,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+
+ switch (r->level) {
+ case LIBNET_RPC_CONNECT_SERVER:
+ case LIBNET_RPC_CONNECT_SERVER_ADDRESS:
+ case LIBNET_RPC_CONNECT_BINDING:
+ c = libnet_RpcConnectSrv_send(ctx, mem_ctx, r, monitor);
+ break;
+
+ case LIBNET_RPC_CONNECT_PDC:
+ case LIBNET_RPC_CONNECT_DC:
+ c = libnet_RpcConnectDC_send(ctx, mem_ctx, r, monitor);
+ break;
+
+ case LIBNET_RPC_CONNECT_DC_INFO:
+ c = libnet_RpcConnectDCInfo_send(ctx, mem_ctx, r, monitor);
+ break;
+
+ default:
+ c = talloc_zero(mem_ctx, struct composite_context);
+ composite_error(c, NT_STATUS_INVALID_LEVEL);
+ }
+
+ return c;
+}
+
+
+/**
+ * Receives result of connection to rpc pipe on remote server or pdc
+ *
+ * @param c composite context
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r data structure containing necessary parameters and return values
+ * @return nt status of rpc connection
+ **/
+
+NTSTATUS libnet_RpcConnect_recv(struct composite_context *c, struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx, struct libnet_RpcConnect *r)
+{
+ switch (r->level) {
+ case LIBNET_RPC_CONNECT_SERVER:
+ case LIBNET_RPC_CONNECT_BINDING:
+ return libnet_RpcConnectSrv_recv(c, ctx, mem_ctx, r);
+
+ case LIBNET_RPC_CONNECT_PDC:
+ case LIBNET_RPC_CONNECT_DC:
+ return libnet_RpcConnectDC_recv(c, ctx, mem_ctx, r);
+
+ case LIBNET_RPC_CONNECT_DC_INFO:
+ return libnet_RpcConnectDCInfo_recv(c, ctx, mem_ctx, r);
+
+ default:
+ ZERO_STRUCT(r->out);
+ return NT_STATUS_INVALID_LEVEL;
+ }
+}
+
+
+/**
+ * Connect to a rpc pipe on a remote server - sync version
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r data structure containing necessary parameters and return values
+ * @return nt status of rpc connection
+ **/
+
+NTSTATUS libnet_RpcConnect(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_RpcConnect *r)
+{
+ struct composite_context *c;
+
+ c = libnet_RpcConnect_send(ctx, mem_ctx, r, NULL);
+ return libnet_RpcConnect_recv(c, ctx, mem_ctx, r);
+}
diff --git a/source4/libnet/libnet_rpc.h b/source4/libnet/libnet_rpc.h
new file mode 100644
index 0000000..fb26284
--- /dev/null
+++ b/source4/libnet/libnet_rpc.h
@@ -0,0 +1,73 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 "librpc/rpc/dcerpc.h"
+
+/*
+ * struct definition for connecting to a dcerpc inferface
+ */
+
+enum libnet_RpcConnect_level {
+ LIBNET_RPC_CONNECT_SERVER, /* connect to a standalone rpc server */
+ LIBNET_RPC_CONNECT_SERVER_ADDRESS, /* connect to a standalone rpc server,
+ knowing both name and address */
+ LIBNET_RPC_CONNECT_PDC, /* connect to a domain pdc (resolves domain
+ name to a pdc address before connecting) */
+ LIBNET_RPC_CONNECT_DC, /* connect to any DC (resolves domain
+ name to a DC address before connecting) */
+ LIBNET_RPC_CONNECT_BINDING, /* specified binding string */
+ LIBNET_RPC_CONNECT_DC_INFO /* connect to a DC and provide basic domain
+ information (name, realm, sid, guid) */
+};
+
+struct libnet_RpcConnect {
+ enum libnet_RpcConnect_level level;
+
+ struct {
+ const char *name;
+ const char *address;
+ const char *binding;
+ const struct ndr_interface_table *dcerpc_iface;
+ int dcerpc_flags;
+ } in;
+ struct {
+ struct dcerpc_pipe *dcerpc_pipe;
+
+ /* parameters provided in LIBNET_RPC_CONNECT_DC_INFO level, null otherwise */
+ const char *domain_name;
+ struct dom_sid *domain_sid;
+ const char *realm; /* these parameters are only present if */
+ struct GUID *guid; /* the remote server is known to be AD */
+
+ const char *error_string;
+ } out;
+};
+
+
+/*
+ * Monitor messages sent from libnet_rpc.c functions
+ */
+
+struct msg_net_rpc_connect {
+ const char *host;
+ const char *domain_name;
+ const char *endpoint;
+ enum dcerpc_transport_t transport;
+};
diff --git a/source4/libnet/libnet_samsync.h b/source4/libnet/libnet_samsync.h
new file mode 100644
index 0000000..fb938d0
--- /dev/null
+++ b/source4/libnet/libnet_samsync.h
@@ -0,0 +1,32 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Bartlett <abartlet@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 "librpc/gen_ndr/netlogon.h"
+
+struct libnet_SamDump_keytab {
+ struct {
+ const char *binding_string;
+ const char *keytab_name;
+ struct cli_credentials *machine_account;
+ } in;
+ struct {
+ const char *error_string;
+ } out;
+};
+
diff --git a/source4/libnet/libnet_share.c b/source4/libnet/libnet_share.c
new file mode 100644
index 0000000..d8e8240
--- /dev/null
+++ b/source4/libnet/libnet_share.c
@@ -0,0 +1,215 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Grégory LEOCADIE <gleocadie@idealx.com>
+
+ 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 "libnet/libnet.h"
+#include "librpc/gen_ndr/ndr_srvsvc_c.h"
+
+
+NTSTATUS libnet_ListShares(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx, struct libnet_ListShares *r)
+{
+ NTSTATUS status;
+ struct libnet_RpcConnect c;
+ struct srvsvc_NetShareEnumAll s;
+ struct srvsvc_NetShareInfoCtr info_ctr;
+ uint32_t resume_handle = 0;
+ uint32_t totalentries = 0;
+ struct srvsvc_NetShareCtr0 ctr0;
+ struct srvsvc_NetShareCtr1 ctr1;
+ struct srvsvc_NetShareCtr2 ctr2;
+ struct srvsvc_NetShareCtr501 ctr501;
+ struct srvsvc_NetShareCtr502 ctr502;
+
+ ZERO_STRUCT(c);
+
+ c.level = LIBNET_RPC_CONNECT_SERVER;
+ c.in.name = r->in.server_name;
+ c.in.dcerpc_iface = &ndr_table_srvsvc;
+
+ s.in.server_unc = talloc_asprintf(mem_ctx, "\\\\%s", c.in.name);
+
+ status = libnet_RpcConnect(ctx, mem_ctx, &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "Connection to SRVSVC pipe of server %s "
+ "failed: %s",
+ r->in.server_name,
+ nt_errstr(status));
+ return status;
+ }
+
+ info_ctr.level = r->in.level;
+ switch (info_ctr.level) {
+ case 0:
+ info_ctr.ctr.ctr0 = &ctr0;
+ ZERO_STRUCT(ctr0);
+ break;
+ case 1:
+ info_ctr.ctr.ctr1 = &ctr1;
+ ZERO_STRUCT(ctr1);
+ break;
+ case 2:
+ info_ctr.ctr.ctr2 = &ctr2;
+ ZERO_STRUCT(ctr2);
+ break;
+ case 501:
+ info_ctr.ctr.ctr501 = &ctr501;
+ ZERO_STRUCT(ctr501);
+ break;
+ case 502:
+ info_ctr.ctr.ctr502 = &ctr502;
+ ZERO_STRUCT(ctr502);
+ break;
+ default:
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "libnet_ListShares: Invalid info level requested: %d",
+ info_ctr.level);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ s.in.max_buffer = ~0;
+ s.in.resume_handle = &resume_handle;
+ s.in.info_ctr = &info_ctr;
+ s.out.info_ctr = &info_ctr;
+ s.out.totalentries = &totalentries;
+
+ status = dcerpc_srvsvc_NetShareEnumAll_r(c.out.dcerpc_pipe->binding_handle, mem_ctx, &s);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "srvsvc_NetShareEnumAll on server '%s' failed"
+ ": %s",
+ r->in.server_name, nt_errstr(status));
+ goto disconnect;
+ }
+
+ if (!W_ERROR_IS_OK(s.out.result) && !W_ERROR_EQUAL(s.out.result, WERR_MORE_DATA)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "srvsvc_NetShareEnumAll on server '%s' failed: %s",
+ r->in.server_name, win_errstr(s.out.result));
+ goto disconnect;
+ }
+
+ r->out.ctr = s.out.info_ctr->ctr;
+
+disconnect:
+ talloc_free(c.out.dcerpc_pipe);
+
+ return status;
+}
+
+
+NTSTATUS libnet_AddShare(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx, struct libnet_AddShare *r)
+{
+ NTSTATUS status;
+ struct libnet_RpcConnect c;
+ struct srvsvc_NetShareAdd s;
+ union srvsvc_NetShareInfo info;
+
+ ZERO_STRUCT(c);
+
+ c.level = LIBNET_RPC_CONNECT_SERVER;
+ c.in.name = r->in.server_name;
+ c.in.dcerpc_iface = &ndr_table_srvsvc;
+
+ status = libnet_RpcConnect(ctx, mem_ctx, &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "Connection to SRVSVC pipe of server %s "
+ "failed: %s",
+ r->in.server_name, nt_errstr(status));
+ return status;
+ }
+
+ info.info2 = &r->in.share;
+
+ s.in.level = 2;
+ s.in.info = &info;
+ s.in.server_unc = talloc_asprintf(mem_ctx, "\\\\%s", r->in.server_name);
+
+ status = dcerpc_srvsvc_NetShareAdd_r(c.out.dcerpc_pipe->binding_handle, mem_ctx, &s);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "srvsvc_NetShareAdd '%s' on server '%s' failed"
+ ": %s",
+ r->in.share.name, r->in.server_name,
+ nt_errstr(status));
+ } else if (!W_ERROR_IS_OK(s.out.result)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "srvsvc_NetShareAdd '%s' on server '%s' failed"
+ ": %s",
+ r->in.share.name, r->in.server_name,
+ win_errstr(s.out.result));
+ status = werror_to_ntstatus(s.out.result);
+ }
+
+ talloc_free(c.out.dcerpc_pipe);
+
+ return status;
+}
+
+
+NTSTATUS libnet_DelShare(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx, struct libnet_DelShare *r)
+{
+ NTSTATUS status;
+ struct libnet_RpcConnect c;
+ struct srvsvc_NetShareDel s;
+
+ ZERO_STRUCT(c);
+ ZERO_STRUCT(s);
+
+ c.level = LIBNET_RPC_CONNECT_SERVER;
+ c.in.name = r->in.server_name;
+ c.in.dcerpc_iface = &ndr_table_srvsvc;
+
+ status = libnet_RpcConnect(ctx, mem_ctx, &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "Connection to SRVSVC pipe of server %s "
+ "failed: %s",
+ r->in.server_name, nt_errstr(status));
+ return status;
+ }
+
+ s.in.server_unc = talloc_asprintf(mem_ctx, "\\\\%s", r->in.server_name);
+ s.in.share_name = r->in.share_name;
+
+ status = dcerpc_srvsvc_NetShareDel_r(c.out.dcerpc_pipe->binding_handle, mem_ctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "srvsvc_NetShareDel '%s' on server '%s' failed"
+ ": %s",
+ r->in.share_name, r->in.server_name,
+ nt_errstr(status));
+ } else if (!W_ERROR_IS_OK(s.out.result)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "srvsvc_NetShareDel '%s' on server '%s' failed"
+ ": %s",
+ r->in.share_name, r->in.server_name,
+ win_errstr(s.out.result));
+ status = werror_to_ntstatus(s.out.result);
+ }
+
+ talloc_free(c.out.dcerpc_pipe);
+
+ return status;
+}
diff --git a/source4/libnet/libnet_share.h b/source4/libnet/libnet_share.h
new file mode 100644
index 0000000..3a9bd72
--- /dev/null
+++ b/source4/libnet/libnet_share.h
@@ -0,0 +1,70 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Grégory LEOCADIE <gleocadie@idealx.com>
+
+ 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 "librpc/gen_ndr/srvsvc.h"
+
+enum libnet_ListShares_level {
+ LIBNET_LIST_SHARES_GENERIC,
+ LIBNET_LIST_SHARES_SRVSVC
+};
+
+struct libnet_ListShares {
+ struct {
+ const char *server_name;
+ uint32_t *resume_handle;
+ uint32_t level;
+ } in;
+ struct {
+ const char *error_string;
+ union srvsvc_NetShareCtr ctr;
+ uint32_t *resume_handle;
+ } out;
+};
+
+enum libnet_AddShare_level {
+ LIBNET_ADD_SHARE_GENERIC,
+ LIBNET_ADD_SHARE_SRVSVC
+};
+
+struct libnet_AddShare {
+ enum libnet_AddShare_level level;
+ struct {
+ const char * server_name;
+ struct srvsvc_NetShareInfo2 share;
+ } in;
+ struct {
+ const char* error_string;
+ } out;
+};
+
+enum libnet_DelShare_level {
+ LIBNET_DEL_SHARE_GENERIC,
+ LIBNET_DEL_SHARE_SRVSVC
+};
+
+struct libnet_DelShare {
+ enum libnet_DelShare_level level;
+ struct {
+ const char *server_name;
+ const char *share_name;
+ } in;
+ struct {
+ const char *error_string;
+ } out;
+};
diff --git a/source4/libnet/libnet_site.c b/source4/libnet/libnet_site.c
new file mode 100644
index 0000000..691e4b2
--- /dev/null
+++ b/source4/libnet/libnet_site.c
@@ -0,0 +1,292 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Brad Henry 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 "libnet/libnet.h"
+#include "libcli/cldap/cldap.h"
+#include <ldb.h>
+#include <ldb_errors.h>
+#include "libcli/resolve/resolve.h"
+#include "param/param.h"
+#include "lib/tsocket/tsocket.h"
+
+/**
+ * 1. Setup a CLDAP socket.
+ * 2. Lookup the default Site-Name.
+ */
+NTSTATUS libnet_FindSite(TALLOC_CTX *ctx, struct libnet_context *lctx, struct libnet_JoinSite *r)
+{
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx;
+
+ char *site_name_str;
+ char *config_dn_str;
+ char *server_dn_str;
+
+ struct cldap_socket *cldap = NULL;
+ struct cldap_netlogon search;
+ int ret;
+ struct tsocket_address *dest_address;
+
+ tmp_ctx = talloc_named(ctx, 0, "libnet_FindSite temp context");
+ if (!tmp_ctx) {
+ r->out.error_string = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Resolve the site name. */
+ ZERO_STRUCT(search);
+ search.in.dest_address = NULL;
+ search.in.dest_port = 0;
+ search.in.acct_control = -1;
+ search.in.version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
+ search.in.map_response = true;
+
+ ret = tsocket_address_inet_from_strings(tmp_ctx, "ip",
+ r->in.dest_address,
+ r->in.cldap_port,
+ &dest_address);
+ if (ret != 0) {
+ r->out.error_string = NULL;
+ status = map_nt_error_from_unix_common(errno);
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ /* we want to use non async calls, so we're not passing an event context */
+ status = cldap_socket_init(tmp_ctx, NULL, dest_address, &cldap);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ r->out.error_string = NULL;
+ return status;
+ }
+ status = cldap_netlogon(cldap, tmp_ctx, &search);
+ if (!NT_STATUS_IS_OK(status)
+ || search.out.netlogon.data.nt5_ex.client_site == NULL
+ || search.out.netlogon.data.nt5_ex.client_site[0] == '\0') {
+ /*
+ If cldap_netlogon() returns in error,
+ default to using Default-First-Site-Name.
+ */
+ site_name_str = talloc_asprintf(tmp_ctx, "%s",
+ "Default-First-Site-Name");
+ if (!site_name_str) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ site_name_str = talloc_asprintf(tmp_ctx, "%s",
+ search.out.netlogon.data.nt5_ex.client_site);
+ if (!site_name_str) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ /* Generate the CN=Configuration,... DN. */
+/* TODO: look it up! */
+ config_dn_str = talloc_asprintf(tmp_ctx, "CN=Configuration,%s", r->in.domain_dn_str);
+ if (!config_dn_str) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Generate the CN=Servers,... DN. */
+ server_dn_str = talloc_asprintf(tmp_ctx, "CN=%s,CN=Servers,CN=%s,CN=Sites,%s",
+ r->in.netbios_name, site_name_str, config_dn_str);
+ if (!server_dn_str) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r->out.site_name_str = site_name_str;
+ talloc_steal(r, site_name_str);
+
+ r->out.config_dn_str = config_dn_str;
+ talloc_steal(r, config_dn_str);
+
+ r->out.server_dn_str = server_dn_str;
+ talloc_steal(r, server_dn_str);
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+}
+
+/*
+ * find out Site specific stuff:
+ * 1. Lookup the Site name.
+ * 2. Add entry CN=<netbios name>,CN=Servers,CN=<site name>,CN=Sites,CN=Configuration,<domain dn>.
+ * TODO: 3.) use DsAddEntry() to create CN=NTDS Settings,CN=<netbios name>,CN=Servers,CN=<site name>,...
+ */
+NTSTATUS libnet_JoinSite(struct libnet_context *ctx,
+ struct ldb_context *remote_ldb,
+ struct libnet_JoinDomain *libnet_r)
+{
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx;
+
+ struct libnet_JoinSite *r;
+
+ struct ldb_dn *server_dn;
+ struct ldb_message *msg;
+ int rtn;
+
+ const char *server_dn_str;
+ const char *host;
+ struct nbt_name name;
+ const char *dest_addr = NULL;
+
+ tmp_ctx = talloc_named(libnet_r, 0, "libnet_JoinSite temp context");
+ if (!tmp_ctx) {
+ libnet_r->out.error_string = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r = talloc(tmp_ctx, struct libnet_JoinSite);
+ if (!r) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ host = dcerpc_binding_get_string_option(libnet_r->out.samr_binding, "host");
+ make_nbt_name_client(&name, host);
+ status = resolve_name_ex(lpcfg_resolve_context(ctx->lp_ctx),
+ 0, 0,
+ &name, r, &dest_addr, ctx->event_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ /* Resolve the site name and AD DN's. */
+ r->in.dest_address = dest_addr;
+ r->in.netbios_name = libnet_r->in.netbios_name;
+ r->in.domain_dn_str = libnet_r->out.domain_dn_str;
+ r->in.cldap_port = lpcfg_cldap_port(ctx->lp_ctx);
+
+ status = libnet_FindSite(tmp_ctx, ctx, r);
+ if (!NT_STATUS_IS_OK(status)) {
+ libnet_r->out.error_string =
+ talloc_steal(libnet_r, r->out.error_string);
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ server_dn_str = r->out.server_dn_str;
+
+ /*
+ Add entry CN=<netbios name>,CN=Servers,CN=<site name>,CN=Sites,CN=Configuration,<domain dn>.
+ */
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rtn = ldb_msg_add_string(msg, "objectClass", "server");
+ if (rtn != LDB_SUCCESS) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ rtn = ldb_msg_add_string(msg, "systemFlags", "50000000");
+ if (rtn != LDB_SUCCESS) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ rtn = ldb_msg_add_string(msg, "serverReference", libnet_r->out.account_dn_str);
+ if (rtn != LDB_SUCCESS) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ server_dn = ldb_dn_new(tmp_ctx, remote_ldb, server_dn_str);
+ if ( ! ldb_dn_validate(server_dn)) {
+ libnet_r->out.error_string = talloc_asprintf(libnet_r,
+ "Invalid server dn: %s",
+ server_dn_str);
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ msg->dn = server_dn;
+
+ rtn = ldb_add(remote_ldb, msg);
+ if (rtn == LDB_ERR_ENTRY_ALREADY_EXISTS) {
+ unsigned int i;
+
+ /* make a 'modify' msg, and only for serverReference */
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ msg->dn = server_dn;
+
+ rtn = ldb_msg_add_string(msg, "serverReference",libnet_r->out.account_dn_str);
+ if (rtn != LDB_SUCCESS) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* mark all the message elements (should be just one)
+ as LDB_FLAG_MOD_REPLACE */
+ for (i=0;i<msg->num_elements;i++) {
+ msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ rtn = ldb_modify(remote_ldb, msg);
+ if (rtn != LDB_SUCCESS) {
+ libnet_r->out.error_string
+ = talloc_asprintf(libnet_r,
+ "Failed to modify server entry %s: %s: %d",
+ server_dn_str,
+ ldb_errstring(remote_ldb), rtn);
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ } else if (rtn != LDB_SUCCESS) {
+ libnet_r->out.error_string
+ = talloc_asprintf(libnet_r,
+ "Failed to add server entry %s: %s: %d",
+ server_dn_str, ldb_errstring(remote_ldb),
+ rtn);
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ DEBUG(0, ("We still need to perform a DsAddEntry() so that we can create the CN=NTDS Settings container.\n"));
+
+ /* Store the server DN in libnet_r */
+ libnet_r->out.server_dn_str = server_dn_str;
+ talloc_steal(libnet_r, server_dn_str);
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+}
diff --git a/source4/libnet/libnet_site.h b/source4/libnet/libnet_site.h
new file mode 100644
index 0000000..8e607c5
--- /dev/null
+++ b/source4/libnet/libnet_site.h
@@ -0,0 +1,35 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Brad Henry 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 libnet_JoinSite {
+ struct {
+ const char *dest_address;
+ const char *netbios_name;
+ const char *domain_dn_str;
+ uint16_t cldap_port;
+ } in;
+
+ struct {
+ const char *error_string;
+ const char *site_name_str;
+ const char *config_dn_str;
+ const char *server_dn_str;
+ } out;
+};
+
diff --git a/source4/libnet/libnet_time.c b/source4/libnet/libnet_time.c
new file mode 100644
index 0000000..e3b6275
--- /dev/null
+++ b/source4/libnet/libnet_time.c
@@ -0,0 +1,125 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 "libnet/libnet.h"
+#include "system/time.h"
+#include "librpc/gen_ndr/ndr_srvsvc_c.h"
+
+/*
+ * get the remote time of a server via srvsvc_NetRemoteTOD
+ */
+static NTSTATUS libnet_RemoteTOD_srvsvc(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_RemoteTOD *r)
+{
+ NTSTATUS status;
+ struct libnet_RpcConnect c;
+ struct srvsvc_NetRemoteTOD tod;
+ struct srvsvc_NetRemoteTODInfo *info = NULL;
+ struct tm tm;
+
+ ZERO_STRUCT(c);
+
+ /* prepare connect to the SRVSVC pipe of a timeserver */
+ c.level = LIBNET_RPC_CONNECT_SERVER;
+ c.in.name = r->srvsvc.in.server_name;
+ c.in.dcerpc_iface = &ndr_table_srvsvc;
+
+ /* 1. connect to the SRVSVC pipe of a timeserver */
+ status = libnet_RpcConnect(ctx, mem_ctx, &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->srvsvc.out.error_string = talloc_asprintf(mem_ctx,
+ "Connection to SRVSVC pipe of server '%s' failed: %s",
+ r->srvsvc.in.server_name, nt_errstr(status));
+ return status;
+ }
+
+ /* prepare srvsvc_NetrRemoteTOD */
+ tod.in.server_unc = talloc_asprintf(mem_ctx, "\\%s", c.in.name);
+ tod.out.info = &info;
+
+ /* 2. try srvsvc_NetRemoteTOD */
+ status = dcerpc_srvsvc_NetRemoteTOD_r(c.out.dcerpc_pipe->binding_handle, mem_ctx, &tod);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->srvsvc.out.error_string = talloc_asprintf(mem_ctx,
+ "srvsvc_NetrRemoteTOD on server '%s' failed: %s",
+ r->srvsvc.in.server_name, nt_errstr(status));
+ goto disconnect;
+ }
+
+ /* check result of srvsvc_NetrRemoteTOD */
+ if (!W_ERROR_IS_OK(tod.out.result)) {
+ r->srvsvc.out.error_string = talloc_asprintf(mem_ctx,
+ "srvsvc_NetrRemoteTOD on server '%s' failed: %s",
+ r->srvsvc.in.server_name, win_errstr(tod.out.result));
+ status = werror_to_ntstatus(tod.out.result);
+ goto disconnect;
+ }
+
+ /* need to set the out parameters */
+ tm.tm_sec = (int)info->secs;
+ tm.tm_min = (int)info->mins;
+ tm.tm_hour = (int)info->hours;
+ tm.tm_mday = (int)info->day;
+ tm.tm_mon = (int)info->month -1;
+ tm.tm_year = (int)info->year - 1900;
+ tm.tm_wday = -1;
+ tm.tm_yday = -1;
+ tm.tm_isdst = -1;
+
+ r->srvsvc.out.time = timegm(&tm);
+ r->srvsvc.out.time_zone = info->timezone * 60;
+
+ goto disconnect;
+
+disconnect:
+ /* close connection */
+ talloc_free(c.out.dcerpc_pipe);
+
+ return status;
+}
+
+static NTSTATUS libnet_RemoteTOD_generic(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_RemoteTOD *r)
+{
+ NTSTATUS status;
+ union libnet_RemoteTOD r2;
+
+ r2.srvsvc.level = LIBNET_REMOTE_TOD_SRVSVC;
+ r2.srvsvc.in.server_name = r->generic.in.server_name;
+
+ status = libnet_RemoteTOD(ctx, mem_ctx, &r2);
+
+ r->generic.out.time = r2.srvsvc.out.time;
+ r->generic.out.time_zone = r2.srvsvc.out.time_zone;
+
+ r->generic.out.error_string = r2.srvsvc.out.error_string;
+
+ return status;
+}
+
+NTSTATUS libnet_RemoteTOD(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_RemoteTOD *r)
+{
+ switch (r->generic.level) {
+ case LIBNET_REMOTE_TOD_GENERIC:
+ return libnet_RemoteTOD_generic(ctx, mem_ctx, r);
+ case LIBNET_REMOTE_TOD_SRVSVC:
+ return libnet_RemoteTOD_srvsvc(ctx, mem_ctx, r);
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
diff --git a/source4/libnet/libnet_time.h b/source4/libnet/libnet_time.h
new file mode 100644
index 0000000..4382d72
--- /dev/null
+++ b/source4/libnet/libnet_time.h
@@ -0,0 +1,46 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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/>.
+*/
+
+/* struct and enum for getting the time of a remote system */
+enum libnet_RemoteTOD_level {
+ LIBNET_REMOTE_TOD_GENERIC,
+ LIBNET_REMOTE_TOD_SRVSVC
+};
+
+union libnet_RemoteTOD {
+ struct {
+ enum libnet_RemoteTOD_level level;
+
+ struct _libnet_RemoteTOD_in {
+ const char *server_name;
+ } in;
+
+ struct _libnet_RemoteTOD_out {
+ time_t time;
+ int time_zone;
+ const char *error_string;
+ } out;
+ } generic;
+
+ struct {
+ enum libnet_RemoteTOD_level level;
+ struct _libnet_RemoteTOD_in in;
+ struct _libnet_RemoteTOD_out out;
+ } srvsvc;
+};
diff --git a/source4/libnet/libnet_unbecome_dc.c b/source4/libnet/libnet_unbecome_dc.c
new file mode 100644
index 0000000..38d6a94
--- /dev/null
+++ b/source4/libnet/libnet_unbecome_dc.c
@@ -0,0 +1,792 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 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 "libnet/libnet.h"
+#include "libcli/composite/composite.h"
+#include "libcli/cldap/cldap.h"
+#include <ldb.h>
+#include <ldb_errors.h>
+#include "ldb_wrap.h"
+#include "dsdb/samdb/samdb.h"
+#include "../libds/common/flags.h"
+#include "librpc/gen_ndr/ndr_drsuapi_c.h"
+#include "param/param.h"
+#include "lib/tsocket/tsocket.h"
+
+/*****************************************************************************
+ * Windows 2003 (w2k3) does the following steps when changing the server role
+ * from domain controller back to domain member
+ *
+ * We mostly do the same.
+ *****************************************************************************/
+
+/*
+ * lookup DC:
+ * - using nbt name<1C> request and a samlogon mailslot request
+ * or
+ * - using a DNS SRV _ldap._tcp.dc._msdcs. request and a CLDAP netlogon request
+ *
+ * see: unbecomeDC_send_cldap() and unbecomeDC_recv_cldap()
+ */
+
+/*
+ * Open 1st LDAP connection to the DC using admin credentials
+ *
+ * see: unbecomeDC_ldap_connect()
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: unbecomeDC_ldap_rootdse()
+ *
+ * Request:
+ * basedn: ""
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: defaultNamingContext
+ * configurationNamingContext
+ * Result:
+ * ""
+ * defaultNamingContext: <domain_partition>
+ * configurationNamingContext:CN=Configuration,<domain_partition>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: unbecomeDC_ldap_computer_object()
+ *
+ * Request:
+ * basedn: <domain_partition>
+ * scope: sub
+ * filter: (&(|(objectClass=user)(objectClass=computer))(sAMAccountName=<new_dc_account_name>))
+ * attrs: distinguishedName
+ * userAccountControl
+ * Result:
+ * CN=<new_dc_netbios_name>,CN=Domain Controllers,<domain_partition>
+ * distinguishedName: CN=<new_dc_netbios_name>,CN=Domain Controllers,<domain_partition>
+ * userAccoountControl: 532480 <0x82000>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: unbecomeDC_ldap_modify_computer()
+ *
+ * Request:
+ * basedn: CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: userAccountControl
+ * Result:
+ * CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * userAccoountControl: 532480 <0x82000>
+ */
+
+/*
+ * LDAP modify 1st LDAP connection:
+ *
+ * see: unbecomeDC_ldap_modify_computer()
+ *
+ * Request (replace):
+ * CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * userAccoountControl: 4096 <0x1000>
+ * Result:
+ * <success>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: unbecomeDC_ldap_move_computer()
+ *
+ * Request:
+ * basedn: <WKGUID=aa312825768811d1aded00c04fd8d5cd,<domain_partition>>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: 1.1
+ * Result:
+ * CN=Computers,<domain_partition>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * not implemented because it doesn't give any new information
+ *
+ * Request:
+ * basedn: CN=Computers,<domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: distinguishedName
+ * Result:
+ * CN=Computers,<domain_partition>
+ * distinguishedName: CN=Computers,<domain_partition>
+ */
+
+/*
+ * LDAP modifyRDN 1st LDAP connection:
+ *
+ * see: unbecomeDC_ldap_move_computer()
+ *
+ * Request:
+ * entry: CN=<new_dc_netbios_name>,CN=Domain Controllers,<domain_partition>
+ * newrdn: CN=<new_dc_netbios_name>
+ * deleteoldrdn: TRUE
+ * newparent: CN=Computers,<domain_partition>
+ * Result:
+ * <success>
+ */
+
+/*
+ * LDAP unbind on the 1st LDAP connection
+ *
+ * not implemented, because it's not needed...
+ */
+
+/*
+ * Open 1st DRSUAPI connection to the DC using admin credentials
+ * DsBind with DRSUAPI_DS_BIND_GUID ("e24d201a-4fd6-11d1-a3da-0000f875ae0d")
+ *
+ * see: unbecomeDC_drsuapi_connect_send(), unbecomeDC_drsuapi_connect_recv(),
+ * unbecomeDC_drsuapi_bind_send() and unbecomeDC_drsuapi_bind_recv()
+ */
+
+/*
+ * DsRemoveDsServer to remove the
+ * CN=<machine_name>,CN=Servers,CN=<site_name>,CN=Configuration,<domain_partition>
+ * and CN=NTDS Settings,CN=<machine_name>,CN=Servers,CN=<site_name>,CN=Configuration,<domain_partition>
+ * on the 1st DRSUAPI connection
+ *
+ * see: unbecomeDC_drsuapi_remove_ds_server_send() and unbecomeDC_drsuapi_remove_ds_server_recv()
+ */
+
+/*
+ * DsUnbind on the 1st DRSUAPI connection
+ *
+ * not implemented, because it's not needed...
+ */
+
+
+struct libnet_UnbecomeDC_state {
+ struct composite_context *creq;
+
+ struct libnet_context *libnet;
+
+ struct {
+ struct cldap_socket *sock;
+ struct cldap_netlogon io;
+ struct NETLOGON_SAM_LOGON_RESPONSE_EX netlogon;
+ } cldap;
+
+ struct {
+ struct ldb_context *ldb;
+ } ldap;
+
+ struct {
+ struct dcerpc_binding *binding;
+ struct dcerpc_pipe *pipe;
+ struct dcerpc_binding_handle *drsuapi_handle;
+ struct drsuapi_DsBind bind_r;
+ struct GUID bind_guid;
+ struct drsuapi_DsBindInfoCtr bind_info_ctr;
+ struct drsuapi_DsBindInfo28 local_info28;
+ struct drsuapi_DsBindInfo28 remote_info28;
+ struct policy_handle bind_handle;
+ struct drsuapi_DsRemoveDSServer rm_ds_srv_r;
+ } drsuapi;
+
+ struct {
+ /* input */
+ const char *dns_name;
+ const char *netbios_name;
+
+ /* constructed */
+ struct GUID guid;
+ const char *dn_str;
+ } domain;
+
+ struct {
+ /* constructed */
+ const char *config_dn_str;
+ } forest;
+
+ struct {
+ /* input */
+ const char *address;
+
+ /* constructed */
+ const char *dns_name;
+ const char *netbios_name;
+ const char *site_name;
+ } source_dsa;
+
+ struct {
+ /* input */
+ const char *netbios_name;
+
+ /* constructed */
+ const char *dns_name;
+ const char *site_name;
+ const char *computer_dn_str;
+ const char *server_dn_str;
+ uint32_t user_account_control;
+ } dest_dsa;
+};
+
+static void unbecomeDC_recv_cldap(struct tevent_req *req);
+
+static void unbecomeDC_send_cldap(struct libnet_UnbecomeDC_state *s)
+{
+ struct composite_context *c = s->creq;
+ struct tevent_req *req;
+ struct tsocket_address *dest_address;
+ int ret;
+
+ s->cldap.io.in.dest_address = NULL;
+ s->cldap.io.in.dest_port = 0;
+ s->cldap.io.in.realm = s->domain.dns_name;
+ s->cldap.io.in.host = s->dest_dsa.netbios_name;
+ s->cldap.io.in.user = NULL;
+ s->cldap.io.in.domain_guid = NULL;
+ s->cldap.io.in.domain_sid = NULL;
+ s->cldap.io.in.acct_control = -1;
+ s->cldap.io.in.version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
+ s->cldap.io.in.map_response = true;
+
+ ret = tsocket_address_inet_from_strings(s, "ip",
+ s->source_dsa.address,
+ lpcfg_cldap_port(s->libnet->lp_ctx),
+ &dest_address);
+ if (ret != 0) {
+ c->status = map_nt_error_from_unix_common(errno);
+ if (!composite_is_ok(c)) return;
+ }
+
+ c->status = cldap_socket_init(s, NULL, dest_address, &s->cldap.sock);
+ if (!composite_is_ok(c)) return;
+
+ req = cldap_netlogon_send(s, s->libnet->event_ctx,
+ s->cldap.sock, &s->cldap.io);
+ if (composite_nomem(req, c)) return;
+ tevent_req_set_callback(req, unbecomeDC_recv_cldap, s);
+}
+
+static void unbecomeDC_connect_ldap(struct libnet_UnbecomeDC_state *s);
+
+static void unbecomeDC_recv_cldap(struct tevent_req *req)
+{
+ struct libnet_UnbecomeDC_state *s = tevent_req_callback_data(req,
+ struct libnet_UnbecomeDC_state);
+ struct composite_context *c = s->creq;
+
+ c->status = cldap_netlogon_recv(req, s, &s->cldap.io);
+ talloc_free(req);
+ if (!composite_is_ok(c)) return;
+
+ s->cldap.netlogon = s->cldap.io.out.netlogon.data.nt5_ex;
+
+ s->domain.dns_name = s->cldap.netlogon.dns_domain;
+ s->domain.netbios_name = s->cldap.netlogon.domain_name;
+ s->domain.guid = s->cldap.netlogon.domain_uuid;
+
+ s->source_dsa.dns_name = s->cldap.netlogon.pdc_dns_name;
+ s->source_dsa.netbios_name = s->cldap.netlogon.pdc_name;
+ s->source_dsa.site_name = s->cldap.netlogon.server_site;
+
+ s->dest_dsa.site_name = s->cldap.netlogon.client_site;
+
+ unbecomeDC_connect_ldap(s);
+}
+
+static NTSTATUS unbecomeDC_ldap_connect(struct libnet_UnbecomeDC_state *s)
+{
+ char *url;
+
+ url = talloc_asprintf(s, "ldap://%s/", s->source_dsa.dns_name);
+ NT_STATUS_HAVE_NO_MEMORY(url);
+
+ s->ldap.ldb = ldb_wrap_connect(s, s->libnet->event_ctx, s->libnet->lp_ctx, url,
+ NULL,
+ s->libnet->cred,
+ 0);
+ talloc_free(url);
+ if (s->ldap.ldb == NULL) {
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS unbecomeDC_ldap_rootdse(struct libnet_UnbecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ static const char *attrs[] = {
+ "defaultNamingContext",
+ "configurationNamingContext",
+ NULL
+ };
+
+ basedn = ldb_dn_new(s, s->ldap.ldb, NULL);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap.ldb, s, &r, basedn, LDB_SCOPE_BASE, attrs,
+ "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->domain.dn_str = ldb_msg_find_attr_as_string(r->msgs[0], "defaultNamingContext", NULL);
+ if (!s->domain.dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ talloc_steal(s, s->domain.dn_str);
+
+ s->forest.config_dn_str = ldb_msg_find_attr_as_string(r->msgs[0], "configurationNamingContext", NULL);
+ if (!s->forest.config_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ talloc_steal(s, s->forest.config_dn_str);
+
+ s->dest_dsa.server_dn_str = talloc_asprintf(s, "CN=%s,CN=Servers,CN=%s,CN=Sites,%s",
+ s->dest_dsa.netbios_name,
+ s->dest_dsa.site_name,
+ s->forest.config_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(s->dest_dsa.server_dn_str);
+
+ talloc_free(r);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS unbecomeDC_ldap_computer_object(struct libnet_UnbecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ static const char *attrs[] = {
+ "distinguishedName",
+ "userAccountControl",
+ NULL
+ };
+
+ basedn = ldb_dn_new(s, s->ldap.ldb, s->domain.dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap.ldb, s, &r, basedn, LDB_SCOPE_SUBTREE, attrs,
+ "(&(|(objectClass=user)(objectClass=computer))(sAMAccountName=%s$))",
+ s->dest_dsa.netbios_name);
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->dest_dsa.computer_dn_str = ldb_msg_find_attr_as_string(r->msgs[0], "distinguishedName", NULL);
+ if (!s->dest_dsa.computer_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ talloc_steal(s, s->dest_dsa.computer_dn_str);
+
+ s->dest_dsa.user_account_control = ldb_msg_find_attr_as_uint(r->msgs[0], "userAccountControl", 0);
+
+ talloc_free(r);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS unbecomeDC_ldap_modify_computer(struct libnet_UnbecomeDC_state *s)
+{
+ int ret;
+ struct ldb_message *msg;
+ uint32_t user_account_control = UF_WORKSTATION_TRUST_ACCOUNT;
+ unsigned int i;
+
+ /* as the value is already as we want it to be, we're done */
+ if (s->dest_dsa.user_account_control == user_account_control) {
+ return NT_STATUS_OK;
+ }
+
+ /* make a 'modify' msg, and only for serverReference */
+ msg = ldb_msg_new(s);
+ NT_STATUS_HAVE_NO_MEMORY(msg);
+ msg->dn = ldb_dn_new(msg, s->ldap.ldb, s->dest_dsa.computer_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(msg->dn);
+
+ ret = samdb_msg_add_uint(s->ldap.ldb, msg, msg, "userAccountControl",
+ user_account_control);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* mark all the message elements (should be just one)
+ as LDB_FLAG_MOD_REPLACE */
+ for (i=0;i<msg->num_elements;i++) {
+ msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ ret = ldb_modify(s->ldap.ldb, msg);
+ talloc_free(msg);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ }
+
+ s->dest_dsa.user_account_control = user_account_control;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS unbecomeDC_ldap_move_computer(struct libnet_UnbecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ struct ldb_dn *old_dn;
+ struct ldb_dn *new_dn;
+ static const char *_1_1_attrs[] = {
+ "1.1",
+ NULL
+ };
+
+ basedn = ldb_dn_new_fmt(s, s->ldap.ldb, "<WKGUID=aa312825768811d1aded00c04fd8d5cd,%s>",
+ s->domain.dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap.ldb, s, &r, basedn, LDB_SCOPE_BASE,
+ _1_1_attrs, "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ old_dn = ldb_dn_new(r, s->ldap.ldb, s->dest_dsa.computer_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(old_dn);
+
+ new_dn = r->msgs[0]->dn;
+
+ if (!ldb_dn_add_child_fmt(new_dn, "CN=%s", s->dest_dsa.netbios_name)) {
+ talloc_free(r);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (ldb_dn_compare(old_dn, new_dn) == 0) {
+ /* we don't need to rename if the old and new dn match */
+ talloc_free(r);
+ return NT_STATUS_OK;
+ }
+
+ ret = ldb_rename(s->ldap.ldb, old_dn, new_dn);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(r);
+ return NT_STATUS_LDAP(ret);
+ }
+
+ s->dest_dsa.computer_dn_str = ldb_dn_alloc_linearized(s, new_dn);
+ NT_STATUS_HAVE_NO_MEMORY(s->dest_dsa.computer_dn_str);
+
+ talloc_free(r);
+
+ return NT_STATUS_OK;
+}
+
+static void unbecomeDC_drsuapi_connect_send(struct libnet_UnbecomeDC_state *s);
+
+static void unbecomeDC_connect_ldap(struct libnet_UnbecomeDC_state *s)
+{
+ struct composite_context *c = s->creq;
+
+ c->status = unbecomeDC_ldap_connect(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = unbecomeDC_ldap_rootdse(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = unbecomeDC_ldap_computer_object(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = unbecomeDC_ldap_modify_computer(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = unbecomeDC_ldap_move_computer(s);
+ if (!composite_is_ok(c)) return;
+
+ unbecomeDC_drsuapi_connect_send(s);
+}
+
+static void unbecomeDC_drsuapi_connect_recv(struct composite_context *creq);
+
+static void unbecomeDC_drsuapi_connect_send(struct libnet_UnbecomeDC_state *s)
+{
+ struct composite_context *c = s->creq;
+ struct composite_context *creq;
+ char *binding_str;
+
+ binding_str = talloc_asprintf(s, "ncacn_ip_tcp:%s[seal,target_hostname=%s]",
+ s->source_dsa.address,
+ s->source_dsa.dns_name);
+ if (composite_nomem(binding_str, c)) return;
+
+ c->status = dcerpc_parse_binding(s, binding_str, &s->drsuapi.binding);
+ talloc_free(binding_str);
+ if (!composite_is_ok(c)) return;
+
+ if (DEBUGLEVEL >= 10) {
+ c->status = dcerpc_binding_set_flags(s->drsuapi.binding,
+ DCERPC_DEBUG_PRINT_BOTH,
+ 0);
+ if (!composite_is_ok(c)) return;
+ }
+
+ creq = dcerpc_pipe_connect_b_send(s, s->drsuapi.binding, &ndr_table_drsuapi,
+ s->libnet->cred, s->libnet->event_ctx,
+ s->libnet->lp_ctx);
+ composite_continue(c, creq, unbecomeDC_drsuapi_connect_recv, s);
+}
+
+static void unbecomeDC_drsuapi_bind_send(struct libnet_UnbecomeDC_state *s);
+
+static void unbecomeDC_drsuapi_connect_recv(struct composite_context *req)
+{
+ struct libnet_UnbecomeDC_state *s = talloc_get_type(req->async.private_data,
+ struct libnet_UnbecomeDC_state);
+ struct composite_context *c = s->creq;
+
+ c->status = dcerpc_pipe_connect_b_recv(req, s, &s->drsuapi.pipe);
+ if (!composite_is_ok(c)) return;
+
+ s->drsuapi.drsuapi_handle = s->drsuapi.pipe->binding_handle;
+
+ unbecomeDC_drsuapi_bind_send(s);
+}
+
+static void unbecomeDC_drsuapi_bind_recv(struct tevent_req *subreq);
+
+static void unbecomeDC_drsuapi_bind_send(struct libnet_UnbecomeDC_state *s)
+{
+ struct composite_context *c = s->creq;
+ struct drsuapi_DsBindInfo28 *bind_info28;
+ struct tevent_req *subreq;
+
+ GUID_from_string(DRSUAPI_DS_BIND_GUID, &s->drsuapi.bind_guid);
+
+ bind_info28 = &s->drsuapi.local_info28;
+ bind_info28->supported_extensions = 0;
+ bind_info28->site_guid = GUID_zero();
+ bind_info28->pid = 0;
+ bind_info28->repl_epoch = 0;
+
+ s->drsuapi.bind_info_ctr.length = 28;
+ s->drsuapi.bind_info_ctr.info.info28 = *bind_info28;
+
+ s->drsuapi.bind_r.in.bind_guid = &s->drsuapi.bind_guid;
+ s->drsuapi.bind_r.in.bind_info = &s->drsuapi.bind_info_ctr;
+ s->drsuapi.bind_r.out.bind_handle = &s->drsuapi.bind_handle;
+
+ subreq = dcerpc_drsuapi_DsBind_r_send(s, c->event_ctx,
+ s->drsuapi.drsuapi_handle,
+ &s->drsuapi.bind_r);
+ if (composite_nomem(subreq, c)) return;
+ tevent_req_set_callback(subreq, unbecomeDC_drsuapi_bind_recv, s);
+}
+
+static void unbecomeDC_drsuapi_remove_ds_server_send(struct libnet_UnbecomeDC_state *s);
+
+static void unbecomeDC_drsuapi_bind_recv(struct tevent_req *subreq)
+{
+ struct libnet_UnbecomeDC_state *s = tevent_req_callback_data(subreq,
+ struct libnet_UnbecomeDC_state);
+ struct composite_context *c = s->creq;
+
+ c->status = dcerpc_drsuapi_DsBind_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ if (!W_ERROR_IS_OK(s->drsuapi.bind_r.out.result)) {
+ composite_error(c, werror_to_ntstatus(s->drsuapi.bind_r.out.result));
+ return;
+ }
+
+ ZERO_STRUCT(s->drsuapi.remote_info28);
+ if (s->drsuapi.bind_r.out.bind_info) {
+ switch (s->drsuapi.bind_r.out.bind_info->length) {
+ case 24: {
+ struct drsuapi_DsBindInfo24 *info24;
+ info24 = &s->drsuapi.bind_r.out.bind_info->info.info24;
+ s->drsuapi.remote_info28.supported_extensions = info24->supported_extensions;
+ s->drsuapi.remote_info28.site_guid = info24->site_guid;
+ s->drsuapi.remote_info28.pid = info24->pid;
+ s->drsuapi.remote_info28.repl_epoch = 0;
+ break;
+ }
+ case 28: {
+ s->drsuapi.remote_info28 = s->drsuapi.bind_r.out.bind_info->info.info28;
+ break;
+ }
+ case 32: {
+ struct drsuapi_DsBindInfo32 *info32;
+ info32 = &s->drsuapi.bind_r.out.bind_info->info.info32;
+ s->drsuapi.remote_info28.supported_extensions = info32->supported_extensions;
+ s->drsuapi.remote_info28.site_guid = info32->site_guid;
+ s->drsuapi.remote_info28.pid = info32->pid;
+ s->drsuapi.remote_info28.repl_epoch = info32->repl_epoch;
+ break;
+ }
+ case 48: {
+ struct drsuapi_DsBindInfo48 *info48;
+ info48 = &s->drsuapi.bind_r.out.bind_info->info.info48;
+ s->drsuapi.remote_info28.supported_extensions = info48->supported_extensions;
+ s->drsuapi.remote_info28.site_guid = info48->site_guid;
+ s->drsuapi.remote_info28.pid = info48->pid;
+ s->drsuapi.remote_info28.repl_epoch = info48->repl_epoch;
+ break;
+ }
+ case 52: {
+ struct drsuapi_DsBindInfo52 *info52;
+ info52 = &s->drsuapi.bind_r.out.bind_info->info.info52;
+ s->drsuapi.remote_info28.supported_extensions = info52->supported_extensions;
+ s->drsuapi.remote_info28.site_guid = info52->site_guid;
+ s->drsuapi.remote_info28.pid = info52->pid;
+ s->drsuapi.remote_info28.repl_epoch = info52->repl_epoch;
+ break;
+ }
+ default:
+ DEBUG(1, ("Warning: invalid info length in bind info: %d\n",
+ s->drsuapi.bind_r.out.bind_info->length));
+ break;
+ }
+ }
+
+ unbecomeDC_drsuapi_remove_ds_server_send(s);
+}
+
+static void unbecomeDC_drsuapi_remove_ds_server_recv(struct tevent_req *subreq);
+
+static void unbecomeDC_drsuapi_remove_ds_server_send(struct libnet_UnbecomeDC_state *s)
+{
+ struct composite_context *c = s->creq;
+ struct drsuapi_DsRemoveDSServer *r = &s->drsuapi.rm_ds_srv_r;
+ struct tevent_req *subreq;
+
+ r->in.bind_handle = &s->drsuapi.bind_handle;
+ r->in.level = 1;
+ r->in.req = talloc(s, union drsuapi_DsRemoveDSServerRequest);
+ r->in.req->req1.server_dn = s->dest_dsa.server_dn_str;
+ r->in.req->req1.domain_dn = s->domain.dn_str;
+ r->in.req->req1.commit = true;
+
+ r->out.level_out = talloc(s, uint32_t);
+ r->out.res = talloc(s, union drsuapi_DsRemoveDSServerResult);
+
+ subreq = dcerpc_drsuapi_DsRemoveDSServer_r_send(s, c->event_ctx,
+ s->drsuapi.drsuapi_handle,
+ r);
+ if (composite_nomem(subreq, c)) return;
+ tevent_req_set_callback(subreq, unbecomeDC_drsuapi_remove_ds_server_recv, s);
+}
+
+static void unbecomeDC_drsuapi_remove_ds_server_recv(struct tevent_req *subreq)
+{
+ struct libnet_UnbecomeDC_state *s = tevent_req_callback_data(subreq,
+ struct libnet_UnbecomeDC_state);
+ struct composite_context *c = s->creq;
+ struct drsuapi_DsRemoveDSServer *r = &s->drsuapi.rm_ds_srv_r;
+
+ c->status = dcerpc_drsuapi_DsRemoveDSServer_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ if (!W_ERROR_IS_OK(r->out.result)) {
+ composite_error(c, werror_to_ntstatus(r->out.result));
+ return;
+ }
+
+ if (*r->out.level_out != 1) {
+ composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ composite_done(c);
+}
+
+struct composite_context *libnet_UnbecomeDC_send(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_UnbecomeDC *r)
+{
+ struct composite_context *c;
+ struct libnet_UnbecomeDC_state *s;
+ char *tmp_name;
+
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct libnet_UnbecomeDC_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+ s->creq = c;
+ s->libnet = ctx;
+
+ /* Domain input */
+ s->domain.dns_name = talloc_strdup(s, r->in.domain_dns_name);
+ if (composite_nomem(s->domain.dns_name, c)) return c;
+ s->domain.netbios_name = talloc_strdup(s, r->in.domain_netbios_name);
+ if (composite_nomem(s->domain.netbios_name, c)) return c;
+
+ /* Source DSA input */
+ s->source_dsa.address = talloc_strdup(s, r->in.source_dsa_address);
+ if (composite_nomem(s->source_dsa.address, c)) return c;
+
+ /* Destination DSA input */
+ s->dest_dsa.netbios_name= talloc_strdup(s, r->in.dest_dsa_netbios_name);
+ if (composite_nomem(s->dest_dsa.netbios_name, c)) return c;
+
+ /* Destination DSA dns_name construction */
+ tmp_name = strlower_talloc(s, s->dest_dsa.netbios_name);
+ if (composite_nomem(tmp_name, c)) return c;
+ s->dest_dsa.dns_name = talloc_asprintf_append_buffer(tmp_name, ".%s",
+ s->domain.dns_name);
+ if (composite_nomem(s->dest_dsa.dns_name, c)) return c;
+
+ unbecomeDC_send_cldap(s);
+ return c;
+}
+
+NTSTATUS libnet_UnbecomeDC_recv(struct composite_context *c, TALLOC_CTX *mem_ctx, struct libnet_UnbecomeDC *r)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ ZERO_STRUCT(r->out);
+
+ talloc_free(c);
+ return status;
+}
+
+NTSTATUS libnet_UnbecomeDC(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_UnbecomeDC *r)
+{
+ NTSTATUS status;
+ struct composite_context *c;
+ c = libnet_UnbecomeDC_send(ctx, mem_ctx, r);
+ status = libnet_UnbecomeDC_recv(c, mem_ctx, r);
+ return status;
+}
diff --git a/source4/libnet/libnet_unbecome_dc.h b/source4/libnet/libnet_unbecome_dc.h
new file mode 100644
index 0000000..30a412f
--- /dev/null
+++ b/source4/libnet/libnet_unbecome_dc.h
@@ -0,0 +1,31 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 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/>.
+*/
+
+struct libnet_UnbecomeDC {
+ struct {
+ const char *domain_dns_name;
+ const char *domain_netbios_name;
+ const char *source_dsa_address;
+ const char *dest_dsa_netbios_name;
+ } in;
+
+ struct {
+ const char *error_string;
+ } out;
+};
diff --git a/source4/libnet/libnet_user.c b/source4/libnet/libnet_user.c
new file mode 100644
index 0000000..f4b90d9
--- /dev/null
+++ b/source4/libnet/libnet_user.c
@@ -0,0 +1,1241 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Rafal Szczesniak 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 "libnet/libnet.h"
+#include "libcli/composite/composite.h"
+#include "auth/credentials/credentials.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/samr.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "librpc/gen_ndr/lsa.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "libcli/security/security.h"
+
+
+struct create_user_state {
+ struct libnet_CreateUser r;
+ struct libnet_DomainOpen domain_open;
+ struct libnet_rpc_useradd user_add;
+ struct libnet_context *ctx;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static void continue_rpc_useradd(struct composite_context *ctx);
+static void continue_domain_open_create(struct composite_context *ctx);
+
+
+/**
+ * Sends request to create user account
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r pointer to a structure containing arguments and results of this call
+ * @param monitor function pointer for receiving monitor messages
+ * @return compostite context of this request
+ */
+struct composite_context* libnet_CreateUser_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_CreateUser *r,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct create_user_state *s;
+ struct composite_context *create_req;
+ bool prereq_met = false;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct create_user_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ /* store arguments in the state structure */
+ s->ctx = ctx;
+ s->r = *r;
+ ZERO_STRUCT(s->r.out);
+
+ /* prerequisite: make sure the domain is opened */
+ prereq_met = samr_domain_opened(ctx, c, s->r.in.domain_name, &c, &s->domain_open,
+ continue_domain_open_create, monitor);
+ if (!prereq_met) return c;
+
+ /* prepare arguments for useradd call */
+ s->user_add.in.username = r->in.user_name;
+ s->user_add.in.domain_handle = ctx->samr.handle;
+
+ /* send the request */
+ create_req = libnet_rpc_useradd_send(s, s->ctx->event_ctx,
+ ctx->samr.samr_handle,
+ &s->user_add, monitor);
+ if (composite_nomem(create_req, c)) return c;
+
+ /* set the next stage */
+ composite_continue(c, create_req, continue_rpc_useradd, c);
+ return c;
+}
+
+
+/*
+ * Stage 0.5 (optional): receive result of domain open request
+ * and send useradd request
+ */
+static void continue_domain_open_create(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct create_user_state *s;
+ struct composite_context *create_req;
+ struct monitor_msg msg;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct create_user_state);
+
+ /* receive result of DomainOpen call */
+ c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domain_open);
+ if (!composite_is_ok(c)) return;
+
+ /* send monitor message */
+ if (s->monitor_fn) s->monitor_fn(&msg);
+
+ /* prepare arguments for useradd call */
+ s->user_add.in.username = s->r.in.user_name;
+ s->user_add.in.domain_handle = s->ctx->samr.handle;
+
+ /* send the request */
+ create_req = libnet_rpc_useradd_send(s, s->ctx->event_ctx,
+ s->ctx->samr.samr_handle,
+ &s->user_add, s->monitor_fn);
+ if (composite_nomem(create_req, c)) return;
+
+ /* set the next stage */
+ composite_continue(c, create_req, continue_rpc_useradd, c);
+}
+
+
+/*
+ * Stage 1: receive result of useradd call
+ */
+static void continue_rpc_useradd(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct create_user_state *s;
+ struct monitor_msg msg;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct create_user_state);
+
+ /* receive result of the call */
+ c->status = libnet_rpc_useradd_recv(ctx, c, &s->user_add);
+ if (!composite_is_ok(c)) return;
+
+ /* send monitor message */
+ if (s->monitor_fn) s->monitor_fn(&msg);
+
+ /* we're done */
+ composite_done(c);
+}
+
+
+/**
+ * Receive result of CreateUser call
+ *
+ * @param c composite context returned by send request routine
+ * @param mem_ctx memory context of this call
+ * @param r pointer to a structure containing arguments and result of this call
+ * @return nt status
+ */
+NTSTATUS libnet_CreateUser_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_CreateUser *r)
+{
+ NTSTATUS status;
+
+ r->out.error_string = NULL;
+
+ /* wait for result of async request and check status code */
+ status = composite_wait(c);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_strdup(mem_ctx, nt_errstr(status));
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Synchronous version of CreateUser call
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r pointer to a structure containing arguments and result of this call
+ * @return nt status
+ */
+NTSTATUS libnet_CreateUser(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_CreateUser *r)
+{
+ struct composite_context *c;
+
+ c = libnet_CreateUser_send(ctx, mem_ctx, r, NULL);
+ return libnet_CreateUser_recv(c, mem_ctx, r);
+}
+
+
+struct delete_user_state {
+ struct libnet_DeleteUser r;
+ struct libnet_context *ctx;
+ struct libnet_DomainOpen domain_open;
+ struct libnet_rpc_userdel user_del;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static void continue_rpc_userdel(struct composite_context *ctx);
+static void continue_domain_open_delete(struct composite_context *ctx);
+
+
+/**
+ * Sends request to delete user account
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r pointer to structure containing arguments and result of this call
+ * @param monitor function pointer for receiving monitor messages
+ */
+struct composite_context *libnet_DeleteUser_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_DeleteUser *r,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct delete_user_state *s;
+ struct composite_context *delete_req;
+ bool prereq_met = false;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct delete_user_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ /* store arguments in state structure */
+ s->ctx = ctx;
+ s->r = *r;
+ ZERO_STRUCT(s->r.out);
+
+ /* prerequisite: make sure the domain is opened before proceeding */
+ prereq_met = samr_domain_opened(ctx, c, s->r.in.domain_name, &c, &s->domain_open,
+ continue_domain_open_delete, monitor);
+ if (!prereq_met) return c;
+
+ /* prepare arguments for userdel call */
+ s->user_del.in.username = r->in.user_name;
+ s->user_del.in.domain_handle = ctx->samr.handle;
+
+ /* send request */
+ delete_req = libnet_rpc_userdel_send(s, s->ctx->event_ctx,
+ ctx->samr.samr_handle,
+ &s->user_del, monitor);
+ if (composite_nomem(delete_req, c)) return c;
+
+ /* set the next stage */
+ composite_continue(c, delete_req, continue_rpc_userdel, c);
+ return c;
+}
+
+
+/*
+ * Stage 0.5 (optional): receive result of domain open request
+ * and send useradd request
+ */
+static void continue_domain_open_delete(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct delete_user_state *s;
+ struct composite_context *delete_req;
+ struct monitor_msg msg;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct delete_user_state);
+
+ /* receive result of DomainOpen call */
+ c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domain_open);
+ if (!composite_is_ok(c)) return;
+
+ /* send monitor message */
+ if (s->monitor_fn) s->monitor_fn(&msg);
+
+ /* prepare arguments for userdel call */
+ s->user_del.in.username = s->r.in.user_name;
+ s->user_del.in.domain_handle = s->ctx->samr.handle;
+
+ /* send request */
+ delete_req = libnet_rpc_userdel_send(s, s->ctx->event_ctx,
+ s->ctx->samr.samr_handle,
+ &s->user_del, s->monitor_fn);
+ if (composite_nomem(delete_req, c)) return;
+
+ /* set the next stage */
+ composite_continue(c, delete_req, continue_rpc_userdel, c);
+}
+
+
+/*
+ * Stage 1: receive result of userdel call and finish the composite function
+ */
+static void continue_rpc_userdel(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct delete_user_state *s;
+ struct monitor_msg msg;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct delete_user_state);
+
+ /* receive result of userdel call */
+ c->status = libnet_rpc_userdel_recv(ctx, c, &s->user_del);
+ if (!composite_is_ok(c)) return;
+
+ /* send monitor message */
+ if (s->monitor_fn) s->monitor_fn(&msg);
+
+ /* we're done */
+ composite_done(c);
+}
+
+
+/**
+ * Receives result of asynchronous DeleteUser call
+ *
+ * @param c composite context returned by async DeleteUser call
+ * @param mem_ctx memory context of this call
+ * @param r pointer to structure containing arguments and result
+ */
+NTSTATUS libnet_DeleteUser_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_DeleteUser *r)
+{
+ NTSTATUS status;
+ struct delete_user_state *s;
+
+ r->out.error_string = NULL;
+
+ /* wait for result of async request and check status code */
+ status = composite_wait(c);
+ if (!NT_STATUS_IS_OK(status)) {
+ s = talloc_get_type_abort(c->private_data, struct delete_user_state);
+ r->out.error_string = talloc_steal(mem_ctx, s->r.out.error_string);
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Synchronous version of DeleteUser call
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r pointer to structure containing arguments and result
+ */
+NTSTATUS libnet_DeleteUser(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_DeleteUser *r)
+{
+ struct composite_context *c;
+
+ c = libnet_DeleteUser_send(ctx, mem_ctx, r, NULL);
+ return libnet_DeleteUser_recv(c, mem_ctx, r);
+}
+
+
+struct modify_user_state {
+ struct libnet_ModifyUser r;
+ struct libnet_context *ctx;
+ struct libnet_DomainOpen domain_open;
+ struct libnet_rpc_userinfo user_info;
+ struct libnet_rpc_usermod user_mod;
+
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static void continue_rpc_usermod(struct composite_context *ctx);
+static void continue_domain_open_modify(struct composite_context *ctx);
+static NTSTATUS set_user_changes(TALLOC_CTX *mem_ctx, struct usermod_change *mod,
+ struct libnet_rpc_userinfo *info, struct libnet_ModifyUser *r);
+static void continue_rpc_userinfo(struct composite_context *ctx);
+
+
+/**
+ * Sends request to modify user account
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r pointer to structure containing arguments and result of this call
+ * @param monitor function pointer for receiving monitor messages
+ */
+struct composite_context *libnet_ModifyUser_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_ModifyUser *r,
+ void (*monitor)(struct monitor_msg*))
+{
+ const uint16_t level = 21;
+ struct composite_context *c;
+ struct modify_user_state *s;
+ struct composite_context *userinfo_req;
+ bool prereq_met = false;
+
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct modify_user_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ s->ctx = ctx;
+ s->r = *r;
+
+ prereq_met = samr_domain_opened(ctx, c, s->r.in.domain_name, &c, &s->domain_open,
+ continue_domain_open_modify, monitor);
+ if (!prereq_met) return c;
+
+ s->user_info.in.username = r->in.user_name;
+ s->user_info.in.domain_handle = ctx->samr.handle;
+ s->user_info.in.level = level;
+
+ userinfo_req = libnet_rpc_userinfo_send(s, s->ctx->event_ctx,
+ ctx->samr.samr_handle,
+ &s->user_info, monitor);
+ if (composite_nomem(userinfo_req, c)) return c;
+
+ composite_continue(c, userinfo_req, continue_rpc_userinfo, c);
+ return c;
+}
+
+
+/*
+ * Stage 0.5 (optional): receive result of domain open request
+ * and send userinfo request
+ */
+static void continue_domain_open_modify(struct composite_context *ctx)
+{
+ const uint16_t level = 21;
+ struct composite_context *c;
+ struct modify_user_state *s;
+ struct composite_context *userinfo_req;
+ struct monitor_msg msg;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct modify_user_state);
+
+ c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domain_open);
+ if (!composite_is_ok(c)) return;
+
+ if (s->monitor_fn) s->monitor_fn(&msg);
+
+ s->user_info.in.domain_handle = s->ctx->samr.handle;
+ s->user_info.in.username = s->r.in.user_name;
+ s->user_info.in.level = level;
+
+ userinfo_req = libnet_rpc_userinfo_send(s, s->ctx->event_ctx,
+ s->ctx->samr.samr_handle,
+ &s->user_info, s->monitor_fn);
+ if (composite_nomem(userinfo_req, c)) return;
+
+ composite_continue(c, userinfo_req, continue_rpc_userinfo, c);
+}
+
+
+/*
+ * Stage 1: receive result of userinfo call, prepare user changes
+ * (set the fields a caller required to change) and send usermod request
+ */
+static void continue_rpc_userinfo(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct modify_user_state *s;
+ struct composite_context *usermod_req;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct modify_user_state);
+
+ c->status = libnet_rpc_userinfo_recv(ctx, c, &s->user_info);
+ if (!composite_is_ok(c)) return;
+
+ s->user_mod.in.domain_handle = s->ctx->samr.handle;
+ s->user_mod.in.username = s->r.in.user_name;
+
+ c->status = set_user_changes(c, &s->user_mod.in.change, &s->user_info, &s->r);
+
+ usermod_req = libnet_rpc_usermod_send(s, s->ctx->event_ctx,
+ s->ctx->samr.samr_handle,
+ &s->user_mod, s->monitor_fn);
+ if (composite_nomem(usermod_req, c)) return;
+
+ composite_continue(c, usermod_req, continue_rpc_usermod, c);
+}
+
+
+/*
+ * Prepare user changes: compare userinfo result to requested changes and
+ * set the field values and flags accordingly for user modify call
+ */
+static NTSTATUS set_user_changes(TALLOC_CTX *mem_ctx, struct usermod_change *mod,
+ struct libnet_rpc_userinfo *info, struct libnet_ModifyUser *r)
+{
+ struct samr_UserInfo21 *user;
+
+ if (mod == NULL || info == NULL || r == NULL || info->in.level != 21) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ user = &info->out.info.info21;
+ mod->fields = 0; /* reset flag field before setting individual flags */
+
+ /* account name change */
+ SET_FIELD_LSA_STRING(r->in, user, mod, account_name, USERMOD_FIELD_ACCOUNT_NAME);
+
+ /* full name change */
+ SET_FIELD_LSA_STRING(r->in, user, mod, full_name, USERMOD_FIELD_FULL_NAME);
+
+ /* description change */
+ SET_FIELD_LSA_STRING(r->in, user, mod, description, USERMOD_FIELD_DESCRIPTION);
+
+ /* comment change */
+ SET_FIELD_LSA_STRING(r->in, user, mod, comment, USERMOD_FIELD_COMMENT);
+
+ /* home directory change */
+ SET_FIELD_LSA_STRING(r->in, user, mod, home_directory, USERMOD_FIELD_HOME_DIRECTORY);
+
+ /* home drive change */
+ SET_FIELD_LSA_STRING(r->in, user, mod, home_drive, USERMOD_FIELD_HOME_DRIVE);
+
+ /* logon script change */
+ SET_FIELD_LSA_STRING(r->in, user, mod, logon_script, USERMOD_FIELD_LOGON_SCRIPT);
+
+ /* profile path change */
+ SET_FIELD_LSA_STRING(r->in, user, mod, profile_path, USERMOD_FIELD_PROFILE_PATH);
+
+ /* account expiry change */
+ SET_FIELD_NTTIME(r->in, user, mod, acct_expiry, USERMOD_FIELD_ACCT_EXPIRY);
+
+ /* account flags change */
+ SET_FIELD_ACCT_FLAGS(r->in, user, mod, acct_flags, USERMOD_FIELD_ACCT_FLAGS);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ * Stage 2: receive result of usermod request and finish the composite function
+ */
+static void continue_rpc_usermod(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct modify_user_state *s;
+ struct monitor_msg msg;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct modify_user_state);
+
+ c->status = libnet_rpc_usermod_recv(ctx, c, &s->user_mod);
+ if (!composite_is_ok(c)) return;
+
+ if (s->monitor_fn) s->monitor_fn(&msg);
+ composite_done(c);
+}
+
+
+/**
+ * Receive result of ModifyUser call
+ *
+ * @param c composite context returned by send request routine
+ * @param mem_ctx memory context of this call
+ * @param r pointer to a structure containing arguments and result of this call
+ * @return nt status
+ */
+NTSTATUS libnet_ModifyUser_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_ModifyUser *r)
+{
+ NTSTATUS status = composite_wait(c);
+
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Synchronous version of ModifyUser call
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r pointer to a structure containing arguments and result of this call
+ * @return nt status
+ */
+NTSTATUS libnet_ModifyUser(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_ModifyUser *r)
+{
+ struct composite_context *c;
+
+ c = libnet_ModifyUser_send(ctx, mem_ctx, r, NULL);
+ return libnet_ModifyUser_recv(c, mem_ctx, r);
+}
+
+
+struct user_info_state {
+ struct libnet_context *ctx;
+ const char *domain_name;
+ enum libnet_UserInfo_level level;
+ const char *user_name;
+ const char *sid_string;
+ struct libnet_LookupName lookup;
+ struct libnet_DomainOpen domopen;
+ struct libnet_rpc_userinfo userinfo;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static void continue_name_found(struct composite_context *ctx);
+static void continue_domain_open_info(struct composite_context *ctx);
+static void continue_info_received(struct composite_context *ctx);
+
+
+/**
+ * Sends request to get user account information
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r pointer to a structure containing arguments and results of this call
+ * @param monitor function pointer for receiving monitor messages
+ * @return compostite context of this request
+ */
+struct composite_context* libnet_UserInfo_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_UserInfo *r,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct user_info_state *s;
+ struct composite_context *lookup_req, *info_req;
+ bool prereq_met = false;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct user_info_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ /* store arguments in the state structure */
+ s->monitor_fn = monitor;
+ s->ctx = ctx;
+ s->domain_name = talloc_strdup(c, r->in.domain_name);
+ s->level = r->in.level;
+ switch (s->level) {
+ case USER_INFO_BY_NAME:
+ s->user_name = talloc_strdup(c, r->in.data.user_name);
+ s->sid_string = NULL;
+ break;
+ case USER_INFO_BY_SID:
+ s->user_name = NULL;
+ s->sid_string = dom_sid_string(c, r->in.data.user_sid);
+ break;
+ }
+
+ /* prerequisite: make sure the domain is opened */
+ prereq_met = samr_domain_opened(ctx, c, s->domain_name, &c, &s->domopen,
+ continue_domain_open_info, monitor);
+ if (!prereq_met) return c;
+
+ switch (s->level) {
+ case USER_INFO_BY_NAME:
+ /* prepare arguments for LookupName call */
+ s->lookup.in.domain_name = s->domain_name;
+ s->lookup.in.name = s->user_name;
+
+ /* send the request */
+ lookup_req = libnet_LookupName_send(ctx, c, &s->lookup,
+ s->monitor_fn);
+ if (composite_nomem(lookup_req, c)) return c;
+
+ /* set the next stage */
+ composite_continue(c, lookup_req, continue_name_found, c);
+ break;
+ case USER_INFO_BY_SID:
+ /* prepare arguments for UserInfo call */
+ s->userinfo.in.domain_handle = s->ctx->samr.handle;
+ s->userinfo.in.sid = s->sid_string;
+ s->userinfo.in.level = 21;
+
+ /* send the request */
+ info_req = libnet_rpc_userinfo_send(s, s->ctx->event_ctx,
+ s->ctx->samr.samr_handle,
+ &s->userinfo,
+ s->monitor_fn);
+ if (composite_nomem(info_req, c)) return c;
+
+ /* set the next stage */
+ composite_continue(c, info_req, continue_info_received, c);
+ break;
+ }
+
+ return c;
+}
+
+
+/*
+ * Stage 0.5 (optional): receive result of domain open request
+ * and send LookupName request
+ */
+static void continue_domain_open_info(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct user_info_state *s;
+ struct composite_context *lookup_req, *info_req;
+ struct monitor_msg msg;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct user_info_state);
+
+ /* receive result of DomainOpen call */
+ c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domopen);
+ if (!composite_is_ok(c)) return;
+
+ /* send monitor message */
+ if (s->monitor_fn) s->monitor_fn(&msg);
+
+ switch (s->level) {
+ case USER_INFO_BY_NAME:
+ /* prepare arguments for LookupName call */
+ s->lookup.in.domain_name = s->domain_name;
+ s->lookup.in.name = s->user_name;
+
+ /* send the request */
+ lookup_req = libnet_LookupName_send(s->ctx, c, &s->lookup, s->monitor_fn);
+ if (composite_nomem(lookup_req, c)) return;
+
+ /* set the next stage */
+ composite_continue(c, lookup_req, continue_name_found, c);
+ break;
+
+ case USER_INFO_BY_SID:
+ /* prepare arguments for UserInfo call */
+ s->userinfo.in.domain_handle = s->ctx->samr.handle;
+ s->userinfo.in.sid = s->sid_string;
+ s->userinfo.in.level = 21;
+
+ /* send the request */
+ info_req = libnet_rpc_userinfo_send(s, s->ctx->event_ctx,
+ s->ctx->samr.samr_handle,
+ &s->userinfo,
+ s->monitor_fn);
+ if (composite_nomem(info_req, c)) return;
+
+ /* set the next stage */
+ composite_continue(c, info_req, continue_info_received, c);
+ break;
+ }
+}
+
+
+/*
+ * Stage 1: receive the name (if found) and send userinfo request
+ */
+static void continue_name_found(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct user_info_state *s;
+ struct composite_context *info_req;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct user_info_state);
+
+ /* receive result of LookupName call */
+ c->status = libnet_LookupName_recv(ctx, c, &s->lookup);
+ if (!composite_is_ok(c)) return;
+
+ /* we're only interested in user accounts this time */
+ if (s->lookup.out.sid_type != SID_NAME_USER) {
+ composite_error(c, NT_STATUS_NO_SUCH_USER);
+ return;
+ }
+
+ /* prepare arguments for UserInfo call */
+ s->userinfo.in.domain_handle = s->ctx->samr.handle;
+ s->userinfo.in.sid = s->lookup.out.sidstr;
+ s->userinfo.in.level = 21;
+
+ /* send the request */
+ info_req = libnet_rpc_userinfo_send(s, s->ctx->event_ctx,
+ s->ctx->samr.samr_handle,
+ &s->userinfo, s->monitor_fn);
+ if (composite_nomem(info_req, c)) return;
+
+ /* set the next stage */
+ composite_continue(c, info_req, continue_info_received, c);
+}
+
+
+/*
+ * Stage 2: receive user account information and finish the composite function
+ */
+static void continue_info_received(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct user_info_state *s;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct user_info_state);
+
+ /* receive result of userinfo call */
+ c->status = libnet_rpc_userinfo_recv(ctx, c, &s->userinfo);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+
+/**
+ * Receive result of UserInfo call
+ *
+ * @param c composite context returned by send request routine
+ * @param mem_ctx memory context of this call
+ * @param r pointer to a structure containing arguments and result of this call
+ * @return nt status
+ */
+NTSTATUS libnet_UserInfo_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_UserInfo *r)
+{
+ NTSTATUS status;
+ struct user_info_state *s;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status) && r != NULL) {
+ struct samr_UserInfo21 *info;
+
+ s = talloc_get_type_abort(c->private_data, struct user_info_state);
+ info = &s->userinfo.out.info.info21;
+
+ r->out.user_sid = dom_sid_add_rid(mem_ctx, s->ctx->samr.sid, info->rid);
+ r->out.primary_group_sid = dom_sid_add_rid(mem_ctx, s->ctx->samr.sid, info->primary_gid);
+
+ /* string fields */
+ r->out.account_name = talloc_steal(mem_ctx, info->account_name.string);
+ r->out.full_name = talloc_steal(mem_ctx, info->full_name.string);
+ r->out.description = talloc_steal(mem_ctx, info->description.string);
+ r->out.home_directory = talloc_steal(mem_ctx, info->home_directory.string);
+ r->out.home_drive = talloc_steal(mem_ctx, info->home_drive.string);
+ r->out.comment = talloc_steal(mem_ctx, info->comment.string);
+ r->out.logon_script = talloc_steal(mem_ctx, info->logon_script.string);
+ r->out.profile_path = talloc_steal(mem_ctx, info->profile_path.string);
+
+ /* time fields (allocation) */
+ r->out.acct_expiry = talloc(mem_ctx, struct timeval);
+ r->out.allow_password_change = talloc(mem_ctx, struct timeval);
+ r->out.force_password_change = talloc(mem_ctx, struct timeval);
+ r->out.last_logon = talloc(mem_ctx, struct timeval);
+ r->out.last_logoff = talloc(mem_ctx, struct timeval);
+ r->out.last_password_change = talloc(mem_ctx, struct timeval);
+
+ /* time fields (converting) */
+ nttime_to_timeval(r->out.acct_expiry, info->acct_expiry);
+ nttime_to_timeval(r->out.allow_password_change, info->allow_password_change);
+ nttime_to_timeval(r->out.force_password_change, info->force_password_change);
+ nttime_to_timeval(r->out.last_logon, info->last_logon);
+ nttime_to_timeval(r->out.last_logoff, info->last_logoff);
+ nttime_to_timeval(r->out.last_password_change, info->last_password_change);
+
+ /* flag and number fields */
+ r->out.acct_flags = info->acct_flags;
+
+ r->out.error_string = talloc_strdup(mem_ctx, "Success");
+
+ } else {
+ r->out.error_string = talloc_asprintf(mem_ctx, "Error: %s", nt_errstr(status));
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Synchronous version of UserInfo call
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r pointer to a structure containing arguments and result of this call
+ * @return nt status
+ */
+NTSTATUS libnet_UserInfo(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_UserInfo *r)
+{
+ struct composite_context *c;
+
+ c = libnet_UserInfo_send(ctx, mem_ctx, r, NULL);
+ return libnet_UserInfo_recv(c, mem_ctx, r);
+}
+
+
+struct userlist_state {
+ struct libnet_context *ctx;
+ const char *domain_name;
+ struct lsa_DomainInfo dominfo;
+ int page_size;
+ uint32_t resume_index;
+ struct userlist *users;
+ uint32_t count;
+
+ struct libnet_DomainOpen domain_open;
+ struct lsa_QueryInfoPolicy query_domain;
+ struct samr_EnumDomainUsers user_list;
+
+ void (*monitor_fn)(struct monitor_msg*);
+};
+
+
+static void continue_lsa_domain_opened(struct composite_context *ctx);
+static void continue_domain_queried(struct tevent_req *subreq);
+static void continue_samr_domain_opened(struct composite_context *ctx);
+static void continue_users_enumerated(struct tevent_req *subreq);
+
+
+/**
+ * Sends request to list (enumerate) user accounts
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r pointer to structure containing arguments and results of this call
+ * @param monitor function pointer for receiving monitor messages
+ * @return compostite context of this request
+ */
+struct composite_context* libnet_UserList_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_UserList *r,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct userlist_state *s;
+ struct tevent_req *subreq;
+ bool prereq_met = false;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct userlist_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ /* store the arguments in the state structure */
+ s->ctx = ctx;
+ s->page_size = r->in.page_size;
+ s->resume_index = r->in.resume_index;
+ s->domain_name = talloc_strdup(c, r->in.domain_name);
+ s->monitor_fn = monitor;
+
+ /* make sure we have lsa domain handle before doing anything */
+ prereq_met = lsa_domain_opened(ctx, c, s->domain_name, &c, &s->domain_open,
+ continue_lsa_domain_opened, monitor);
+ if (!prereq_met) return c;
+
+ /* prepare arguments of QueryDomainInfo call */
+ s->query_domain.in.handle = &ctx->lsa.handle;
+ s->query_domain.in.level = LSA_POLICY_INFO_DOMAIN;
+ s->query_domain.out.info = talloc_zero(c, union lsa_PolicyInformation *);
+ if (composite_nomem(s->query_domain.out.info, c)) return c;
+
+ /* send the request */
+ subreq = dcerpc_lsa_QueryInfoPolicy_r_send(s, c->event_ctx,
+ ctx->lsa.pipe->binding_handle,
+ &s->query_domain);
+ if (composite_nomem(subreq, c)) return c;
+
+ tevent_req_set_callback(subreq, continue_domain_queried, c);
+ return c;
+}
+
+
+/*
+ * Stage 0.5 (optional): receive lsa domain handle and send
+ * request to query domain info
+ */
+static void continue_lsa_domain_opened(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct userlist_state *s;
+ struct tevent_req *subreq;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct userlist_state);
+
+ /* receive lsa domain handle */
+ c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domain_open);
+ if (!composite_is_ok(c)) return;
+
+ /* prepare arguments of QueryDomainInfo call */
+ s->query_domain.in.handle = &s->ctx->lsa.handle;
+ s->query_domain.in.level = LSA_POLICY_INFO_DOMAIN;
+ s->query_domain.out.info = talloc_zero(c, union lsa_PolicyInformation *);
+ if (composite_nomem(s->query_domain.out.info, c)) return;
+
+ /* send the request */
+ subreq = dcerpc_lsa_QueryInfoPolicy_r_send(s, c->event_ctx,
+ s->ctx->lsa.pipe->binding_handle,
+ &s->query_domain);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_domain_queried, c);
+}
+
+
+/*
+ * Stage 1: receive domain info and request to enum users,
+ * provided a valid samr handle is opened
+ */
+static void continue_domain_queried(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct userlist_state *s;
+ bool prereq_met = false;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct userlist_state);
+
+ /* receive result of rpc request */
+ c->status = dcerpc_lsa_QueryInfoPolicy_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ /* get the returned domain info */
+ s->dominfo = (*s->query_domain.out.info)->domain;
+
+ /* make sure we have samr domain handle before continuing */
+ prereq_met = samr_domain_opened(s->ctx, c, s->domain_name, &c, &s->domain_open,
+ continue_samr_domain_opened, s->monitor_fn);
+ if (!prereq_met) return;
+
+ /* prepare arguments of EnumDomainUsers call */
+ s->user_list.in.domain_handle = &s->ctx->samr.handle;
+ s->user_list.in.max_size = s->page_size;
+ s->user_list.in.resume_handle = &s->resume_index;
+ s->user_list.in.acct_flags = ACB_NORMAL;
+ s->user_list.out.resume_handle = &s->resume_index;
+ s->user_list.out.num_entries = talloc(s, uint32_t);
+ if (composite_nomem(s->user_list.out.num_entries, c)) return;
+ s->user_list.out.sam = talloc(s, struct samr_SamArray *);
+ if (composite_nomem(s->user_list.out.sam, c)) return;
+
+ /* send the request */
+ subreq = dcerpc_samr_EnumDomainUsers_r_send(s, c->event_ctx,
+ s->ctx->samr.pipe->binding_handle,
+ &s->user_list);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_users_enumerated, c);
+}
+
+
+/*
+ * Stage 1.5 (optional): receive samr domain handle
+ * and request to enumerate accounts
+ */
+static void continue_samr_domain_opened(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct userlist_state *s;
+ struct tevent_req *subreq;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct userlist_state);
+
+ /* receive samr domain handle */
+ c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domain_open);
+ if (!composite_is_ok(c)) return;
+
+ /* prepare arguments of EnumDomainUsers call */
+ s->user_list.in.domain_handle = &s->ctx->samr.handle;
+ s->user_list.in.max_size = s->page_size;
+ s->user_list.in.resume_handle = &s->resume_index;
+ s->user_list.in.acct_flags = ACB_NORMAL;
+ s->user_list.out.resume_handle = &s->resume_index;
+ s->user_list.out.sam = talloc(s, struct samr_SamArray *);
+ if (composite_nomem(s->user_list.out.sam, c)) return;
+ s->user_list.out.num_entries = talloc(s, uint32_t);
+ if (composite_nomem(s->user_list.out.num_entries, c)) return;
+
+ /* send the request */
+ subreq = dcerpc_samr_EnumDomainUsers_r_send(s, c->event_ctx,
+ s->ctx->samr.pipe->binding_handle,
+ &s->user_list);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_users_enumerated, c);
+}
+
+
+/*
+ * Stage 2: receive enumerated users and their rids
+ */
+static void continue_users_enumerated(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct userlist_state *s;
+ uint32_t i;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct userlist_state);
+
+ /* receive result of rpc request */
+ c->status = dcerpc_samr_EnumDomainUsers_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ /* get the actual status of the rpc call result
+ (instead of rpc layer status) */
+ c->status = s->user_list.out.result;
+
+ /* we're interested in status "ok" as well as two
+ enum-specific status codes */
+ if (NT_STATUS_IS_OK(c->status) ||
+ NT_STATUS_EQUAL(c->status, STATUS_MORE_ENTRIES) ||
+ NT_STATUS_EQUAL(c->status, NT_STATUS_NO_MORE_ENTRIES)) {
+
+ /* get enumerated accounts counter and resume handle (the latter allows
+ making subsequent call to continue enumeration) */
+ s->resume_index = *s->user_list.out.resume_handle;
+ s->count = *s->user_list.out.num_entries;
+
+ /* prepare returned user accounts array */
+ s->users = talloc_array(c, struct userlist, (*s->user_list.out.sam)->count);
+ if (composite_nomem(s->users, c)) return;
+
+ for (i = 0; i < (*s->user_list.out.sam)->count; i++) {
+ struct dom_sid *user_sid;
+ struct samr_SamEntry *entry = &(*s->user_list.out.sam)->entries[i];
+ struct dom_sid *domain_sid = (*s->query_domain.out.info)->domain.sid;
+
+ /* construct user sid from returned rid and queried domain sid */
+ user_sid = dom_sid_add_rid(c, domain_sid, entry->idx);
+ if (composite_nomem(user_sid, c)) return;
+
+ /* username */
+ s->users[i].username = talloc_strdup(s->users, entry->name.string);
+ if (composite_nomem(s->users[i].username, c)) return;
+
+ /* sid string */
+ s->users[i].sid = dom_sid_string(s->users, user_sid);
+ if (composite_nomem(s->users[i].sid, c)) return;
+ }
+
+ /* that's it */
+ composite_done(c);
+ return;
+
+ } else {
+ /* something went wrong */
+ composite_error(c, c->status);
+ return;
+ }
+}
+
+
+/**
+ * Receive result of UserList call
+ *
+ * @param c composite context returned by send request routine
+ * @param mem_ctx memory context of this call
+ * @param r pointer to structure containing arguments and result of this call
+ * @return nt status
+ */
+NTSTATUS libnet_UserList_recv(struct composite_context* c, TALLOC_CTX *mem_ctx,
+ struct libnet_UserList *r)
+{
+ NTSTATUS status;
+ struct userlist_state *s;
+
+ if (c == NULL || mem_ctx == NULL || r == NULL) {
+ talloc_free(c);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status) ||
+ NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
+
+ s = talloc_get_type_abort(c->private_data, struct userlist_state);
+
+ /* get results from composite context */
+ r->out.count = s->count;
+ r->out.resume_index = s->resume_index;
+ r->out.users = talloc_steal(mem_ctx, s->users);
+
+ if (NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_strdup(mem_ctx, "Success");
+ } else {
+ /* success, but we're not done yet */
+ r->out.error_string = talloc_asprintf(mem_ctx, "Success (status: %s)",
+ nt_errstr(status));
+ }
+
+ } else {
+ r->out.error_string = talloc_asprintf(mem_ctx, "Error: %s", nt_errstr(status));
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Synchronous version of UserList call
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r pointer to structure containing arguments and result of this call
+ * @return nt status
+ */
+NTSTATUS libnet_UserList(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_UserList *r)
+{
+ struct composite_context *c;
+
+ c = libnet_UserList_send(ctx, mem_ctx, r, NULL);
+ return libnet_UserList_recv(c, mem_ctx, r);
+}
diff --git a/source4/libnet/libnet_user.h b/source4/libnet/libnet_user.h
new file mode 100644
index 0000000..8203d14
--- /dev/null
+++ b/source4/libnet/libnet_user.h
@@ -0,0 +1,156 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Rafal Szczesniak <mimir@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/>.
+*/
+
+
+struct libnet_CreateUser {
+ struct {
+ const char *user_name;
+ const char *domain_name;
+ } in;
+ struct {
+ const char *error_string;
+ } out;
+};
+
+
+struct libnet_DeleteUser {
+ struct {
+ const char *user_name;
+ const char *domain_name;
+ } in;
+ struct {
+ const char *error_string;
+ } out;
+};
+
+
+struct libnet_ModifyUser {
+ struct {
+ const char *user_name;
+ const char *domain_name;
+
+ const char *account_name;
+ const char *full_name;
+ const char *description;
+ const char *home_directory;
+ const char *home_drive;
+ const char *comment;
+ const char *logon_script;
+ const char *profile_path;
+ struct timeval *acct_expiry;
+ struct timeval *allow_password_change;
+ struct timeval *force_password_change;
+ struct timeval *last_password_change;
+ uint32_t acct_flags;
+ } in;
+ struct {
+ const char *error_string;
+ } out;
+};
+
+
+#define SET_FIELD_LSA_STRING(new, current, mod, field, flag) \
+ if (new.field != NULL && \
+ !strequal_m(current->field.string, new.field)) { \
+ \
+ mod->field = talloc_strdup(mem_ctx, new.field); \
+ if (mod->field == NULL) return NT_STATUS_NO_MEMORY; \
+ \
+ mod->fields |= flag; \
+ }
+
+#define SET_FIELD_NTTIME(new, current, mod, field, flag) \
+ if (new.field != 0) { \
+ NTTIME newval = timeval_to_nttime(new.field); \
+ if (newval != current->field) { \
+ mod->field = talloc_memdup(mem_ctx, new.field, sizeof(*new.field)); \
+ if (mod->field == NULL) return NT_STATUS_NO_MEMORY; \
+ mod->fields |= flag; \
+ } \
+ }
+
+#define SET_FIELD_UINT32(new, current, mod, field, flag) \
+ if (current->field != new.field) { \
+ mod->field = new.field; \
+ mod->fields |= flag; \
+ }
+
+#define SET_FIELD_ACCT_FLAGS(new, current, mod, field, flag) \
+ if (new.field) { \
+ if (current->field != new.field) { \
+ mod->field = new.field; \
+ mod->fields |= flag; \
+ } \
+ }
+
+enum libnet_UserInfo_level {
+ USER_INFO_BY_NAME=0,
+ USER_INFO_BY_SID
+};
+
+struct libnet_UserInfo {
+ struct {
+ const char *domain_name;
+ enum libnet_UserInfo_level level;
+ union {
+ const char *user_name;
+ const struct dom_sid *user_sid;
+ } data;
+ } in;
+ struct {
+ struct dom_sid *user_sid;
+ struct dom_sid *primary_group_sid;
+ const char *account_name;
+ const char *full_name;
+ const char *description;
+ const char *home_directory;
+ const char *home_drive;
+ const char *comment;
+ const char *logon_script;
+ const char *profile_path;
+ struct timeval *acct_expiry;
+ struct timeval *allow_password_change;
+ struct timeval *force_password_change;
+ struct timeval *last_logon;
+ struct timeval *last_logoff;
+ struct timeval *last_password_change;
+ uint32_t acct_flags;
+ const char *error_string;
+ } out;
+};
+
+
+struct libnet_UserList {
+ struct {
+ const char *domain_name;
+ int page_size;
+ uint32_t resume_index;
+ } in;
+ struct {
+ int count;
+ uint32_t resume_index;
+
+ struct userlist {
+ const char *sid;
+ const char *username;
+ } *users;
+
+ const char *error_string;
+ } out;
+};
diff --git a/source4/libnet/libnet_vampire.c b/source4/libnet/libnet_vampire.c
new file mode 100644
index 0000000..73adf67
--- /dev/null
+++ b/source4/libnet/libnet_vampire.c
@@ -0,0 +1,838 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Extract the user/system database from a remote server
+
+ Copyright (C) Stefan Metzmacher 2004-2006
+ Copyright (C) Brad Henry 2005
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-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 "libnet/libnet.h"
+#include "lib/events/events.h"
+#include "dsdb/samdb/samdb.h"
+#include "../lib/util/dlinklist.h"
+#include <ldb.h>
+#include <ldb_errors.h>
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "system/time.h"
+#include "ldb_wrap.h"
+#include "auth/auth.h"
+#include "auth/credentials/credentials.h"
+#include "param/param.h"
+#include "param/provision.h"
+#include "libcli/security/security.h"
+#include "dsdb/common/util.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_DRS_REPL
+
+/*
+List of tasks vampire.py must perform:
+- Domain Join
+ - but don't write the secrets.ldb
+ - results for this should be enough to handle the provision
+- if vampire method is samsync
+ - Provision using these results
+ - do we still want to support this NT4 technology?
+- Start samsync with libnet code
+ - provision in the callback
+- Write out the secrets database, using the code from libnet_Join
+
+*/
+struct libnet_vampire_cb_state {
+ const char *netbios_name;
+ const char *domain_name;
+ const char *realm;
+ struct cli_credentials *machine_account;
+
+ /* Schema loaded from local LDIF files */
+ struct dsdb_schema *provision_schema;
+
+ /* 1st pass, with some OIDs/attribute names/class names not
+ * converted, because we may not know them yet */
+ struct dsdb_schema *self_made_schema;
+
+ /* prefixMap in LDB format, from the remote DRS server */
+ DATA_BLOB prefixmap_blob;
+ const struct dsdb_schema *schema;
+
+ struct ldb_context *ldb;
+
+ struct {
+ uint32_t object_count;
+ struct drsuapi_DsReplicaObjectListItemEx *first_object;
+ struct drsuapi_DsReplicaObjectListItemEx *last_object;
+ } schema_part;
+
+ const char *targetdir;
+
+ struct loadparm_context *lp_ctx;
+ struct tevent_context *event_ctx;
+ unsigned total_objects;
+ unsigned total_links;
+ char *last_partition;
+ const char *server_dn_str;
+};
+
+/* initialise a state structure ready for replication of chunks */
+void *libnet_vampire_replicate_init(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ struct loadparm_context *lp_ctx)
+{
+ struct libnet_vampire_cb_state *s = talloc_zero(mem_ctx, struct libnet_vampire_cb_state);
+ if (!s) {
+ return NULL;
+ }
+
+ s->ldb = samdb;
+ s->lp_ctx = lp_ctx;
+ s->provision_schema = dsdb_get_schema(s->ldb, s);
+ s->schema = s->provision_schema;
+ s->netbios_name = lpcfg_netbios_name(lp_ctx);
+ s->domain_name = lpcfg_workgroup(lp_ctx);
+ s->realm = lpcfg_realm(lp_ctx);
+
+ return s;
+}
+
+/* Caller is expected to keep supplied pointers around for the lifetime of the structure */
+void *libnet_vampire_cb_state_init(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx, struct tevent_context *event_ctx,
+ const char *netbios_name, const char *domain_name, const char *realm,
+ const char *targetdir)
+{
+ struct libnet_vampire_cb_state *s = talloc_zero(mem_ctx, struct libnet_vampire_cb_state);
+ if (!s) {
+ return NULL;
+ }
+
+ s->lp_ctx = lp_ctx;
+ s->event_ctx = event_ctx;
+ s->netbios_name = netbios_name;
+ s->domain_name = domain_name;
+ s->realm = realm;
+ s->targetdir = targetdir;
+ return s;
+}
+
+struct ldb_context *libnet_vampire_cb_ldb(struct libnet_vampire_cb_state *state)
+{
+ state = talloc_get_type_abort(state, struct libnet_vampire_cb_state);
+ return state->ldb;
+}
+
+struct loadparm_context *libnet_vampire_cb_lp_ctx(struct libnet_vampire_cb_state *state)
+{
+ state = talloc_get_type_abort(state, struct libnet_vampire_cb_state);
+ return state->lp_ctx;
+}
+
+NTSTATUS libnet_vampire_cb_prepare_db(void *private_data,
+ const struct libnet_BecomeDC_PrepareDB *p)
+{
+ struct libnet_vampire_cb_state *s = talloc_get_type(private_data, struct libnet_vampire_cb_state);
+ struct provision_settings settings;
+ struct provision_result result;
+ NTSTATUS status;
+
+ ZERO_STRUCT(settings);
+ settings.site_name = p->dest_dsa->site_name;
+ settings.root_dn_str = p->forest->root_dn_str;
+ settings.domain_dn_str = p->domain->dn_str;
+ settings.config_dn_str = p->forest->config_dn_str;
+ settings.schema_dn_str = p->forest->schema_dn_str;
+ settings.netbios_name = p->dest_dsa->netbios_name;
+ settings.realm = s->realm;
+ settings.domain = s->domain_name;
+ settings.server_dn_str = p->dest_dsa->server_dn_str;
+ settings.machine_password = generate_random_machine_password(s, 120, 120);
+ settings.targetdir = s->targetdir;
+ settings.use_ntvfs = true;
+ status = provision_bare(s, s->lp_ctx, &settings, &result);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ s->ldb = talloc_steal(s, result.samdb);
+ s->lp_ctx = talloc_reparent(talloc_parent(result.lp_ctx), s, result.lp_ctx);
+ s->provision_schema = dsdb_get_schema(s->ldb, s);
+ s->server_dn_str = talloc_steal(s, p->dest_dsa->server_dn_str);
+
+ /* wrap the entire vapire operation in a transaction. This
+ isn't just cosmetic - we use this to ensure that linked
+ attribute back links are added at the end by relying on a
+ transaction commit hook in the linked attributes module. We
+ need to do this as the order of objects coming from the
+ server is not sufficiently deterministic to know that the
+ record that a backlink needs to be created in has itself
+ been created before the object containing the forward link
+ has come over the wire */
+ if (ldb_transaction_start(s->ldb) != LDB_SUCCESS) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ return NT_STATUS_OK;
+
+
+}
+
+NTSTATUS libnet_vampire_cb_check_options(void *private_data,
+ const struct libnet_BecomeDC_CheckOptions *o)
+{
+ struct libnet_vampire_cb_state *s = talloc_get_type(private_data, struct libnet_vampire_cb_state);
+
+ DEBUG(0,("Become DC [%s] of Domain[%s]/[%s]\n",
+ s->netbios_name,
+ o->domain->netbios_name, o->domain->dns_name));
+
+ DEBUG(0,("Promotion Partner is Server[%s] from Site[%s]\n",
+ o->source_dsa->dns_name, o->source_dsa->site_name));
+
+ DEBUG(0,("Options:crossRef behavior_version[%u]\n"
+ "\tschema object_version[%u]\n"
+ "\tdomain behavior_version[%u]\n"
+ "\tdomain w2k3_update_revision[%u]\n",
+ o->forest->crossref_behavior_version,
+ o->forest->schema_object_version,
+ o->domain->behavior_version,
+ o->domain->w2k3_update_revision));
+
+ return NT_STATUS_OK;
+}
+
+static WERROR libnet_vampire_cb_apply_schema(struct libnet_vampire_cb_state *s,
+ const struct libnet_BecomeDC_StoreChunk *c)
+{
+ WERROR status;
+ struct dsdb_schema_prefixmap *pfm_remote;
+ const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
+ struct dsdb_schema *provision_schema;
+ uint32_t object_count = 0;
+ struct drsuapi_DsReplicaObjectListItemEx *first_object;
+ uint32_t linked_attributes_count;
+ struct drsuapi_DsReplicaLinkedAttribute *linked_attributes;
+ const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector;
+ struct dsdb_extended_replicated_objects *schema_objs;
+ struct repsFromTo1 *s_dsa;
+ char *tmp_dns_name;
+ struct ldb_context *schema_ldb;
+ struct ldb_dn *partition_dn;
+ struct ldb_message *msg;
+ struct ldb_message_element *prefixMap_el;
+ uint32_t i;
+ int ret;
+ bool ok;
+ uint64_t seq_num = 0;
+ uint32_t cycle_before_switching;
+
+ DEBUG(0,("Analyze and apply schema objects\n"));
+
+ s_dsa = talloc_zero(s, struct repsFromTo1);
+ if (s_dsa == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ s_dsa->other_info = talloc(s_dsa, struct repsFromTo1OtherInfo);
+ if (s_dsa->other_info == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ switch (c->ctr_level) {
+ case 1:
+ mapping_ctr = &c->ctr1->mapping_ctr;
+ object_count = s->schema_part.object_count;
+ first_object = s->schema_part.first_object;
+ linked_attributes_count = 0;
+ linked_attributes = NULL;
+ s_dsa->highwatermark = c->ctr1->new_highwatermark;
+ s_dsa->source_dsa_obj_guid = c->ctr1->source_dsa_guid;
+ s_dsa->source_dsa_invocation_id = c->ctr1->source_dsa_invocation_id;
+ uptodateness_vector = NULL; /* TODO: map it */
+ break;
+ case 6:
+ mapping_ctr = &c->ctr6->mapping_ctr;
+ object_count = s->schema_part.object_count;
+ first_object = s->schema_part.first_object;
+ linked_attributes_count = c->ctr6->linked_attributes_count;
+ linked_attributes = c->ctr6->linked_attributes;
+ s_dsa->highwatermark = c->ctr6->new_highwatermark;
+ s_dsa->source_dsa_obj_guid = c->ctr6->source_dsa_guid;
+ s_dsa->source_dsa_invocation_id = c->ctr6->source_dsa_invocation_id;
+ uptodateness_vector = c->ctr6->uptodateness_vector;
+ break;
+ default:
+ return WERR_INVALID_PARAMETER;
+ }
+ /* We must set these up to ensure the replMetaData is written
+ * correctly, before our NTDS Settings entry is replicated */
+ ok = samdb_set_ntds_invocation_id(s->ldb, &c->dest_dsa->invocation_id);
+ if (!ok) {
+ DEBUG(0,("Failed to set cached ntds invocationId\n"));
+ return WERR_INTERNAL_ERROR;
+ }
+ ok = samdb_set_ntds_objectGUID(s->ldb, &c->dest_dsa->ntds_guid);
+ if (!ok) {
+ DEBUG(0,("Failed to set cached ntds objectGUID\n"));
+ return WERR_INTERNAL_ERROR;
+ }
+
+ status = dsdb_schema_pfm_from_drsuapi_pfm(mapping_ctr, true,
+ s, &pfm_remote, NULL);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,(__location__ ": Failed to decode remote prefixMap: %s",
+ win_errstr(status)));
+ return status;
+ }
+
+ s_dsa->replica_flags = DRSUAPI_DRS_WRIT_REP
+ | DRSUAPI_DRS_INIT_SYNC
+ | DRSUAPI_DRS_PER_SYNC;
+ memset(s_dsa->schedule, 0x11, sizeof(s_dsa->schedule));
+
+ tmp_dns_name = GUID_string(s_dsa->other_info, &s_dsa->source_dsa_obj_guid);
+ if (tmp_dns_name == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ tmp_dns_name = talloc_asprintf_append_buffer(tmp_dns_name, "._msdcs.%s", c->forest->dns_name);
+ if (tmp_dns_name == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ s_dsa->other_info->dns_name = tmp_dns_name;
+
+ if (s->self_made_schema == NULL) {
+ DEBUG(0,("libnet_vampire_cb_apply_schema: called with out self_made_schema\n"));
+ return WERR_INTERNAL_ERROR;
+ }
+
+ schema_ldb = provision_get_schema(s, s->lp_ctx,
+ c->forest->schema_dn_str,
+ &s->prefixmap_blob);
+ if (!schema_ldb) {
+ DEBUG(0,("Failed to re-load from local provision using remote prefixMap. "
+ "Will continue with local prefixMap\n"));
+ provision_schema = dsdb_get_schema(s->ldb, s);
+ } else {
+ provision_schema = dsdb_get_schema(schema_ldb, s);
+ ret = dsdb_reference_schema(s->ldb, provision_schema, SCHEMA_MEMORY_ONLY);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,("Failed to attach schema from local provision using remote prefixMap."));
+ return WERR_INTERNAL_ERROR;
+ }
+ talloc_unlink(s, schema_ldb);
+ }
+
+ cycle_before_switching = lpcfg_parm_long(s->lp_ctx, NULL,
+ "become dc",
+ "schema convert retrial", 1);
+
+ provision_schema->resolving_in_progress = true;
+ s->self_made_schema->resolving_in_progress = true;
+
+ status = dsdb_repl_resolve_working_schema(s->ldb,
+ pfm_remote,
+ cycle_before_switching,
+ provision_schema,
+ s->self_made_schema,
+ object_count,
+ first_object);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0, ("%s: dsdb_repl_resolve_working_schema() failed: %s",
+ __location__, win_errstr(status)));
+ return status;
+ }
+
+ /* free temp objects for 1st conversion phase */
+ talloc_unlink(s, provision_schema);
+
+ s->self_made_schema->resolving_in_progress = false;
+
+ /*
+ * attach the schema we just brought over DRS to the ldb,
+ * so we can use it in dsdb_convert_object_ex below
+ */
+ ret = dsdb_set_schema(s->ldb, s->self_made_schema, SCHEMA_WRITE);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,("Failed to attach working schema from DRS.\n"));
+ return WERR_INTERNAL_ERROR;
+ }
+
+ /* we don't want to access the self made schema anymore */
+ s->schema = s->self_made_schema;
+ s->self_made_schema = NULL;
+
+ partition_dn = ldb_dn_new(s, s->ldb, c->partition->nc.dn);
+ if (partition_dn == NULL) {
+ DEBUG(0,("Failed to parse partition DN from DRS.\n"));
+ return WERR_INVALID_PARAMETER;
+ }
+
+ /* Now convert the schema elements again, using the schema we finalised, ready to actually import */
+ status = dsdb_replicated_objects_convert(s->ldb,
+ s->schema,
+ partition_dn,
+ mapping_ctr,
+ object_count,
+ first_object,
+ linked_attributes_count,
+ linked_attributes,
+ s_dsa,
+ uptodateness_vector,
+ c->gensec_skey,
+ 0,
+ s, &schema_objs);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("Failed to convert objects when trying to import over DRS (2nd pass, to store remote schema): %s\n", win_errstr(status)));
+ return status;
+ }
+
+ if (lpcfg_parm_bool(s->lp_ctx, NULL, "become dc", "dump objects", false)) {
+ for (i=0; i < schema_objs->num_objects; i++) {
+ struct ldb_ldif ldif;
+ fprintf(stdout, "#\n");
+ ldif.changetype = LDB_CHANGETYPE_NONE;
+ ldif.msg = schema_objs->objects[i].msg;
+ ldb_ldif_write_file(s->ldb, stdout, &ldif);
+ NDR_PRINT_DEBUG(replPropertyMetaDataBlob, schema_objs->objects[i].meta_data);
+ }
+ }
+
+ status = dsdb_replicated_objects_commit(s->ldb, NULL, schema_objs, &seq_num);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("Failed to commit objects: %s\n", win_errstr(status)));
+ return status;
+ }
+
+ msg = ldb_msg_new(schema_objs);
+ if (msg == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ msg->dn = schema_objs->partition_dn;
+
+ /* We must ensure a prefixMap has been written. Unlike other
+ * attributes (including schemaInfo), it is not replicated in
+ * the normal replication stream. We can use the one from
+ * s->prefixmap_blob because we operate with one, unchanging
+ * prefixMap for this entire operation. */
+ ret = ldb_msg_add_value(msg, "prefixMap", &s->prefixmap_blob, &prefixMap_el);
+ if (ret != LDB_SUCCESS) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ /* We want to know if a prefixMap was written already, as it
+ * would mean that the above comment was not true, and we have
+ * somehow updated the prefixMap during this transaction */
+ prefixMap_el->flags = LDB_FLAG_MOD_ADD;
+
+ ret = dsdb_modify(s->ldb, msg, DSDB_FLAG_AS_SYSTEM);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,("Failed to add prefixMap: %s\n", ldb_errstring(s->ldb)));
+ return WERR_INTERNAL_ERROR;
+ }
+
+ talloc_free(s_dsa);
+ talloc_free(schema_objs);
+
+ s->schema = dsdb_get_schema(s->ldb, s);
+ if (!s->schema) {
+ DEBUG(0,("Failed to get loaded dsdb_schema\n"));
+ return WERR_INTERNAL_ERROR;
+ }
+
+ return WERR_OK;
+}
+
+WERROR libnet_vampire_cb_schema_chunk(void *private_data,
+ const struct libnet_BecomeDC_StoreChunk *c)
+{
+ struct libnet_vampire_cb_state *s = talloc_get_type(private_data, struct libnet_vampire_cb_state);
+ WERROR werr;
+ const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
+ uint32_t nc_object_count;
+ uint32_t nc_total_received = 0;
+ uint32_t object_count;
+ struct drsuapi_DsReplicaObjectListItemEx *first_object;
+ struct drsuapi_DsReplicaObjectListItemEx *cur;
+ uint32_t nc_linked_attributes_count;
+ uint32_t linked_attributes_count;
+
+ switch (c->ctr_level) {
+ case 1:
+ mapping_ctr = &c->ctr1->mapping_ctr;
+ nc_object_count = c->ctr1->extended_ret; /* maybe w2k send this unexpected? */
+ object_count = c->ctr1->object_count;
+ first_object = c->ctr1->first_object;
+ nc_linked_attributes_count = 0;
+ linked_attributes_count = 0;
+ break;
+ case 6:
+ mapping_ctr = &c->ctr6->mapping_ctr;
+ nc_object_count = c->ctr6->nc_object_count;
+ object_count = c->ctr6->object_count;
+ first_object = c->ctr6->first_object;
+ nc_linked_attributes_count = c->ctr6->nc_linked_attributes_count;
+ linked_attributes_count = c->ctr6->linked_attributes_count;
+ break;
+ default:
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (!s->schema_part.first_object) {
+ nc_total_received = object_count;
+ } else {
+ nc_total_received = s->schema_part.object_count + object_count;
+ }
+ if (nc_object_count) {
+ DEBUG(0,("Schema-DN[%s] objects[%u/%u] linked_values[%u/%u]\n",
+ c->partition->nc.dn, nc_total_received, nc_object_count,
+ linked_attributes_count, nc_linked_attributes_count));
+ } else {
+ DEBUG(0,("Schema-DN[%s] objects[%u] linked_values[%u]\n",
+ c->partition->nc.dn, nc_total_received, linked_attributes_count));
+ }
+
+ if (!s->self_made_schema) {
+ struct drsuapi_DsReplicaOIDMapping_Ctr mapping_ctr_without_schema_info;
+ /* Put the DRS prefixmap aside for the schema we are
+ * about to load in the provision, and into the one we
+ * are making with the help of DRS */
+
+ mapping_ctr_without_schema_info = *mapping_ctr;
+
+ /* This strips off the 0xFF schema info from the end,
+ * because we don't want it in the blob */
+ if (mapping_ctr_without_schema_info.num_mappings > 0) {
+ mapping_ctr_without_schema_info.num_mappings--;
+ }
+ werr = dsdb_get_drsuapi_prefixmap_as_blob(&mapping_ctr_without_schema_info, s, &s->prefixmap_blob);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ /* Set up two manually-constructed schema - the local
+ * schema from the provision will be used to build
+ * one, which will then in turn be used to build the
+ * other. */
+ s->self_made_schema = dsdb_new_schema(s);
+ if (s->self_made_schema == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ werr = dsdb_load_prefixmap_from_drsuapi(s->self_made_schema, mapping_ctr);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+ } else {
+ werr = dsdb_schema_pfm_contains_drsuapi_pfm(s->self_made_schema->prefixmap, mapping_ctr);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+ }
+
+ if (!s->schema_part.first_object) {
+ s->schema_part.object_count = object_count;
+ s->schema_part.first_object = talloc_steal(s, first_object);
+ } else {
+ s->schema_part.object_count += object_count;
+ s->schema_part.last_object->next_object = talloc_steal(s->schema_part.last_object,
+ first_object);
+ }
+ if (first_object != NULL) {
+ for (cur = first_object; cur->next_object; cur = cur->next_object) {}
+ } else {
+ cur = first_object;
+ }
+
+ s->schema_part.last_object = cur;
+
+ if (!c->partition->more_data) {
+ return libnet_vampire_cb_apply_schema(s, c);
+ }
+
+ return WERR_OK;
+}
+
+WERROR libnet_vampire_cb_store_chunk(void *private_data,
+ const struct libnet_BecomeDC_StoreChunk *c)
+{
+ struct libnet_vampire_cb_state *s = talloc_get_type(private_data, struct libnet_vampire_cb_state);
+ WERROR status;
+ struct dsdb_schema *schema;
+ const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
+ uint32_t nc_object_count;
+ uint32_t object_count;
+ struct drsuapi_DsReplicaObjectListItemEx *first_object;
+ uint32_t nc_linked_attributes_count;
+ uint32_t linked_attributes_count;
+ struct drsuapi_DsReplicaLinkedAttribute *linked_attributes;
+ const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector;
+ struct dsdb_extended_replicated_objects *objs;
+ uint32_t req_replica_flags;
+ uint32_t dsdb_repl_flags = 0;
+ struct repsFromTo1 *s_dsa;
+ char *tmp_dns_name;
+ uint32_t i;
+ uint64_t seq_num = 0;
+ bool is_exop = false;
+ struct ldb_dn *partition_dn = NULL;
+ struct ldb_dn *nc_root = NULL;
+
+ s_dsa = talloc_zero(s, struct repsFromTo1);
+ if (s_dsa == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ s_dsa->other_info = talloc(s_dsa, struct repsFromTo1OtherInfo);
+ if (s_dsa->other_info == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ switch (c->ctr_level) {
+ case 1:
+ mapping_ctr = &c->ctr1->mapping_ctr;
+ nc_object_count = c->ctr1->extended_ret; /* maybe w2k send this unexpected? */
+ object_count = c->ctr1->object_count;
+ first_object = c->ctr1->first_object;
+ nc_linked_attributes_count = 0;
+ linked_attributes_count = 0;
+ linked_attributes = NULL;
+ s_dsa->highwatermark = c->ctr1->new_highwatermark;
+ s_dsa->source_dsa_obj_guid = c->ctr1->source_dsa_guid;
+ s_dsa->source_dsa_invocation_id = c->ctr1->source_dsa_invocation_id;
+ uptodateness_vector = NULL; /* TODO: map it */
+ break;
+ case 6:
+ mapping_ctr = &c->ctr6->mapping_ctr;
+ nc_object_count = c->ctr6->nc_object_count;
+ object_count = c->ctr6->object_count;
+ first_object = c->ctr6->first_object;
+ nc_linked_attributes_count = c->ctr6->nc_linked_attributes_count;
+ linked_attributes_count = c->ctr6->linked_attributes_count;
+ linked_attributes = c->ctr6->linked_attributes;
+ s_dsa->highwatermark = c->ctr6->new_highwatermark;
+ s_dsa->source_dsa_obj_guid = c->ctr6->source_dsa_guid;
+ s_dsa->source_dsa_invocation_id = c->ctr6->source_dsa_invocation_id;
+ uptodateness_vector = c->ctr6->uptodateness_vector;
+ break;
+ default:
+ return WERR_INVALID_PARAMETER;
+ }
+
+ switch (c->req_level) {
+ case 0:
+ /* none */
+ req_replica_flags = 0;
+ break;
+ case 5:
+ if (c->req5->extended_op != DRSUAPI_EXOP_NONE) {
+ is_exop = true;
+ }
+ req_replica_flags = c->req5->replica_flags;
+ break;
+ case 8:
+ if (c->req8->extended_op != DRSUAPI_EXOP_NONE) {
+ is_exop = true;
+ }
+ req_replica_flags = c->req8->replica_flags;
+ break;
+ case 10:
+ if (c->req10->extended_op != DRSUAPI_EXOP_NONE) {
+ is_exop = true;
+ }
+ req_replica_flags = c->req10->replica_flags;
+
+ if (c->req10->more_flags & DRSUAPI_DRS_GET_TGT) {
+ dsdb_repl_flags |= DSDB_REPL_FLAG_TARGETS_UPTODATE;
+ }
+ break;
+ default:
+ return WERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * If the peer DC doesn't support GET_TGT (req v10), then the link
+ * targets are as up-to-date as they're ever gonna be. (Without this,
+ * cases where we'd normally retry with GET_TGT cause the join to fail)
+ */
+ if (c->req_level < 10) {
+ dsdb_repl_flags |= DSDB_REPL_FLAG_TARGETS_UPTODATE;
+ }
+
+ if (req_replica_flags & DRSUAPI_DRS_CRITICAL_ONLY || is_exop) {
+ /*
+ * If we only replicate the critical objects, or this
+ * is an exop we should not remember what we already
+ * got, as it is incomplete.
+ */
+ ZERO_STRUCT(s_dsa->highwatermark);
+ uptodateness_vector = NULL;
+ dsdb_repl_flags |= DSDB_REPL_FLAG_OBJECT_SUBSET;
+ }
+
+ /* TODO: avoid hardcoded flags */
+ s_dsa->replica_flags = DRSUAPI_DRS_WRIT_REP
+ | DRSUAPI_DRS_INIT_SYNC
+ | DRSUAPI_DRS_PER_SYNC;
+ memset(s_dsa->schedule, 0x11, sizeof(s_dsa->schedule));
+
+ tmp_dns_name = GUID_string(s_dsa->other_info, &s_dsa->source_dsa_obj_guid);
+ if (tmp_dns_name == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ tmp_dns_name = talloc_asprintf_append_buffer(tmp_dns_name, "._msdcs.%s", c->forest->dns_name);
+ if (tmp_dns_name == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ s_dsa->other_info->dns_name = tmp_dns_name;
+
+ /* we want to show a count per partition */
+ if (!s->last_partition || strcmp(s->last_partition, c->partition->nc.dn) != 0) {
+ s->total_objects = 0;
+ s->total_links = 0;
+ talloc_free(s->last_partition);
+ s->last_partition = talloc_strdup(s, c->partition->nc.dn);
+ }
+ s->total_objects += object_count;
+ s->total_links += linked_attributes_count;
+
+ partition_dn = ldb_dn_new(s_dsa, s->ldb, c->partition->nc.dn);
+ if (partition_dn == NULL) {
+ DEBUG(0,("Failed to parse partition DN from DRS.\n"));
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (is_exop) {
+ int ret;
+ if (nc_object_count) {
+ DEBUG(0,("Exop on[%s] objects[%u/%u] linked_values[%u/%u]\n",
+ c->partition->nc.dn, s->total_objects, nc_object_count,
+ s->total_links, nc_linked_attributes_count));
+ } else {
+ DEBUG(0,("Exop on[%s] objects[%u] linked_values[%u]\n",
+ c->partition->nc.dn, s->total_objects, linked_attributes_count));
+ }
+ ret = dsdb_find_nc_root(s->ldb, s_dsa,
+ partition_dn, &nc_root);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Failed to find nc_root for %s\n",
+ ldb_dn_get_linearized(partition_dn)));
+ return WERR_INTERNAL_ERROR;
+ }
+ } else {
+ if (nc_object_count) {
+ DEBUG(0,("Partition[%s] objects[%u/%u] linked_values[%u/%u]\n",
+ c->partition->nc.dn, s->total_objects, nc_object_count,
+ s->total_links, nc_linked_attributes_count));
+ } else {
+ DEBUG(0,("Partition[%s] objects[%u] linked_values[%u]\n",
+ c->partition->nc.dn, s->total_objects, s->total_links));
+ }
+ nc_root = partition_dn;
+ }
+
+
+ schema = dsdb_get_schema(s->ldb, NULL);
+ if (!schema) {
+ DEBUG(0,(__location__ ": Schema is not loaded yet!\n"));
+ return WERR_INTERNAL_ERROR;
+ }
+
+ if (req_replica_flags & DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS) {
+ dsdb_repl_flags |= DSDB_REPL_FLAG_PRIORITISE_INCOMING;
+ }
+
+ if (req_replica_flags & DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) {
+ dsdb_repl_flags |= DSDB_REPL_FLAG_EXPECT_NO_SECRETS;
+ }
+
+ status = dsdb_replicated_objects_convert(s->ldb,
+ schema,
+ nc_root,
+ mapping_ctr,
+ object_count,
+ first_object,
+ linked_attributes_count,
+ linked_attributes,
+ s_dsa,
+ uptodateness_vector,
+ c->gensec_skey,
+ dsdb_repl_flags,
+ s, &objs);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("Failed to convert objects: %s\n", win_errstr(status)));
+ return status;
+ }
+
+ if (lpcfg_parm_bool(s->lp_ctx, NULL, "become dc", "dump objects", false)) {
+ for (i=0; i < objs->num_objects; i++) {
+ struct ldb_ldif ldif;
+ fprintf(stdout, "#\n");
+ ldif.changetype = LDB_CHANGETYPE_NONE;
+ ldif.msg = objs->objects[i].msg;
+ ldb_ldif_write_file(s->ldb, stdout, &ldif);
+ NDR_PRINT_DEBUG(replPropertyMetaDataBlob, objs->objects[i].meta_data);
+ }
+ }
+ status = dsdb_replicated_objects_commit(s->ldb, NULL, objs, &seq_num);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("Failed to commit objects: %s\n", win_errstr(status)));
+ return status;
+ }
+
+ /* reset debug counters once we've finished replicating the partition */
+ if (!c->partition->more_data) {
+ s->total_objects = 0;
+ s->total_links = 0;
+ }
+
+ talloc_free(s_dsa);
+ talloc_free(objs);
+
+ for (i=0; i < linked_attributes_count; i++) {
+ const struct dsdb_attribute *sa;
+
+ if (!linked_attributes[i].identifier) {
+ DEBUG(0, ("No linked attribute identifier\n"));
+ return WERR_INTERNAL_ERROR;
+ }
+
+ if (!linked_attributes[i].value.blob) {
+ DEBUG(0, ("No linked attribute value\n"));
+ return WERR_INTERNAL_ERROR;
+ }
+
+ sa = dsdb_attribute_by_attributeID_id(s->schema,
+ linked_attributes[i].attid);
+ if (!sa) {
+ DEBUG(0, ("Unable to find attribute via attribute id %d\n", linked_attributes[i].attid));
+ return WERR_INTERNAL_ERROR;
+ }
+
+ if (lpcfg_parm_bool(s->lp_ctx, NULL, "become dc", "dump objects", false)) {
+ DEBUG(0,("# %s\n", sa->lDAPDisplayName));
+ NDR_PRINT_DEBUG(drsuapi_DsReplicaLinkedAttribute, &linked_attributes[i]);
+ dump_data(0,
+ linked_attributes[i].value.blob->data,
+ linked_attributes[i].value.blob->length);
+ }
+ }
+
+ return WERR_OK;
+}
+
diff --git a/source4/libnet/libnet_vampire.h b/source4/libnet/libnet_vampire.h
new file mode 100644
index 0000000..ea616ab
--- /dev/null
+++ b/source4/libnet/libnet_vampire.h
@@ -0,0 +1,58 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+ Copyright (C) Brad Henry 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 __LIBNET_VAMPIRE_H__
+#define __LIBNET_VAMPIRE_H__
+
+struct libnet_Vampire {
+ struct {
+ const char *domain_name;
+ const char *netbios_name;
+ const char *targetdir;
+ } in;
+
+ struct {
+ struct dom_sid *domain_sid;
+ const char *domain_name;
+ const char *error_string;
+ } out;
+};
+
+struct libnet_Replicate {
+ struct {
+ const char *domain_name;
+ const char *netbios_name;
+ const char *targetdir; /* optional, may be NULL */
+ struct dom_sid *domain_sid;
+ const char *realm;
+ const char *server;
+ const char *join_password;
+ uint32_t kvno;
+ } in;
+ struct {
+ const char *error_string;
+ } out;
+};
+
+/* Private context for the default callbacks */
+struct libnet_vampire_cb_state;
+
+#endif /* __LIBNET_VAMPIRE_H__ */
diff --git a/source4/libnet/prereq_domain.c b/source4/libnet/prereq_domain.c
new file mode 100644
index 0000000..679669e
--- /dev/null
+++ b/source4/libnet/prereq_domain.c
@@ -0,0 +1,144 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Rafal Szczesniak 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 "libnet/libnet.h"
+#include "libcli/composite/composite.h"
+#include "auth/credentials/credentials.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/samr.h"
+#include "librpc/gen_ndr/ndr_samr.h"
+#include "librpc/gen_ndr/lsa.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+
+
+bool samr_domain_opened(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ struct composite_context **parent_ctx,
+ struct libnet_DomainOpen *domain_open,
+ void (*continue_fn)(struct composite_context*),
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *domopen_req;
+
+ if (parent_ctx == NULL || *parent_ctx == NULL) return false;
+
+ if (domain_name == NULL) {
+ /*
+ * Try to guess the domain name from credentials,
+ * if it's not been explicitly specified.
+ */
+
+ if (ndr_policy_handle_empty(&ctx->samr.handle)) {
+ domain_open->in.type = DOMAIN_SAMR;
+ domain_open->in.domain_name = cli_credentials_get_domain(ctx->cred);
+ domain_open->in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+
+ } else {
+ composite_error(*parent_ctx, NT_STATUS_INVALID_PARAMETER);
+ return true;
+ }
+
+ } else {
+ /*
+ * The domain name has been specified, so check whether the same
+ * domain is already opened. If it is - just return NULL. Start
+ * opening a new domain otherwise.
+ */
+
+ if (ndr_policy_handle_empty(&ctx->samr.handle) ||
+ !strequal(domain_name, ctx->samr.name)) {
+ domain_open->in.type = DOMAIN_SAMR;
+ domain_open->in.domain_name = domain_name;
+ domain_open->in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+
+ } else {
+ /* domain has already been opened and it's the same domain
+ as requested */
+ return true;
+ }
+ }
+
+ /* send request to open the domain */
+ domopen_req = libnet_DomainOpen_send(ctx, mem_ctx, domain_open, monitor);
+ if (composite_nomem(domopen_req, *parent_ctx)) return false;
+
+ composite_continue(*parent_ctx, domopen_req, continue_fn, *parent_ctx);
+ return false;
+}
+
+
+bool lsa_domain_opened(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ struct composite_context **parent_ctx,
+ struct libnet_DomainOpen *domain_open,
+ void (*continue_fn)(struct composite_context*),
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *domopen_req;
+
+ if (parent_ctx == NULL || *parent_ctx == NULL) return false;
+
+ if (domain_name == NULL) {
+ /*
+ * Try to guess the domain name from credentials,
+ * if it's not been explicitly specified.
+ */
+
+ if (ndr_policy_handle_empty(&ctx->lsa.handle)) {
+ domain_open->in.type = DOMAIN_LSA;
+ domain_open->in.domain_name = cli_credentials_get_domain(ctx->cred);
+ domain_open->in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+
+ } else {
+ composite_error(*parent_ctx, NT_STATUS_INVALID_PARAMETER);
+ /* this ensures the calling function exits and composite function error
+ gets noticed quickly */
+ return true;
+ }
+
+ } else {
+ /*
+ * The domain name has been specified, so check whether the same
+ * domain is already opened. If it is - just return NULL. Start
+ * opening a new domain otherwise.
+ */
+
+ if (ndr_policy_handle_empty(&ctx->lsa.handle) ||
+ !strequal(domain_name, ctx->lsa.name)) {
+ domain_open->in.type = DOMAIN_LSA;
+ domain_open->in.domain_name = domain_name;
+ domain_open->in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+
+ } else {
+ /* domain has already been opened and it's the same domain
+ as requested */
+ return true;
+ }
+ }
+
+ /* send request to open the domain */
+ domopen_req = libnet_DomainOpen_send(ctx, mem_ctx, domain_open, monitor);
+ /* see the comment above to find out why true is returned here */
+ if (composite_nomem(domopen_req, *parent_ctx)) return true;
+
+ composite_continue(*parent_ctx, domopen_req, continue_fn, *parent_ctx);
+ return false;
+}
diff --git a/source4/libnet/py_net.c b/source4/libnet/py_net.c
new file mode 100644
index 0000000..fe5979e
--- /dev/null
+++ b/source4/libnet/py_net.c
@@ -0,0 +1,945 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008-2010
+ Copyright (C) Kamen Mazdrashki <kamen.mazdrashki@postpath.com> 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 <Python.h>
+#include "python/py3compat.h"
+#include "includes.h"
+#include "python/modules.h"
+#include <pyldb.h>
+#include <pytalloc.h>
+#include "libnet.h"
+#include "auth/credentials/pycredentials.h"
+#include "libcli/security/security.h"
+#include "lib/events/events.h"
+#include "param/pyparam.h"
+#include "auth/gensec/gensec.h"
+#include "librpc/rpc/pyrpc_util.h"
+#include "libcli/resolve/resolve.h"
+#include "libcli/finddc.h"
+#include "dsdb/samdb/samdb.h"
+#include "py_net.h"
+#include "librpc/rpc/pyrpc_util.h"
+#include "libcli/drsuapi/drsuapi.h"
+
+static void PyErr_SetDsExtendedError(enum drsuapi_DsExtendedError ext_err, const char *error_description)
+{
+ PyObject *mod = NULL;
+ PyObject *error = NULL;
+ mod = PyImport_ImportModule("samba");
+ if (mod) {
+ error = PyObject_GetAttrString(mod, "DsExtendedError");
+ }
+ if (error_description == NULL) {
+ switch (ext_err) {
+ /* Copied out of ndr_drsuapi.c:ndr_print_drsuapi_DsExtendedError() */
+ case DRSUAPI_EXOP_ERR_NONE:
+ error_description = "DRSUAPI_EXOP_ERR_NONE";
+ break;
+ case DRSUAPI_EXOP_ERR_SUCCESS:
+ error_description = "DRSUAPI_EXOP_ERR_SUCCESS";
+ break;
+ case DRSUAPI_EXOP_ERR_UNKNOWN_OP:
+ error_description = "DRSUAPI_EXOP_ERR_UNKNOWN_OP";
+ break;
+ case DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER:
+ error_description = "DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER";
+ break;
+ case DRSUAPI_EXOP_ERR_UPDATE_ERR:
+ error_description = "DRSUAPI_EXOP_ERR_UPDATE_ERR";
+ break;
+ case DRSUAPI_EXOP_ERR_EXCEPTION:
+ error_description = "DRSUAPI_EXOP_ERR_EXCEPTION";
+ break;
+ case DRSUAPI_EXOP_ERR_UNKNOWN_CALLER:
+ error_description = "DRSUAPI_EXOP_ERR_UNKNOWN_CALLER";
+ break;
+ case DRSUAPI_EXOP_ERR_RID_ALLOC:
+ error_description = "DRSUAPI_EXOP_ERR_RID_ALLOC";
+ break;
+ case DRSUAPI_EXOP_ERR_FSMO_OWNER_DELETED:
+ error_description = "DRSUAPI_EXOP_ERR_FSMO_OWNER_DELETED";
+ break;
+ case DRSUAPI_EXOP_ERR_FMSO_PENDING_OP:
+ error_description = "DRSUAPI_EXOP_ERR_FMSO_PENDING_OP";
+ break;
+ case DRSUAPI_EXOP_ERR_MISMATCH:
+ error_description = "DRSUAPI_EXOP_ERR_MISMATCH";
+ break;
+ case DRSUAPI_EXOP_ERR_COULDNT_CONTACT:
+ error_description = "DRSUAPI_EXOP_ERR_COULDNT_CONTACT";
+ break;
+ case DRSUAPI_EXOP_ERR_FSMO_REFUSING_ROLES:
+ error_description = "DRSUAPI_EXOP_ERR_FSMO_REFUSING_ROLES";
+ break;
+ case DRSUAPI_EXOP_ERR_DIR_ERROR:
+ error_description = "DRSUAPI_EXOP_ERR_DIR_ERROR";
+ break;
+ case DRSUAPI_EXOP_ERR_FSMO_MISSING_SETTINGS:
+ error_description = "DRSUAPI_EXOP_ERR_FSMO_MISSING_SETTINGS";
+ break;
+ case DRSUAPI_EXOP_ERR_ACCESS_DENIED:
+ error_description = "DRSUAPI_EXOP_ERR_ACCESS_DENIED";
+ break;
+ case DRSUAPI_EXOP_ERR_PARAM_ERROR:
+ error_description = "DRSUAPI_EXOP_ERR_PARAM_ERROR";
+ break;
+ }
+ }
+ if (error) {
+ PyObject *value =
+ Py_BuildValue(discard_const_p(char, "(i,s)"),
+ ext_err,
+ error_description);
+ PyErr_SetObject(error, value);
+ if (value) {
+ Py_DECREF(value);
+ }
+ Py_DECREF(error);
+ }
+}
+
+static PyObject *py_net_join_member(py_net_Object *self, PyObject *args, PyObject *kwargs)
+{
+ struct libnet_Join_member r;
+ int _level = 0;
+ NTSTATUS status;
+ PyObject *result;
+ TALLOC_CTX *mem_ctx;
+ const char *kwnames[] = { "domain_name", "netbios_name", "level", "machinepass", NULL };
+
+ ZERO_STRUCT(r);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ssi|z:Join", discard_const_p(char *, kwnames),
+ &r.in.domain_name, &r.in.netbios_name,
+ &_level,
+ &r.in.account_pass)) {
+ return NULL;
+ }
+ r.in.level = _level;
+
+ mem_ctx = talloc_new(self->mem_ctx);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ status = libnet_Join_member(self->libnet_ctx, mem_ctx, &r);
+ if (NT_STATUS_IS_ERR(status)) {
+ PyErr_SetNTSTATUS_and_string(status,
+ r.out.error_string
+ ? r.out.error_string
+ : nt_errstr(status));
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ result = Py_BuildValue("sss", r.out.join_password,
+ dom_sid_string(mem_ctx, r.out.domain_sid),
+ r.out.domain_name);
+
+ talloc_free(mem_ctx);
+
+ return result;
+}
+
+static const char py_net_join_member_doc[] = "join_member(domain_name, netbios_name, level) -> (join_password, domain_sid, domain_name)\n\n" \
+"Join the domain with the specified name.";
+
+static PyObject *py_net_change_password(py_net_Object *self, PyObject *args, PyObject *kwargs)
+{
+ union libnet_ChangePassword r;
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx = NULL;
+ struct tevent_context *ev = NULL;
+ const char *kwnames[] = { "newpassword", "oldpassword", "domain", "username", NULL };
+ const char *newpass = NULL;
+ const char *oldpass = NULL;
+ ZERO_STRUCT(r);
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, PYARG_STR_UNI
+ "|"PYARG_STR_UNI"ss:change_password",
+ discard_const_p(char *, kwnames),
+ "utf8",
+ &newpass,
+ "utf8",
+ &oldpass,
+ &r.generic.in.domain_name,
+ &r.generic.in.account_name)) {
+ return NULL;
+ }
+
+ r.generic.in.newpassword = newpass;
+ r.generic.in.oldpassword = oldpass;
+
+ r.generic.level = LIBNET_CHANGE_PASSWORD_GENERIC;
+ if (r.generic.in.account_name == NULL) {
+ r.generic.in.account_name
+ = cli_credentials_get_username(self->libnet_ctx->cred);
+ }
+ if (r.generic.in.domain_name == NULL) {
+ r.generic.in.domain_name
+ = cli_credentials_get_domain(self->libnet_ctx->cred);
+ }
+ if (r.generic.in.oldpassword == NULL) {
+ r.generic.in.oldpassword
+ = cli_credentials_get_password(self->libnet_ctx->cred);
+ }
+
+ /* FIXME: we really need to get a context from the caller or we may end
+ * up with 2 event contexts */
+ ev = s4_event_context_init(NULL);
+
+ mem_ctx = talloc_new(ev);
+ if (mem_ctx == NULL) {
+ PyMem_Free(discard_const_p(char, newpass));
+ PyMem_Free(discard_const_p(char, oldpass));
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ status = libnet_ChangePassword(self->libnet_ctx, mem_ctx, &r);
+
+ PyMem_Free(discard_const_p(char, newpass));
+ PyMem_Free(discard_const_p(char, oldpass));
+
+ if (NT_STATUS_IS_ERR(status)) {
+ PyErr_SetNTSTATUS_and_string(status,
+ r.generic.out.error_string
+ ? r.generic.out.error_string
+ : nt_errstr(status));
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ talloc_free(mem_ctx);
+ Py_RETURN_NONE;
+}
+
+static const char py_net_change_password_doc[] = "change_password(newpassword) -> True\n\n" \
+"Change password for a user. You must supply credential with enough rights to do this.\n\n" \
+"Sample usage is:\n" \
+"net.change_password(newpassword=<new_password>)\n";
+
+
+static PyObject *py_net_set_password(py_net_Object *self, PyObject *args, PyObject *kwargs)
+{
+ union libnet_SetPassword r;
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx;
+ struct tevent_context *ev;
+ const char *kwnames[] = { "account_name", "domain_name", "newpassword", "force_samr_18", NULL };
+ PyObject *py_force_samr_18 = Py_False;
+
+ ZERO_STRUCT(r);
+
+ r.generic.level = LIBNET_SET_PASSWORD_GENERIC;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sss|O:set_password",
+ discard_const_p(char *, kwnames),
+ &r.generic.in.account_name,
+ &r.generic.in.domain_name,
+ &r.generic.in.newpassword,
+ &py_force_samr_18)) {
+ return NULL;
+ }
+
+ if (py_force_samr_18) {
+ if (!PyBool_Check(py_force_samr_18)) {
+ PyErr_SetString(PyExc_TypeError, "Expected boolean force_samr_18");
+ return NULL;
+ }
+ if (py_force_samr_18 == Py_True) {
+ r.generic.samr_level = LIBNET_SET_PASSWORD_SAMR_HANDLE_18;
+ }
+ }
+
+ /* FIXME: we really need to get a context from the caller or we may end
+ * up with 2 event contexts */
+ ev = s4_event_context_init(NULL);
+
+ mem_ctx = talloc_new(ev);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ status = libnet_SetPassword(self->libnet_ctx, mem_ctx, &r);
+ if (NT_STATUS_IS_ERR(status)) {
+ PyErr_SetNTSTATUS_and_string(status,
+ r.generic.out.error_string
+ ? r.generic.out.error_string
+ : nt_errstr(status));
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ talloc_free(mem_ctx);
+
+ Py_RETURN_NONE;
+}
+
+static const char py_net_set_password_doc[] = "set_password(account_name, domain_name, newpassword) -> True\n\n" \
+"Set password for a user. You must supply credential with enough rights to do this.\n\n" \
+"Sample usage is:\n" \
+"net.set_password(account_name=account_name, domain_name=domain_name, newpassword=new_pass)\n";
+
+
+static PyObject *py_net_time(py_net_Object *self, PyObject *args, PyObject *kwargs)
+{
+ const char *kwnames[] = { "server_name", NULL };
+ union libnet_RemoteTOD r;
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx;
+ char timestr[64];
+ PyObject *ret;
+ struct tm *tm;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s",
+ discard_const_p(char *, kwnames), &r.generic.in.server_name))
+ return NULL;
+
+ r.generic.level = LIBNET_REMOTE_TOD_GENERIC;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ status = libnet_RemoteTOD(self->libnet_ctx, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS_and_string(status,
+ r.generic.out.error_string
+ ? r.generic.out.error_string
+ : nt_errstr(status));
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ ZERO_STRUCT(timestr);
+ tm = localtime(&r.generic.out.time);
+ strftime(timestr, sizeof(timestr)-1, "%c %Z",tm);
+
+ ret = PyUnicode_FromString(timestr);
+
+ talloc_free(mem_ctx);
+
+ return ret;
+}
+
+static const char py_net_time_doc[] = "time(server_name) -> timestr\n"
+"Retrieve the remote time on a server";
+
+static PyObject *py_net_user_create(py_net_Object *self, PyObject *args, PyObject *kwargs)
+{
+ const char *kwnames[] = { "username", NULL };
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx;
+ struct libnet_CreateUser r;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", discard_const_p(char *, kwnames),
+ &r.in.user_name))
+ return NULL;
+
+ r.in.domain_name = cli_credentials_get_domain(self->libnet_ctx->cred);
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ status = libnet_CreateUser(self->libnet_ctx, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS_and_string(status,
+ r.out.error_string
+ ? r.out.error_string
+ : nt_errstr(status));
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ talloc_free(mem_ctx);
+
+ Py_RETURN_NONE;
+}
+
+static const char py_net_create_user_doc[] = "create_user(username)\n"
+"Create a new user.";
+
+static PyObject *py_net_user_delete(py_net_Object *self, PyObject *args, PyObject *kwargs)
+{
+ const char *kwnames[] = { "username", NULL };
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx;
+ struct libnet_DeleteUser r;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", discard_const_p(char *, kwnames),
+ &r.in.user_name))
+ return NULL;
+
+ r.in.domain_name = cli_credentials_get_domain(self->libnet_ctx->cred);
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ status = libnet_DeleteUser(self->libnet_ctx, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS_and_string(status,
+ r.out.error_string
+ ? r.out.error_string
+ : nt_errstr(status));
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ talloc_free(mem_ctx);
+
+ Py_RETURN_NONE;
+}
+
+static const char py_net_delete_user_doc[] = "delete_user(username)\n"
+"Delete a user.";
+
+struct replicate_state {
+ void *vampire_state;
+ dcerpc_InterfaceObject *drs_pipe;
+ struct libnet_BecomeDC_StoreChunk chunk;
+ DATA_BLOB gensec_skey;
+ struct libnet_BecomeDC_Partition partition;
+ struct libnet_BecomeDC_Forest forest;
+ struct libnet_BecomeDC_DestDSA dest_dsa;
+};
+
+/*
+ setup for replicate_chunk() calls
+ */
+static PyObject *py_net_replicate_init(py_net_Object *self, PyObject *args, PyObject *kwargs)
+{
+ const char *kwnames[] = { "samdb", "lp", "drspipe", "invocation_id", NULL };
+ PyObject *py_ldb, *py_lp, *py_drspipe, *py_invocation_id;
+ struct ldb_context *samdb;
+ struct loadparm_context *lp;
+ struct replicate_state *s;
+ NTSTATUS status;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOOO",
+ discard_const_p(char *, kwnames),
+ &py_ldb, &py_lp, &py_drspipe,
+ &py_invocation_id)) {
+ return NULL;
+ }
+
+ s = talloc_zero(NULL, struct replicate_state);
+ if (!s) return NULL;
+
+ lp = lpcfg_from_py_object(s, py_lp);
+ if (lp == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected lp object");
+ talloc_free(s);
+ return NULL;
+ }
+
+ samdb = pyldb_Ldb_AsLdbContext(py_ldb);
+ if (samdb == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected ldb object");
+ talloc_free(s);
+ return NULL;
+ }
+ if (!py_check_dcerpc_type(py_invocation_id, "samba.dcerpc.misc", "GUID")) {
+
+ talloc_free(s);
+ return NULL;
+ }
+ s->dest_dsa.invocation_id = *pytalloc_get_type(py_invocation_id, struct GUID);
+
+ s->drs_pipe = (dcerpc_InterfaceObject *)(py_drspipe);
+
+ s->vampire_state = libnet_vampire_replicate_init(s, samdb, lp);
+ if (s->vampire_state == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Failed to initialise vampire_state");
+ talloc_free(s);
+ return NULL;
+ }
+
+ status = gensec_session_key(s->drs_pipe->pipe->conn->security_state.generic_state,
+ s,
+ &s->gensec_skey);
+ if (!NT_STATUS_IS_OK(status)) {
+ char *error_string = talloc_asprintf(s,
+ "Unable to get session key from drspipe: %s",
+ nt_errstr(status));
+ PyErr_SetNTSTATUS_and_string(status, error_string);
+ talloc_free(s);
+ return NULL;
+ }
+
+ s->forest.dns_name = samdb_dn_to_dns_domain(s, ldb_get_root_basedn(samdb));
+ s->forest.root_dn_str = ldb_dn_get_linearized(ldb_get_root_basedn(samdb));
+ s->forest.config_dn_str = ldb_dn_get_linearized(ldb_get_config_basedn(samdb));
+ s->forest.schema_dn_str = ldb_dn_get_linearized(ldb_get_schema_basedn(samdb));
+
+ s->chunk.gensec_skey = &s->gensec_skey;
+ s->chunk.partition = &s->partition;
+ s->chunk.forest = &s->forest;
+ s->chunk.dest_dsa = &s->dest_dsa;
+
+ return pytalloc_GenericObject_steal(s);
+}
+
+
+/*
+ process one replication chunk
+ */
+static PyObject *py_net_replicate_chunk(py_net_Object *self, PyObject *args, PyObject *kwargs)
+{
+ const char *kwnames[] = { "state", "level", "ctr",
+ "schema", "req_level", "req",
+ NULL };
+ PyObject *py_state, *py_ctr, *py_schema = Py_None, *py_req = Py_None;
+ struct replicate_state *s;
+ unsigned level;
+ unsigned req_level = 0;
+ WERROR (*chunk_handler)(void *private_data, const struct libnet_BecomeDC_StoreChunk *c);
+ WERROR werr;
+ enum drsuapi_DsExtendedError extended_ret = DRSUAPI_EXOP_ERR_NONE;
+ enum drsuapi_DsExtendedOperation exop = DRSUAPI_EXOP_NONE;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OIO|OIO",
+ discard_const_p(char *, kwnames),
+ &py_state, &level, &py_ctr,
+ &py_schema, &req_level, &py_req)) {
+ return NULL;
+ }
+
+ s = pytalloc_get_type(py_state, struct replicate_state);
+ if (!s) {
+ return NULL;
+ }
+
+ switch (level) {
+ case 1:
+ if (!py_check_dcerpc_type(py_ctr, "samba.dcerpc.drsuapi", "DsGetNCChangesCtr1")) {
+ return NULL;
+ }
+ s->chunk.ctr1 = pytalloc_get_ptr(py_ctr);
+ if (s->chunk.ctr1->naming_context != NULL) {
+ s->partition.nc = *s->chunk.ctr1->naming_context;
+ }
+ extended_ret = s->chunk.ctr1->extended_ret;
+ s->partition.more_data = s->chunk.ctr1->more_data;
+ s->partition.source_dsa_guid = s->chunk.ctr1->source_dsa_guid;
+ s->partition.source_dsa_invocation_id = s->chunk.ctr1->source_dsa_invocation_id;
+ s->partition.highwatermark = s->chunk.ctr1->new_highwatermark;
+ break;
+ case 6:
+ if (!py_check_dcerpc_type(py_ctr, "samba.dcerpc.drsuapi", "DsGetNCChangesCtr6")) {
+ return NULL;
+ }
+ s->chunk.ctr6 = pytalloc_get_ptr(py_ctr);
+ if (s->chunk.ctr6->naming_context != NULL) {
+ s->partition.nc = *s->chunk.ctr6->naming_context;
+ }
+ extended_ret = s->chunk.ctr6->extended_ret;
+ s->partition.more_data = s->chunk.ctr6->more_data;
+ s->partition.source_dsa_guid = s->chunk.ctr6->source_dsa_guid;
+ s->partition.source_dsa_invocation_id = s->chunk.ctr6->source_dsa_invocation_id;
+ s->partition.highwatermark = s->chunk.ctr6->new_highwatermark;
+ break;
+ default:
+ PyErr_Format(PyExc_TypeError, "Bad level %u in replicate_chunk", level);
+ return NULL;
+ }
+
+ s->chunk.req5 = NULL;
+ s->chunk.req8 = NULL;
+ s->chunk.req10 = NULL;
+ if (py_req != Py_None) {
+ switch (req_level) {
+ case 0:
+ break;
+ case 5:
+ if (!py_check_dcerpc_type(py_req, "samba.dcerpc.drsuapi", "DsGetNCChangesRequest5")) {
+ return NULL;
+ }
+
+ s->chunk.req5 = pytalloc_get_ptr(py_req);
+ exop = s->chunk.req5->extended_op;
+ break;
+ case 8:
+ if (!py_check_dcerpc_type(py_req, "samba.dcerpc.drsuapi", "DsGetNCChangesRequest8")) {
+ return NULL;
+ }
+
+ s->chunk.req8 = pytalloc_get_ptr(py_req);
+ exop = s->chunk.req8->extended_op;
+ break;
+ case 10:
+ if (!py_check_dcerpc_type(py_req, "samba.dcerpc.drsuapi", "DsGetNCChangesRequest10")) {
+ return NULL;
+ }
+
+ s->chunk.req10 = pytalloc_get_ptr(py_req);
+ exop = s->chunk.req10->extended_op;
+ break;
+ default:
+ PyErr_Format(PyExc_TypeError, "Bad req_level %u in replicate_chunk", req_level);
+ return NULL;
+ }
+ }
+
+ if (exop != DRSUAPI_EXOP_NONE && extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
+ PyErr_SetDsExtendedError(extended_ret, NULL);
+ return NULL;
+ }
+
+ s->chunk.req_level = req_level;
+
+ chunk_handler = libnet_vampire_cb_store_chunk;
+ if (py_schema) {
+ if (!PyBool_Check(py_schema)) {
+ PyErr_SetString(PyExc_TypeError, "Expected boolean schema");
+ return NULL;
+ }
+ if (py_schema == Py_True) {
+ chunk_handler = libnet_vampire_cb_schema_chunk;
+ }
+ }
+
+ s->chunk.ctr_level = level;
+
+ werr = chunk_handler(s->vampire_state, &s->chunk);
+ if (!W_ERROR_IS_OK(werr)) {
+ char *error_string
+ = talloc_asprintf(NULL,
+ "Failed to process 'chunk' of DRS replicated objects: %s",
+ win_errstr(werr));
+ PyErr_SetWERROR_and_string(werr, error_string);
+ TALLOC_FREE(error_string);
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+
+/*
+ just do the decryption of a DRS replicated attribute
+ */
+static PyObject *py_net_replicate_decrypt(py_net_Object *self, PyObject *args, PyObject *kwargs)
+{
+ const char *kwnames[] = { "drspipe", "attribute", "rid", NULL };
+ PyObject *py_drspipe, *py_attribute;
+ NTSTATUS status;
+ dcerpc_InterfaceObject *drs_pipe;
+ TALLOC_CTX *frame;
+ TALLOC_CTX *context;
+ DATA_BLOB gensec_skey;
+ unsigned int rid;
+ struct drsuapi_DsReplicaAttribute *attribute;
+ WERROR werr;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOI",
+ discard_const_p(char *, kwnames),
+ &py_drspipe,
+ &py_attribute, &rid)) {
+ return NULL;
+ }
+
+ frame = talloc_stackframe();
+
+ if (!py_check_dcerpc_type(py_drspipe,
+ "samba.dcerpc.base",
+ "ClientConnection")) {
+ return NULL;
+ }
+ drs_pipe = (dcerpc_InterfaceObject *)(py_drspipe);
+
+ status = gensec_session_key(drs_pipe->pipe->conn->security_state.generic_state,
+ frame,
+ &gensec_skey);
+ if (!NT_STATUS_IS_OK(status)) {
+ char *error_string
+ = talloc_asprintf(frame,
+ "Unable to get session key from drspipe: %s",
+ nt_errstr(status));
+ PyErr_SetNTSTATUS_and_string(status, error_string);
+ talloc_free(frame);
+ return NULL;
+ }
+
+ if (!py_check_dcerpc_type(py_attribute, "samba.dcerpc.drsuapi",
+ "DsReplicaAttribute")) {
+ return NULL;
+ }
+
+ attribute = pytalloc_get_ptr(py_attribute);
+ context = pytalloc_get_mem_ctx(py_attribute);
+ werr = drsuapi_decrypt_attribute(context, &gensec_skey,
+ rid, 0, attribute);
+ if (!W_ERROR_IS_OK(werr)) {
+ char *error_string = talloc_asprintf(frame,
+ "Unable to get decrypt attribute: %s",
+ win_errstr(werr));
+ PyErr_SetWERROR_and_string(werr, error_string);
+ talloc_free(frame);
+ return NULL;
+ }
+
+ talloc_free(frame);
+
+ Py_RETURN_NONE;
+
+}
+
+/*
+ find a DC given a domain name and server type
+ */
+static PyObject *py_net_finddc(py_net_Object *self, PyObject *args, PyObject *kwargs)
+{
+ const char *domain = NULL, *address = NULL;
+ unsigned server_type;
+ NTSTATUS status;
+ struct finddcs *io;
+ TALLOC_CTX *mem_ctx;
+ PyObject *ret;
+ const char * const kwnames[] = { "flags", "domain", "address", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "I|zz",
+ discard_const_p(char *, kwnames),
+ &server_type, &domain, &address)) {
+ return NULL;
+ }
+
+ mem_ctx = talloc_new(self->mem_ctx);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ io = talloc_zero(mem_ctx, struct finddcs);
+ if (io == NULL) {
+ TALLOC_FREE(mem_ctx);
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ if (domain != NULL) {
+ io->in.domain_name = domain;
+ }
+ if (address != NULL) {
+ io->in.server_address = address;
+ }
+ io->in.minimum_dc_flags = server_type;
+
+ status = finddcs_cldap(io, io,
+ lpcfg_resolve_context(self->libnet_ctx->lp_ctx), self->ev);
+ if (NT_STATUS_IS_ERR(status)) {
+ PyErr_SetNTSTATUS(status);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ ret = py_return_ndr_struct("samba.dcerpc.nbt", "NETLOGON_SAM_LOGON_RESPONSE_EX",
+ io, &io->out.netlogon.data.nt5_ex);
+ talloc_free(mem_ctx);
+
+ return ret;
+}
+
+
+static const char py_net_replicate_init_doc[] = "replicate_init(samdb, lp, drspipe)\n"
+ "Setup for replicate_chunk calls.";
+
+static const char py_net_replicate_chunk_doc[] = "replicate_chunk(state, level, ctr, schema)\n"
+ "Process replication for one chunk";
+
+static const char py_net_replicate_decrypt_doc[] = "replicate_decrypt(drs, attribute, rid)\n"
+ "Decrypt (in place) a DsReplicaAttribute replicated with drs.GetNCChanges()";
+
+static const char py_net_finddc_doc[] = "finddc(flags=server_type, domain=None, address=None)\n"
+ "Find a DC with the specified 'server_type' bits. The 'domain' and/or 'address' have to be used as additional search criteria. Returns the whole netlogon struct";
+
+static PyMethodDef net_obj_methods[] = {
+ {
+ .ml_name = "join_member",
+ .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction,
+ py_net_join_member),
+ .ml_flags = METH_VARARGS|METH_KEYWORDS,
+ .ml_doc = py_net_join_member_doc
+ },
+ {
+ .ml_name = "change_password",
+ .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction,
+ py_net_change_password),
+ .ml_flags = METH_VARARGS|METH_KEYWORDS,
+ .ml_doc = py_net_change_password_doc
+ },
+ {
+ .ml_name = "set_password",
+ .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction,
+ py_net_set_password),
+ .ml_flags = METH_VARARGS|METH_KEYWORDS,
+ .ml_doc = py_net_set_password_doc
+ },
+ {
+ .ml_name = "time",
+ .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, py_net_time),
+ .ml_flags = METH_VARARGS|METH_KEYWORDS,
+ .ml_doc = py_net_time_doc
+ },
+ {
+ .ml_name = "create_user",
+ .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction,
+ py_net_user_create),
+ .ml_flags = METH_VARARGS|METH_KEYWORDS,
+ .ml_doc = py_net_create_user_doc
+ },
+ {
+ .ml_name = "delete_user",
+ .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction,
+ py_net_user_delete),
+ .ml_flags = METH_VARARGS|METH_KEYWORDS,
+ .ml_doc = py_net_delete_user_doc
+ },
+ {
+ .ml_name = "replicate_init",
+ .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction,
+ py_net_replicate_init),
+ .ml_flags = METH_VARARGS|METH_KEYWORDS,
+ .ml_doc = py_net_replicate_init_doc
+ },
+ {
+ .ml_name = "replicate_chunk",
+ .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction,
+ py_net_replicate_chunk),
+ .ml_flags = METH_VARARGS|METH_KEYWORDS,
+ .ml_doc = py_net_replicate_chunk_doc
+ },
+ {
+ .ml_name = "replicate_decrypt",
+ .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction,
+ py_net_replicate_decrypt),
+ .ml_flags = METH_VARARGS|METH_KEYWORDS,
+ .ml_doc = py_net_replicate_decrypt_doc
+ },
+ {
+ .ml_name = "finddc",
+ .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction,
+ py_net_finddc),
+ .ml_flags = METH_VARARGS|METH_KEYWORDS,
+ .ml_doc = py_net_finddc_doc
+ },
+ { .ml_name = NULL }
+};
+
+static void py_net_dealloc(py_net_Object *self)
+{
+ talloc_free(self->ev);
+ PyObject_Del(self);
+}
+
+static PyObject *net_obj_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ PyObject *py_creds, *py_lp = Py_None;
+ const char *kwnames[] = { "creds", "lp", "server", NULL };
+ py_net_Object *ret;
+ struct loadparm_context *lp;
+ const char *server_address = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Oz",
+ discard_const_p(char *, kwnames), &py_creds, &py_lp,
+ &server_address))
+ return NULL;
+
+ ret = PyObject_New(py_net_Object, type);
+ if (ret == NULL) {
+ return NULL;
+ }
+
+ /* FIXME: we really need to get a context from the caller or we may end
+ * up with 2 event contexts */
+ ret->ev = s4_event_context_init(NULL);
+ ret->mem_ctx = talloc_new(ret->ev);
+
+ lp = lpcfg_from_py_object(ret->mem_ctx, py_lp);
+ if (lp == NULL) {
+ Py_DECREF(ret);
+ return NULL;
+ }
+
+ ret->libnet_ctx = libnet_context_init(ret->ev, lp);
+ if (ret->libnet_ctx == NULL) {
+ PyErr_SetString(PyExc_RuntimeError, "Unable to initialize net");
+ Py_DECREF(ret);
+ return NULL;
+ }
+
+ ret->libnet_ctx->server_address = server_address;
+
+ ret->libnet_ctx->cred = cli_credentials_from_py_object(py_creds);
+ if (ret->libnet_ctx->cred == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected credentials object");
+ Py_DECREF(ret);
+ return NULL;
+ }
+
+ return (PyObject *)ret;
+}
+
+
+PyTypeObject py_net_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "net.Net",
+ .tp_basicsize = sizeof(py_net_Object),
+ .tp_dealloc = (destructor)py_net_dealloc,
+ .tp_methods = net_obj_methods,
+ .tp_new = net_obj_new,
+};
+
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "net",
+ .m_size = -1,
+};
+
+MODULE_INIT_FUNC(net)
+{
+ PyObject *m;
+
+ if (PyType_Ready(&py_net_Type) < 0)
+ return NULL;
+
+ m = PyModule_Create(&moduledef);
+ if (m == NULL)
+ return NULL;
+
+ Py_INCREF(&py_net_Type);
+ PyModule_AddObject(m, "Net", (PyObject *)&py_net_Type);
+ PyModule_AddIntConstant(m, "LIBNET_JOINDOMAIN_AUTOMATIC", LIBNET_JOINDOMAIN_AUTOMATIC);
+ PyModule_AddIntConstant(m, "LIBNET_JOINDOMAIN_SPECIFIED", LIBNET_JOINDOMAIN_SPECIFIED);
+ PyModule_AddIntConstant(m, "LIBNET_JOIN_AUTOMATIC", LIBNET_JOIN_AUTOMATIC);
+ PyModule_AddIntConstant(m, "LIBNET_JOIN_SPECIFIED", LIBNET_JOIN_SPECIFIED);
+
+ return m;
+}
diff --git a/source4/libnet/py_net.h b/source4/libnet/py_net.h
new file mode 100644
index 0000000..2894d47
--- /dev/null
+++ b/source4/libnet/py_net.h
@@ -0,0 +1,24 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba python bindings to libnet library
+
+ 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/>.
+*/
+
+typedef struct {
+ PyObject_HEAD
+ TALLOC_CTX *mem_ctx;
+ struct libnet_context *libnet_ctx;
+ struct tevent_context *ev;
+} py_net_Object;
diff --git a/source4/libnet/py_net_dckeytab.c b/source4/libnet/py_net_dckeytab.c
new file mode 100644
index 0000000..ad65c7d
--- /dev/null
+++ b/source4/libnet/py_net_dckeytab.c
@@ -0,0 +1,121 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008-2010
+ Copyright (C) Kamen Mazdrashki <kamen.mazdrashki@postpath.com> 2009
+ Copyright (C) Alexander Bokovoy <ab@samba.org> 2012
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <Python.h>
+#include "includes.h"
+#include "python/py3compat.h"
+#include "python/modules.h"
+#include "py_net.h"
+#include "libnet_export_keytab.h"
+
+void initdckeytab(void);
+
+static PyObject *py_net_export_keytab(py_net_Object *self, PyObject *args, PyObject *kwargs)
+{
+ struct libnet_export_keytab r;
+ TALLOC_CTX *mem_ctx;
+ const char *kwnames[] = { "keytab", "principal", NULL };
+ NTSTATUS status;
+ r.in.principal = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|z:export_keytab", discard_const_p(char *, kwnames),
+ &r.in.keytab_name,
+ &r.in.principal)) {
+ return NULL;
+ }
+
+ mem_ctx = talloc_new(self->mem_ctx);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ status = libnet_export_keytab(self->libnet_ctx, mem_ctx, &r);
+ if (NT_STATUS_IS_ERR(status)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ r.out.error_string?r.out.error_string:nt_errstr(status));
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ talloc_free(mem_ctx);
+
+ Py_RETURN_NONE;
+}
+
+static const char py_net_export_keytab_doc[] = "export_keytab(keytab, name)\n\n"
+"Export the DC keytab to a keytab file.";
+
+static PyMethodDef export_keytab_method_table[] = {
+ {"export_keytab", PY_DISCARD_FUNC_SIG(PyCFunction,
+ py_net_export_keytab),
+ METH_VARARGS|METH_KEYWORDS, py_net_export_keytab_doc},
+ { NULL, NULL, 0, NULL }
+};
+
+/*
+ * A fake Python module to inject export_keytab() method into existing samba.net.Net class.
+ * Python enforces that every loaded module actually creates Python module record in
+ * the global module table even if we don't really need that record. Thus, we initialize
+ * dckeytab module but never use it.
+ * */
+void initdckeytab(void);
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "dckeytab",
+ .m_doc = "dckeytab",
+ .m_size = -1,
+ .m_methods = NULL
+};
+
+MODULE_INIT_FUNC(dckeytab)
+{
+ PyObject *m = NULL;
+ PyObject *Net;
+ PyObject *descr;
+ int ret;
+
+ m = PyModule_Create(&moduledef);
+ if (m == NULL)
+ return m;
+
+ m = PyImport_ImportModule("samba.net");
+ if (m == NULL)
+ return m;
+
+ Net = (PyObject *)PyObject_GetAttrString(m, "Net");
+ if (Net == NULL)
+ return m;
+
+ descr = PyDescr_NewMethod((PyTypeObject*)Net, &export_keytab_method_table[0]);
+ if (descr == NULL)
+ return m;
+
+ ret = PyDict_SetItemString(((PyTypeObject*)Net)->tp_dict,
+ export_keytab_method_table[0].ml_name,
+ descr);
+ if (ret != -1) {
+ Py_DECREF(descr);
+ }
+
+ return m;
+}
diff --git a/source4/libnet/userinfo.c b/source4/libnet/userinfo.c
new file mode 100644
index 0000000..ec40b81
--- /dev/null
+++ b/source4/libnet/userinfo.c
@@ -0,0 +1,382 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Rafal Szczesniak 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/>.
+*/
+
+/*
+ a composite function for getting user information via samr pipe
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "librpc/gen_ndr/security.h"
+#include "libcli/security/security.h"
+#include "libnet/libnet.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+
+
+struct userinfo_state {
+ struct dcerpc_binding_handle *binding_handle;
+ struct policy_handle domain_handle;
+ struct policy_handle user_handle;
+ uint16_t level;
+ struct samr_LookupNames lookup;
+ struct samr_OpenUser openuser;
+ struct samr_QueryUserInfo queryuserinfo;
+ struct samr_Close samrclose;
+ union samr_UserInfo *info;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static void continue_userinfo_lookup(struct tevent_req *subreq);
+static void continue_userinfo_openuser(struct tevent_req *subreq);
+static void continue_userinfo_getuser(struct tevent_req *subreq);
+static void continue_userinfo_closeuser(struct tevent_req *subreq);
+
+
+/**
+ * Stage 1 (optional): Look for a username in SAM server.
+ */
+static void continue_userinfo_lookup(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct userinfo_state *s;
+ struct monitor_msg msg;
+ struct msg_rpc_lookup_name *msg_lookup;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct userinfo_state);
+
+ /* receive samr_Lookup reply */
+ c->status = dcerpc_samr_LookupNames_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ /* there could be a problem with name resolving itself */
+ if (!NT_STATUS_IS_OK(s->lookup.out.result)) {
+ composite_error(c, s->lookup.out.result);
+ return;
+ }
+
+ /* issue a monitor message */
+ if (s->monitor_fn) {
+ msg.type = mon_SamrLookupName;
+ msg_lookup = talloc(s, struct msg_rpc_lookup_name);
+ msg_lookup->rid = s->lookup.out.rids->ids;
+ msg_lookup->count = s->lookup.out.rids->count;
+ msg.data = (void*)msg_lookup;
+ msg.data_size = sizeof(*msg_lookup);
+
+ s->monitor_fn(&msg);
+ }
+
+
+ /* have we actually got name resolved
+ - we're looking for only one at the moment */
+ if (s->lookup.out.rids->count != s->lookup.in.num_names) {
+ composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+ if (s->lookup.out.types->count != s->lookup.in.num_names) {
+ composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ /* TODO: find proper status code for more than one rid found */
+
+ /* prepare parameters for LookupNames */
+ s->openuser.in.domain_handle = &s->domain_handle;
+ s->openuser.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ s->openuser.in.rid = s->lookup.out.rids->ids[0];
+ s->openuser.out.user_handle = &s->user_handle;
+
+ /* send request */
+ subreq = dcerpc_samr_OpenUser_r_send(s, c->event_ctx,
+ s->binding_handle,
+ &s->openuser);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_userinfo_openuser, c);
+}
+
+
+/**
+ * Stage 2: Open user policy handle.
+ */
+static void continue_userinfo_openuser(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct userinfo_state *s;
+ struct monitor_msg msg;
+ struct msg_rpc_open_user *msg_open;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct userinfo_state);
+
+ /* receive samr_OpenUser reply */
+ c->status = dcerpc_samr_OpenUser_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ if (!NT_STATUS_IS_OK(s->openuser.out.result)) {
+ composite_error(c, s->openuser.out.result);
+ return;
+ }
+
+ /* issue a monitor message */
+ if (s->monitor_fn) {
+ msg.type = mon_SamrOpenUser;
+ msg_open = talloc(s, struct msg_rpc_open_user);
+ msg_open->rid = s->openuser.in.rid;
+ msg_open->access_mask = s->openuser.in.access_mask;
+ msg.data = (void*)msg_open;
+ msg.data_size = sizeof(*msg_open);
+
+ s->monitor_fn(&msg);
+ }
+
+ /* prepare parameters for QueryUserInfo call */
+ s->queryuserinfo.in.user_handle = &s->user_handle;
+ s->queryuserinfo.in.level = s->level;
+ s->queryuserinfo.out.info = talloc(s, union samr_UserInfo *);
+ if (composite_nomem(s->queryuserinfo.out.info, c)) return;
+
+ /* queue rpc call, set event handling and new state */
+ subreq = dcerpc_samr_QueryUserInfo_r_send(s, c->event_ctx,
+ s->binding_handle,
+ &s->queryuserinfo);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_userinfo_getuser, c);
+}
+
+
+/**
+ * Stage 3: Get requested user information.
+ */
+static void continue_userinfo_getuser(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct userinfo_state *s;
+ struct monitor_msg msg;
+ struct msg_rpc_query_user *msg_query;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct userinfo_state);
+
+ /* receive samr_QueryUserInfo reply */
+ c->status = dcerpc_samr_QueryUserInfo_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ /* check if queryuser itself went ok */
+ if (!NT_STATUS_IS_OK(s->queryuserinfo.out.result)) {
+ composite_error(c, s->queryuserinfo.out.result);
+ return;
+ }
+
+ s->info = talloc_steal(s, *(s->queryuserinfo.out.info));
+
+ /* issue a monitor message */
+ if (s->monitor_fn) {
+ msg.type = mon_SamrQueryUser;
+ msg_query = talloc(s, struct msg_rpc_query_user);
+ msg_query->level = s->queryuserinfo.in.level;
+ msg.data = (void*)msg_query;
+ msg.data_size = sizeof(*msg_query);
+
+ s->monitor_fn(&msg);
+ }
+
+ /* prepare arguments for Close call */
+ s->samrclose.in.handle = &s->user_handle;
+ s->samrclose.out.handle = &s->user_handle;
+
+ /* queue rpc call, set event handling and new state */
+ subreq = dcerpc_samr_Close_r_send(s, c->event_ctx,
+ s->binding_handle,
+ &s->samrclose);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_userinfo_closeuser, c);
+}
+
+
+/**
+ * Stage 4: Close policy handle associated with opened user.
+ */
+static void continue_userinfo_closeuser(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct userinfo_state *s;
+ struct monitor_msg msg;
+ struct msg_rpc_close_user *msg_close;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct userinfo_state);
+
+ /* receive samr_Close reply */
+ c->status = dcerpc_samr_Close_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ if (!NT_STATUS_IS_OK(s->samrclose.out.result)) {
+ composite_error(c, s->samrclose.out.result);
+ return;
+ }
+
+ /* issue a monitor message */
+ if (s->monitor_fn) {
+ msg.type = mon_SamrClose;
+ msg_close = talloc(s, struct msg_rpc_close_user);
+ msg_close->rid = s->openuser.in.rid;
+ msg.data = (void*)msg_close;
+ msg.data_size = sizeof(*msg_close);
+
+ s->monitor_fn(&msg);
+ }
+
+ composite_done(c);
+}
+
+
+/**
+ * Sends asynchronous userinfo request
+ *
+ * @param p dce/rpc call pipe
+ * @param io arguments and results of the call
+ */
+struct composite_context *libnet_rpc_userinfo_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dcerpc_binding_handle *b,
+ struct libnet_rpc_userinfo *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct userinfo_state *s;
+ struct dom_sid *sid;
+ struct tevent_req *subreq;
+
+ if (!b || !io) return NULL;
+
+ c = composite_create(mem_ctx, ev);
+ if (c == NULL) return c;
+
+ s = talloc_zero(c, struct userinfo_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ s->level = io->in.level;
+ s->binding_handle= b;
+ s->domain_handle = io->in.domain_handle;
+ s->monitor_fn = monitor;
+
+ if (io->in.sid) {
+ sid = dom_sid_parse_talloc(s, io->in.sid);
+ if (composite_nomem(sid, c)) return c;
+
+ s->openuser.in.domain_handle = &s->domain_handle;
+ s->openuser.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ s->openuser.in.rid = sid->sub_auths[sid->num_auths - 1];
+ s->openuser.out.user_handle = &s->user_handle;
+
+ /* send request */
+ subreq = dcerpc_samr_OpenUser_r_send(s, c->event_ctx,
+ s->binding_handle,
+ &s->openuser);
+ if (composite_nomem(subreq, c)) return c;
+
+ tevent_req_set_callback(subreq, continue_userinfo_openuser, c);
+
+ } else {
+ /* preparing parameters to send rpc request */
+ s->lookup.in.domain_handle = &s->domain_handle;
+ s->lookup.in.num_names = 1;
+ s->lookup.in.names = talloc_array(s, struct lsa_String, 1);
+ if (composite_nomem(s->lookup.in.names, c)) return c;
+ s->lookup.out.rids = talloc_zero(s, struct samr_Ids);
+ s->lookup.out.types = talloc_zero(s, struct samr_Ids);
+ if (composite_nomem(s->lookup.out.rids, c)) return c;
+ if (composite_nomem(s->lookup.out.types, c)) return c;
+
+ s->lookup.in.names[0].string = talloc_strdup(s, io->in.username);
+ if (composite_nomem(s->lookup.in.names[0].string, c)) return c;
+
+ /* send request */
+ subreq = dcerpc_samr_LookupNames_r_send(s, c->event_ctx,
+ s->binding_handle,
+ &s->lookup);
+ if (composite_nomem(subreq, c)) return c;
+
+ tevent_req_set_callback(subreq, continue_userinfo_lookup, c);
+ }
+
+ return c;
+}
+
+
+/**
+ * Waits for and receives result of asynchronous userinfo call
+ *
+ * @param c composite context returned by asynchronous userinfo call
+ * @param mem_ctx memory context of the call
+ * @param io pointer to results (and arguments) of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_rpc_userinfo_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_rpc_userinfo *io)
+{
+ NTSTATUS status;
+ struct userinfo_state *s;
+
+ /* wait for results of sending request */
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status) && io) {
+ s = talloc_get_type_abort(c->private_data, struct userinfo_state);
+ talloc_steal(mem_ctx, s->info);
+ io->out.info = *s->info;
+ }
+
+ /* memory context associated to composite context is no longer needed */
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Synchronous version of userinfo call
+ *
+ * @param pipe dce/rpc call pipe
+ * @param mem_ctx memory context for the call
+ * @param io arguments and results of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_rpc_userinfo(struct tevent_context *ev,
+ struct dcerpc_binding_handle *b,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_rpc_userinfo *io)
+{
+ struct composite_context *c = libnet_rpc_userinfo_send(mem_ctx, ev, b, io, NULL);
+ return libnet_rpc_userinfo_recv(c, mem_ctx, io);
+}
diff --git a/source4/libnet/userinfo.h b/source4/libnet/userinfo.h
new file mode 100644
index 0000000..273ad87
--- /dev/null
+++ b/source4/libnet/userinfo.h
@@ -0,0 +1,54 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Rafal Szczesniak 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 "librpc/gen_ndr/samr.h"
+
+/*
+ * IO structures for userinfo.c functions
+ */
+
+struct libnet_rpc_userinfo {
+ struct {
+ struct policy_handle domain_handle;
+ const char *username;
+ const char *sid;
+ uint16_t level;
+ } in;
+ struct {
+ union samr_UserInfo info;
+ } out;
+};
+
+
+/*
+ * Monitor messages sent from userinfo.c functions
+ */
+
+struct msg_rpc_open_user {
+ uint32_t rid, access_mask;
+};
+
+struct msg_rpc_query_user {
+ uint16_t level;
+};
+
+struct msg_rpc_close_user {
+ uint32_t rid;
+};
diff --git a/source4/libnet/userman.c b/source4/libnet/userman.c
new file mode 100644
index 0000000..9e76364
--- /dev/null
+++ b/source4/libnet/userman.c
@@ -0,0 +1,922 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Rafal Szczesniak 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/>.
+*/
+
+/*
+ a composite functions for user management operations (add/del/chg)
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "libnet/libnet.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+
+/*
+ * Composite USER ADD functionality
+ */
+
+struct useradd_state {
+ struct dcerpc_binding_handle *binding_handle;
+ struct policy_handle domain_handle;
+ struct samr_CreateUser createuser;
+ struct policy_handle user_handle;
+ uint32_t user_rid;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static void continue_useradd_create(struct tevent_req *subreq);
+
+
+/**
+ * Stage 1 (and the only one for now): Create user account.
+ */
+static void continue_useradd_create(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct useradd_state *s;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type(c->private_data, struct useradd_state);
+
+ /* check rpc layer status code */
+ c->status = dcerpc_samr_CreateUser_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ /* check create user call status code */
+ c->status = s->createuser.out.result;
+
+ /* get created user account data */
+ s->user_handle = *s->createuser.out.user_handle;
+ s->user_rid = *s->createuser.out.rid;
+
+ /* issue a monitor message */
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+ struct msg_rpc_create_user rpc_create;
+
+ rpc_create.rid = *s->createuser.out.rid;
+
+ msg.type = mon_SamrCreateUser;
+ msg.data = (void*)&rpc_create;
+ msg.data_size = sizeof(rpc_create);
+
+ s->monitor_fn(&msg);
+ }
+
+ composite_done(c);
+}
+
+
+/**
+ * Sends asynchronous useradd request
+ *
+ * @param p dce/rpc call pipe
+ * @param io arguments and results of the call
+ * @param monitor monitor function for providing information about the progress
+ */
+
+struct composite_context *libnet_rpc_useradd_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dcerpc_binding_handle *b,
+ struct libnet_rpc_useradd *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct useradd_state *s;
+ struct tevent_req *subreq;
+
+ if (!b || !io) return NULL;
+
+ /* composite allocation and setup */
+ c = composite_create(mem_ctx, ev);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct useradd_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ /* put passed arguments to the state structure */
+ s->domain_handle = io->in.domain_handle;
+ s->binding_handle= b;
+ s->monitor_fn = monitor;
+
+ /* preparing parameters to send rpc request */
+ s->createuser.in.domain_handle = &io->in.domain_handle;
+
+ s->createuser.in.account_name = talloc_zero(c, struct lsa_String);
+ if (composite_nomem(s->createuser.in.account_name, c)) return c;
+
+ s->createuser.in.account_name->string = talloc_strdup(c, io->in.username);
+ if (composite_nomem(s->createuser.in.account_name->string, c)) return c;
+
+ s->createuser.out.user_handle = &s->user_handle;
+ s->createuser.out.rid = &s->user_rid;
+
+ /* send the request */
+ subreq = dcerpc_samr_CreateUser_r_send(s, c->event_ctx,
+ s->binding_handle,
+ &s->createuser);
+ if (composite_nomem(subreq, c)) return c;
+
+ tevent_req_set_callback(subreq, continue_useradd_create, c);
+ return c;
+}
+
+
+/**
+ * Waits for and receives result of asynchronous useradd call
+ *
+ * @param c composite context returned by asynchronous useradd call
+ * @param mem_ctx memory context of the call
+ * @param io pointer to results (and arguments) of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_rpc_useradd_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_rpc_useradd *io)
+{
+ NTSTATUS status;
+ struct useradd_state *s;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status) && io) {
+ /* get and return result of the call */
+ s = talloc_get_type(c->private_data, struct useradd_state);
+ io->out.user_handle = s->user_handle;
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Synchronous version of useradd call
+ *
+ * @param pipe dce/rpc call pipe
+ * @param mem_ctx memory context for the call
+ * @param io arguments and results of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_rpc_useradd(struct tevent_context *ev,
+ struct dcerpc_binding_handle *b,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_rpc_useradd *io)
+{
+ struct composite_context *c = libnet_rpc_useradd_send(mem_ctx, ev, b, io, NULL);
+ return libnet_rpc_useradd_recv(c, mem_ctx, io);
+}
+
+
+
+/*
+ * Composite USER DELETE functionality
+ */
+
+
+struct userdel_state {
+ struct dcerpc_binding_handle *binding_handle;
+ struct policy_handle domain_handle;
+ struct policy_handle user_handle;
+ struct samr_LookupNames lookupname;
+ struct samr_OpenUser openuser;
+ struct samr_DeleteUser deleteuser;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static void continue_userdel_name_found(struct tevent_req *subreq);
+static void continue_userdel_user_opened(struct tevent_req *subreq);
+static void continue_userdel_deleted(struct tevent_req *subreq);
+
+
+/**
+ * Stage 1: Lookup the user name and resolve it to rid
+ */
+static void continue_userdel_name_found(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct userdel_state *s;
+ struct monitor_msg msg;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type(c->private_data, struct userdel_state);
+
+ /* receive samr_LookupNames result */
+ c->status = dcerpc_samr_LookupNames_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ c->status = s->lookupname.out.result;
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ /* what to do when there's no user account to delete
+ and what if there's more than one rid resolved */
+ if (s->lookupname.out.rids->count != s->lookupname.in.num_names) {
+ composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+ if (s->lookupname.out.types->count != s->lookupname.in.num_names) {
+ composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ /* issue a monitor message */
+ if (s->monitor_fn) {
+ struct msg_rpc_lookup_name msg_lookup;
+
+ msg_lookup.rid = s->lookupname.out.rids->ids;
+ msg_lookup.count = s->lookupname.out.rids->count;
+
+ msg.type = mon_SamrLookupName;
+ msg.data = (void*)&msg_lookup;
+ msg.data_size = sizeof(msg_lookup);
+ s->monitor_fn(&msg);
+ }
+
+ /* prepare the arguments for rpc call */
+ s->openuser.in.domain_handle = &s->domain_handle;
+ s->openuser.in.rid = s->lookupname.out.rids->ids[0];
+ s->openuser.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ s->openuser.out.user_handle = &s->user_handle;
+
+ /* send rpc request */
+ subreq = dcerpc_samr_OpenUser_r_send(s, c->event_ctx,
+ s->binding_handle,
+ &s->openuser);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_userdel_user_opened, c);
+}
+
+
+/**
+ * Stage 2: Open user account.
+ */
+static void continue_userdel_user_opened(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct userdel_state *s;
+ struct monitor_msg msg;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type(c->private_data, struct userdel_state);
+
+ /* receive samr_OpenUser result */
+ c->status = dcerpc_samr_OpenUser_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ c->status = s->openuser.out.result;
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ /* issue a monitor message */
+ if (s->monitor_fn) {
+ struct msg_rpc_open_user msg_open;
+
+ msg_open.rid = s->openuser.in.rid;
+ msg_open.access_mask = s->openuser.in.access_mask;
+
+ msg.type = mon_SamrOpenUser;
+ msg.data = (void*)&msg_open;
+ msg.data_size = sizeof(msg_open);
+ s->monitor_fn(&msg);
+ }
+
+ /* prepare the final rpc call arguments */
+ s->deleteuser.in.user_handle = &s->user_handle;
+ s->deleteuser.out.user_handle = &s->user_handle;
+
+ /* send rpc request */
+ subreq = dcerpc_samr_DeleteUser_r_send(s, c->event_ctx,
+ s->binding_handle,
+ &s->deleteuser);
+ if (composite_nomem(subreq, c)) return;
+
+ /* callback handler setup */
+ tevent_req_set_callback(subreq, continue_userdel_deleted, c);
+}
+
+
+/**
+ * Stage 3: Delete user account
+ */
+static void continue_userdel_deleted(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct userdel_state *s;
+ struct monitor_msg msg;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type(c->private_data, struct userdel_state);
+
+ /* receive samr_DeleteUser result */
+ c->status = dcerpc_samr_DeleteUser_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ /* return the actual function call status */
+ c->status = s->deleteuser.out.result;
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ /* issue a monitor message */
+ if (s->monitor_fn) {
+ msg.type = mon_SamrDeleteUser;
+ msg.data = NULL;
+ msg.data_size = 0;
+ s->monitor_fn(&msg);
+ }
+
+ composite_done(c);
+}
+
+
+/**
+ * Sends asynchronous userdel request
+ *
+ * @param p dce/rpc call pipe
+ * @param io arguments and results of the call
+ * @param monitor monitor function for providing information about the progress
+ */
+
+struct composite_context *libnet_rpc_userdel_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dcerpc_binding_handle *b,
+ struct libnet_rpc_userdel *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct userdel_state *s;
+ struct tevent_req *subreq;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, ev);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct userdel_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ /* store function parameters in the state structure */
+ s->binding_handle= b;
+ s->domain_handle = io->in.domain_handle;
+ s->monitor_fn = monitor;
+
+ /* preparing parameters to send rpc request */
+ s->lookupname.in.domain_handle = &io->in.domain_handle;
+ s->lookupname.in.num_names = 1;
+ s->lookupname.in.names = talloc_zero(s, struct lsa_String);
+ s->lookupname.in.names->string = io->in.username;
+ s->lookupname.out.rids = talloc_zero(s, struct samr_Ids);
+ s->lookupname.out.types = talloc_zero(s, struct samr_Ids);
+ if (composite_nomem(s->lookupname.out.rids, c)) return c;
+ if (composite_nomem(s->lookupname.out.types, c)) return c;
+
+ /* send the request */
+ subreq = dcerpc_samr_LookupNames_r_send(s, c->event_ctx,
+ s->binding_handle,
+ &s->lookupname);
+ if (composite_nomem(subreq, c)) return c;
+
+ /* set the next stage */
+ tevent_req_set_callback(subreq, continue_userdel_name_found, c);
+ return c;
+}
+
+
+/**
+ * Waits for and receives results of asynchronous userdel call
+ *
+ * @param c composite context returned by asynchronous userdel call
+ * @param mem_ctx memory context of the call
+ * @param io pointer to results (and arguments) of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_rpc_userdel_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_rpc_userdel *io)
+{
+ NTSTATUS status;
+ struct userdel_state *s;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status) && io) {
+ s = talloc_get_type(c->private_data, struct userdel_state);
+ io->out.user_handle = s->user_handle;
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Synchronous version of userdel call
+ *
+ * @param pipe dce/rpc call pipe
+ * @param mem_ctx memory context for the call
+ * @param io arguments and results of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_rpc_userdel(struct tevent_context *ev,
+ struct dcerpc_binding_handle *b,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_rpc_userdel *io)
+{
+ struct composite_context *c = libnet_rpc_userdel_send(mem_ctx, ev, b, io, NULL);
+ return libnet_rpc_userdel_recv(c, mem_ctx, io);
+}
+
+
+/*
+ * USER MODIFY functionality
+ */
+
+static void continue_usermod_name_found(struct tevent_req *subreq);
+static void continue_usermod_user_opened(struct tevent_req *subreq);
+static void continue_usermod_user_queried(struct tevent_req *subreq);
+static void continue_usermod_user_changed(struct tevent_req *subreq);
+
+
+struct usermod_state {
+ struct dcerpc_binding_handle *binding_handle;
+ struct policy_handle domain_handle;
+ struct policy_handle user_handle;
+ struct usermod_change change;
+ union samr_UserInfo info;
+ struct samr_LookupNames lookupname;
+ struct samr_OpenUser openuser;
+ struct samr_SetUserInfo setuser;
+ struct samr_QueryUserInfo queryuser;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+/**
+ * Step 1: Lookup user name
+ */
+static void continue_usermod_name_found(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct usermod_state *s;
+ struct monitor_msg msg;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type(c->private_data, struct usermod_state);
+
+ /* receive samr_LookupNames result */
+ c->status = dcerpc_samr_LookupNames_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ c->status = s->lookupname.out.result;
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ /* what to do when there's no user account to delete
+ and what if there's more than one rid resolved */
+ if (s->lookupname.out.rids->count != s->lookupname.in.num_names) {
+ composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+ if (s->lookupname.out.types->count != s->lookupname.in.num_names) {
+ composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ /* issue a monitor message */
+ if (s->monitor_fn) {
+ struct msg_rpc_lookup_name msg_lookup;
+
+ msg_lookup.rid = s->lookupname.out.rids->ids;
+ msg_lookup.count = s->lookupname.out.rids->count;
+
+ msg.type = mon_SamrLookupName;
+ msg.data = (void*)&msg_lookup;
+ msg.data_size = sizeof(msg_lookup);
+ s->monitor_fn(&msg);
+ }
+
+ /* prepare the next rpc call */
+ s->openuser.in.domain_handle = &s->domain_handle;
+ s->openuser.in.rid = s->lookupname.out.rids->ids[0];
+ s->openuser.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ s->openuser.out.user_handle = &s->user_handle;
+
+ /* send the rpc request */
+ subreq = dcerpc_samr_OpenUser_r_send(s, c->event_ctx,
+ s->binding_handle,
+ &s->openuser);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_usermod_user_opened, c);
+}
+
+
+/**
+ * Choose a proper level of samr_UserInfo structure depending on required
+ * change specified by means of flags field. Subsequent calls of this
+ * function are made until there's no flags set meaning that all of the
+ * changes have been made.
+ */
+static bool usermod_setfields(struct usermod_state *s, uint16_t *level,
+ union samr_UserInfo *i, bool queried)
+{
+ if (s->change.fields == 0) return s->change.fields;
+
+ *level = 0;
+
+ if ((s->change.fields & USERMOD_FIELD_ACCOUNT_NAME) &&
+ (*level == 0 || *level == 7)) {
+ *level = 7;
+ i->info7.account_name.string = s->change.account_name;
+
+ s->change.fields ^= USERMOD_FIELD_ACCOUNT_NAME;
+ }
+
+ if ((s->change.fields & USERMOD_FIELD_FULL_NAME) &&
+ (*level == 0 || *level == 8)) {
+ *level = 8;
+ i->info8.full_name.string = s->change.full_name;
+
+ s->change.fields ^= USERMOD_FIELD_FULL_NAME;
+ }
+
+ if ((s->change.fields & USERMOD_FIELD_DESCRIPTION) &&
+ (*level == 0 || *level == 13)) {
+ *level = 13;
+ i->info13.description.string = s->change.description;
+
+ s->change.fields ^= USERMOD_FIELD_DESCRIPTION;
+ }
+
+ if ((s->change.fields & USERMOD_FIELD_COMMENT) &&
+ (*level == 0 || *level == 2)) {
+ *level = 2;
+
+ if (queried) {
+ /* the user info is obtained, so now set the required field */
+ i->info2.comment.string = s->change.comment;
+ s->change.fields ^= USERMOD_FIELD_COMMENT;
+
+ } else {
+ /* we need to query the user info before setting one field in it */
+ return false;
+ }
+ }
+
+ if ((s->change.fields & USERMOD_FIELD_LOGON_SCRIPT) &&
+ (*level == 0 || *level == 11)) {
+ *level = 11;
+ i->info11.logon_script.string = s->change.logon_script;
+
+ s->change.fields ^= USERMOD_FIELD_LOGON_SCRIPT;
+ }
+
+ if ((s->change.fields & USERMOD_FIELD_PROFILE_PATH) &&
+ (*level == 0 || *level == 12)) {
+ *level = 12;
+ i->info12.profile_path.string = s->change.profile_path;
+
+ s->change.fields ^= USERMOD_FIELD_PROFILE_PATH;
+ }
+
+ if ((s->change.fields & USERMOD_FIELD_HOME_DIRECTORY) &&
+ (*level == 0 || *level == 10)) {
+ *level = 10;
+
+ if (queried) {
+ i->info10.home_directory.string = s->change.home_directory;
+ s->change.fields ^= USERMOD_FIELD_HOME_DIRECTORY;
+ } else {
+ return false;
+ }
+ }
+
+ if ((s->change.fields & USERMOD_FIELD_HOME_DRIVE) &&
+ (*level == 0 || *level == 10)) {
+ *level = 10;
+
+ if (queried) {
+ i->info10.home_drive.string = s->change.home_drive;
+ s->change.fields ^= USERMOD_FIELD_HOME_DRIVE;
+ } else {
+ return false;
+ }
+ }
+
+ if ((s->change.fields & USERMOD_FIELD_ACCT_EXPIRY) &&
+ (*level == 0 || *level == 17)) {
+ *level = 17;
+ i->info17.acct_expiry = timeval_to_nttime(s->change.acct_expiry);
+
+ s->change.fields ^= USERMOD_FIELD_ACCT_EXPIRY;
+ }
+
+ if ((s->change.fields & USERMOD_FIELD_ACCT_FLAGS) &&
+ (*level == 0 || *level == 16)) {
+ *level = 16;
+ i->info16.acct_flags = s->change.acct_flags;
+
+ s->change.fields ^= USERMOD_FIELD_ACCT_FLAGS;
+ }
+
+ /* We're going to be here back again soon unless all fields have been set */
+ return true;
+}
+
+
+static NTSTATUS usermod_change(struct composite_context *c,
+ struct usermod_state *s)
+{
+ bool do_set;
+ union samr_UserInfo *i = &s->info;
+ struct tevent_req *subreq;
+
+ /* set the level to invalid value, so that unless setfields routine
+ gives it a valid value we report the error correctly */
+ uint16_t level = 27;
+
+ /* prepare UserInfo level and data based on bitmask field */
+ do_set = usermod_setfields(s, &level, i, false);
+
+ if (level < 1 || level > 26) {
+ /* apparently there's a field that the setfields routine
+ does not know how to set */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* If some specific level is used to set user account data and the change
+ itself does not cover all fields then we need to query the user info
+ first, right before changing the data. Otherwise we could set required
+ fields and accidentally reset the others.
+ */
+ if (!do_set) {
+ s->queryuser.in.user_handle = &s->user_handle;
+ s->queryuser.in.level = level;
+ s->queryuser.out.info = talloc(s, union samr_UserInfo *);
+ if (composite_nomem(s->queryuser.out.info, c)) return NT_STATUS_NO_MEMORY;
+
+
+ /* send query user info request to retrieve complete data of
+ a particular info level */
+ subreq = dcerpc_samr_QueryUserInfo_r_send(s, c->event_ctx,
+ s->binding_handle,
+ &s->queryuser);
+ if (composite_nomem(subreq, c)) return NT_STATUS_NO_MEMORY;
+ tevent_req_set_callback(subreq, continue_usermod_user_queried, c);
+
+ } else {
+ s->setuser.in.user_handle = &s->user_handle;
+ s->setuser.in.level = level;
+ s->setuser.in.info = i;
+
+ /* send set user info request after making required change */
+ subreq = dcerpc_samr_SetUserInfo_r_send(s, c->event_ctx,
+ s->binding_handle,
+ &s->setuser);
+ if (composite_nomem(subreq, c)) return NT_STATUS_NO_MEMORY;
+ tevent_req_set_callback(subreq, continue_usermod_user_changed, c);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/**
+ * Stage 2: Open user account
+ */
+static void continue_usermod_user_opened(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct usermod_state *s;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type(c->private_data, struct usermod_state);
+
+ c->status = dcerpc_samr_OpenUser_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ c->status = s->openuser.out.result;
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ c->status = usermod_change(c, s);
+}
+
+
+/**
+ * Stage 2a (optional): Query the user information
+ */
+static void continue_usermod_user_queried(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct usermod_state *s;
+ union samr_UserInfo *i;
+ uint16_t level = 0;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type(c->private_data, struct usermod_state);
+
+ i = &s->info;
+
+ /* receive samr_QueryUserInfo result */
+ c->status = dcerpc_samr_QueryUserInfo_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ c->status = s->queryuser.out.result;
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ /* get returned user data and make a change (potentially one
+ of many) */
+ s->info = *(*s->queryuser.out.info);
+
+ usermod_setfields(s, &level, i, true);
+
+ /* prepare rpc call arguments */
+ s->setuser.in.user_handle = &s->user_handle;
+ s->setuser.in.level = level;
+ s->setuser.in.info = i;
+
+ /* send the rpc request */
+ subreq = dcerpc_samr_SetUserInfo_r_send(s, c->event_ctx,
+ s->binding_handle,
+ &s->setuser);
+ if (composite_nomem(subreq, c)) return;
+ tevent_req_set_callback(subreq, continue_usermod_user_changed, c);
+}
+
+
+/**
+ * Stage 3: Set new user account data
+ */
+static void continue_usermod_user_changed(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct usermod_state *s;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type(c->private_data, struct usermod_state);
+
+ /* receive samr_SetUserInfo result */
+ c->status = dcerpc_samr_SetUserInfo_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ /* return the actual function call status */
+ c->status = s->setuser.out.result;
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ if (s->change.fields == 0) {
+ /* all fields have been set - we're done */
+ composite_done(c);
+
+ } else {
+ /* something's still not changed - repeat the procedure */
+ c->status = usermod_change(c, s);
+ }
+}
+
+
+/**
+ * Sends asynchronous usermod request
+ *
+ * @param p dce/rpc call pipe
+ * @param io arguments and results of the call
+ * @param monitor monitor function for providing information about the progress
+ */
+
+struct composite_context *libnet_rpc_usermod_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dcerpc_binding_handle *b,
+ struct libnet_rpc_usermod *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct usermod_state *s;
+ struct tevent_req *subreq;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, ev);
+ if (c == NULL) return NULL;
+ s = talloc_zero(c, struct usermod_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ /* store parameters in the call structure */
+ s->binding_handle= b;
+ s->domain_handle = io->in.domain_handle;
+ s->change = io->in.change;
+ s->monitor_fn = monitor;
+
+ /* prepare rpc call arguments */
+ s->lookupname.in.domain_handle = &io->in.domain_handle;
+ s->lookupname.in.num_names = 1;
+ s->lookupname.in.names = talloc_zero(s, struct lsa_String);
+ s->lookupname.in.names->string = io->in.username;
+ s->lookupname.out.rids = talloc_zero(s, struct samr_Ids);
+ s->lookupname.out.types = talloc_zero(s, struct samr_Ids);
+ if (composite_nomem(s->lookupname.out.rids, c)) return c;
+ if (composite_nomem(s->lookupname.out.types, c)) return c;
+
+ /* send the rpc request */
+ subreq = dcerpc_samr_LookupNames_r_send(s, c->event_ctx,
+ s->binding_handle,
+ &s->lookupname);
+ if (composite_nomem(subreq, c)) return c;
+
+ /* callback handler setup */
+ tevent_req_set_callback(subreq, continue_usermod_name_found, c);
+ return c;
+}
+
+
+/**
+ * Waits for and receives results of asynchronous usermod call
+ *
+ * @param c composite context returned by asynchronous usermod call
+ * @param mem_ctx memory context of the call
+ * @param io pointer to results (and arguments) of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_rpc_usermod_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_rpc_usermod *io)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Synchronous version of usermod call
+ *
+ * @param pipe dce/rpc call pipe
+ * @param mem_ctx memory context for the call
+ * @param io arguments and results of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_rpc_usermod(struct tevent_context *ev,
+ struct dcerpc_binding_handle *b,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_rpc_usermod *io)
+{
+ struct composite_context *c = libnet_rpc_usermod_send(mem_ctx, ev, b, io, NULL);
+ return libnet_rpc_usermod_recv(c, mem_ctx, io);
+}
diff --git a/source4/libnet/userman.h b/source4/libnet/userman.h
new file mode 100644
index 0000000..b681c25
--- /dev/null
+++ b/source4/libnet/userman.h
@@ -0,0 +1,106 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Rafal Szczesniak 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 "librpc/gen_ndr/misc.h"
+
+
+/*
+ * IO structures for userman.c functions
+ */
+
+struct libnet_rpc_useradd {
+ struct {
+ struct policy_handle domain_handle;
+ const char *username;
+ } in;
+ struct {
+ struct policy_handle user_handle;
+ } out;
+};
+
+
+struct libnet_rpc_userdel {
+ struct {
+ struct policy_handle domain_handle;
+ const char *username;
+ } in;
+ struct {
+ struct policy_handle user_handle;
+ } out;
+};
+
+
+#define USERMOD_FIELD_ACCOUNT_NAME ( 0x00000001 )
+#define USERMOD_FIELD_FULL_NAME ( 0x00000002 )
+#define USERMOD_FIELD_DESCRIPTION ( 0x00000010 )
+#define USERMOD_FIELD_COMMENT ( 0x00000020 )
+#define USERMOD_FIELD_HOME_DIRECTORY ( 0x00000040 )
+#define USERMOD_FIELD_HOME_DRIVE ( 0x00000080 )
+#define USERMOD_FIELD_LOGON_SCRIPT ( 0x00000100 )
+#define USERMOD_FIELD_PROFILE_PATH ( 0x00000200 )
+#define USERMOD_FIELD_WORKSTATIONS ( 0x00000400 )
+#define USERMOD_FIELD_LOGON_HOURS ( 0x00002000 )
+#define USERMOD_FIELD_ACCT_EXPIRY ( 0x00004000 )
+#define USERMOD_FIELD_ACCT_FLAGS ( 0x00100000 )
+#define USERMOD_FIELD_PARAMETERS ( 0x00200000 )
+#define USERMOD_FIELD_COUNTRY_CODE ( 0x00400000 )
+#define USERMOD_FIELD_CODE_PAGE ( 0x00800000 )
+
+struct libnet_rpc_usermod {
+ struct {
+ struct policy_handle domain_handle;
+ const char *username;
+
+ struct usermod_change {
+ uint32_t fields; /* bitmask field */
+
+ const char *account_name;
+ const char *full_name;
+ const char *description;
+ const char *comment;
+ const char *logon_script;
+ const char *profile_path;
+ const char *home_directory;
+ const char *home_drive;
+ const char *workstations;
+ struct timeval *acct_expiry;
+ struct timeval *allow_password_change;
+ struct timeval *force_password_change;
+ struct timeval *last_logon;
+ struct timeval *last_logoff;
+ struct timeval *last_password_change;
+ uint32_t acct_flags;
+ } change;
+ } in;
+};
+
+
+/*
+ * Monitor messages sent from userman.c functions
+ */
+
+struct msg_rpc_create_user {
+ uint32_t rid;
+};
+
+
+struct msg_rpc_lookup_name {
+ uint32_t *rid;
+ uint32_t count;
+};
diff --git a/source4/libnet/wscript_build b/source4/libnet/wscript_build
new file mode 100644
index 0000000..0ec06f2
--- /dev/null
+++ b/source4/libnet/wscript_build
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+
+pytalloc_util = bld.pyembed_libname('pytalloc-util')
+pyrpc_util = bld.pyembed_libname('pyrpc_util')
+provision = bld.pyembed_libname('PROVISION')
+name = bld.pyembed_libname('samba-net')
+auto_proto='libnet_proto.h'
+bld.SAMBA_LIBRARY(name,
+ source='libnet.c libnet_passwd.c libnet_time.c libnet_rpc.c libnet_join.c libnet_site.c libnet_become_dc.c libnet_unbecome_dc.c libnet_vampire.c libnet_user.c libnet_group.c libnet_share.c libnet_lookup.c libnet_domain.c userinfo.c groupinfo.c userman.c groupman.c prereq_domain.c',
+ autoproto=auto_proto,
+ deps='INIT_SAMR',
+ public_deps='samba-credentials dcerpc dcerpc-samr RPC_NDR_LSA RPC_NDR_SRVSVC RPC_NDR_DRSUAPI cli_composite LIBCLI_RESOLVE LIBCLI_FINDDCS cli_cldap LIBCLI_FINDDCS gensec_schannel LIBCLI_AUTH ndr smbpasswdparser %s LIBCLI_SAMSYNC LIBTSOCKET GNUTLS_HELPERS' % (provision),
+ private_library=True,
+ pyembed=True,
+ enabled=bld.PYTHON_BUILD_IS_ENABLED()
+ )
+
+bld.SAMBA_PYTHON('python_net',
+ source='py_net.c',
+ deps='%s %s %s' % (name, pyrpc_util, pytalloc_util),
+ realname='samba/net.so'
+ )
+
+bld.SAMBA_PYTHON('python_dckeytab',
+ source='py_net_dckeytab.c libnet_export_keytab.c',
+ deps='%s db-glue krb5 com_err' % (pyrpc_util),
+ realname='samba/dckeytab.so',
+ enabled=bld.CONFIG_SET('AD_DC_BUILD_IS_ENABLED')
+ )