summaryrefslogtreecommitdiffstats
path: root/source3/rpc_client
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
commit8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch)
tree4099e8021376c7d8c05bdf8503093d80e9c7bad0 /source3/rpc_client
parentInitial commit. (diff)
downloadsamba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz
samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source3/rpc_client')
-rw-r--r--source3/rpc_client/cli_lsarpc.c850
-rw-r--r--source3/rpc_client/cli_lsarpc.h278
-rw-r--r--source3/rpc_client/cli_mdssvc.c1214
-rw-r--r--source3/rpc_client/cli_mdssvc.h97
-rw-r--r--source3/rpc_client/cli_mdssvc_private.h74
-rw-r--r--source3/rpc_client/cli_mdssvc_util.c551
-rw-r--r--source3/rpc_client/cli_mdssvc_util.h45
-rw-r--r--source3/rpc_client/cli_netlogon.c860
-rw-r--r--source3/rpc_client/cli_netlogon.h111
-rw-r--r--source3/rpc_client/cli_pipe.c3677
-rw-r--r--source3/rpc_client/cli_pipe.h143
-rw-r--r--source3/rpc_client/cli_pipe_schannel.c120
-rw-r--r--source3/rpc_client/cli_samr.c654
-rw-r--r--source3/rpc_client/cli_samr.h229
-rw-r--r--source3/rpc_client/cli_spoolss.c1006
-rw-r--r--source3/rpc_client/cli_spoolss.h153
-rw-r--r--source3/rpc_client/cli_winreg.c975
-rw-r--r--source3/rpc_client/cli_winreg.h449
-rw-r--r--source3/rpc_client/cli_winreg_int.c323
-rw-r--r--source3/rpc_client/cli_winreg_int.h103
-rw-r--r--source3/rpc_client/cli_winreg_spoolss.c4729
-rw-r--r--source3/rpc_client/cli_winreg_spoolss.h720
-rw-r--r--source3/rpc_client/init_lsa.c60
-rw-r--r--source3/rpc_client/init_lsa.h35
-rw-r--r--source3/rpc_client/init_samr.c126
-rw-r--r--source3/rpc_client/init_samr.h54
-rw-r--r--source3/rpc_client/init_spoolss.c479
-rw-r--r--source3/rpc_client/init_spoolss.h55
-rw-r--r--source3/rpc_client/local_np.c852
-rw-r--r--source3/rpc_client/local_np.h56
-rw-r--r--source3/rpc_client/py_mdscli.c572
-rw-r--r--source3/rpc_client/rpc_client.h52
-rw-r--r--source3/rpc_client/rpc_transport.h104
-rw-r--r--source3/rpc_client/rpc_transport_np.c179
-rw-r--r--source3/rpc_client/rpc_transport_sock.c53
-rw-r--r--source3/rpc_client/rpc_transport_tstream.c564
-rw-r--r--source3/rpc_client/util_netlogon.c449
-rw-r--r--source3/rpc_client/util_netlogon.h54
-rw-r--r--source3/rpc_client/wsp_cli.c2221
-rw-r--r--source3/rpc_client/wsp_cli.h114
40 files changed, 23440 insertions, 0 deletions
diff --git a/source3/rpc_client/cli_lsarpc.c b/source3/rpc_client/cli_lsarpc.c
new file mode 100644
index 0000000..73b4872
--- /dev/null
+++ b/source3/rpc_client/cli_lsarpc.c
@@ -0,0 +1,850 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+ Copyright (C) Tim Potter 2000-2001,
+ Copyright (C) Andrew Tridgell 1992-1997,2000,
+ Copyright (C) Rafal Szczesniak 2002
+ Copyright (C) Jeremy Allison 2005.
+ Copyright (C) Michael Adam 2007.
+ Copyright (C) Guenther Deschner 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 "rpc_client/rpc_client.h"
+#include "../librpc/gen_ndr/ndr_lsa_c.h"
+#include "rpc_client/cli_lsarpc.h"
+#include "rpc_client/init_lsa.h"
+#include "../libcli/security/security.h"
+#include "lsa.h"
+
+/** @defgroup lsa LSA - Local Security Architecture
+ * @ingroup rpc_client
+ *
+ * @{
+ **/
+
+/**
+ * @file cli_lsarpc.c
+ *
+ * RPC client routines for the LSA RPC pipe. LSA means "local
+ * security authority", which is half of a password database.
+ **/
+
+NTSTATUS dcerpc_lsa_open_policy(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ bool sec_qos,
+ uint32_t des_access,
+ struct policy_handle *pol,
+ NTSTATUS *result)
+{
+ struct lsa_ObjectAttribute attr = { .len = 0x18, };
+ struct lsa_QosInfo qos;
+ uint16_t system_name = '\\';
+
+ if (sec_qos) {
+ qos.len = 0xc;
+ qos.impersonation_level = 2;
+ qos.context_mode = 1;
+ qos.effective_only = 0;
+
+ attr.sec_qos = &qos;
+ }
+
+ return dcerpc_lsa_OpenPolicy(h,
+ mem_ctx,
+ &system_name,
+ &attr,
+ des_access,
+ pol,
+ result);
+}
+
+/** Open a LSA policy handle
+ *
+ * @param cli Handle on an initialised SMB connection */
+
+NTSTATUS rpccli_lsa_open_policy(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ bool sec_qos, uint32_t des_access,
+ struct policy_handle *pol)
+{
+ NTSTATUS status;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ status = dcerpc_lsa_open_policy(cli->binding_handle,
+ mem_ctx,
+ sec_qos,
+ des_access,
+ pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return result;
+}
+
+NTSTATUS dcerpc_lsa_open_policy2(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ const char *srv_name_slash,
+ bool sec_qos,
+ uint32_t des_access,
+ struct policy_handle *pol,
+ NTSTATUS *result)
+{
+ struct lsa_ObjectAttribute attr = { .len = 0x18, };
+ struct lsa_QosInfo qos;
+
+ if (sec_qos) {
+ qos.len = 0xc;
+ qos.impersonation_level = 2;
+ qos.context_mode = 1;
+ qos.effective_only = 0;
+
+ attr.sec_qos = &qos;
+ }
+
+ return dcerpc_lsa_OpenPolicy2(h,
+ mem_ctx,
+ srv_name_slash,
+ &attr,
+ des_access,
+ pol,
+ result);
+}
+
+NTSTATUS dcerpc_lsa_open_policy3(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ const char *srv_name_slash,
+ bool sec_qos,
+ uint32_t des_access,
+ uint32_t *out_version,
+ union lsa_revision_info *out_revision_info,
+ struct policy_handle *pol,
+ NTSTATUS *result)
+{
+ struct lsa_ObjectAttribute attr = { .len = 0x18, };
+ struct lsa_QosInfo qos;
+ union lsa_revision_info in_revision_info = {
+ .info1 = {
+ .revision = 1,
+ },
+ };
+ uint32_t in_version = 1;
+
+ if (sec_qos) {
+ qos.len = 0xc;
+ qos.impersonation_level = 2;
+ qos.context_mode = 1;
+ qos.effective_only = 0;
+
+ attr.sec_qos = &qos;
+ }
+
+ return dcerpc_lsa_OpenPolicy3(h,
+ mem_ctx,
+ srv_name_slash,
+ &attr,
+ des_access,
+ in_version,
+ &in_revision_info,
+ out_version,
+ out_revision_info,
+ pol,
+ result);
+}
+
+NTSTATUS dcerpc_lsa_open_policy_fallback(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ const char *srv_name_slash,
+ bool sec_qos,
+ uint32_t desired_access,
+ uint32_t *out_version,
+ union lsa_revision_info *out_revision_info,
+ struct policy_handle *pol,
+ NTSTATUS *result)
+{
+ NTSTATUS status;
+
+ status = dcerpc_lsa_open_policy3(h,
+ mem_ctx,
+ srv_name_slash,
+ sec_qos,
+ desired_access,
+ out_version,
+ out_revision_info,
+ pol,
+ result);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ *out_version = 1;
+ *out_revision_info = (union lsa_revision_info) {
+ .info1 = {
+ .revision = 1,
+ }
+ };
+
+ status = dcerpc_lsa_open_policy2(h,
+ mem_ctx,
+ srv_name_slash,
+ sec_qos,
+ desired_access,
+ pol,
+ result);
+ }
+
+ return status;
+}
+
+/* Lookup a list of sids
+ *
+ * internal version withOUT memory allocation of the target arrays.
+ * this assumes sufficiently sized arrays to store domains, names and types. */
+
+static NTSTATUS dcerpc_lsa_lookup_sids_noalloc(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ TALLOC_CTX *domains_ctx,
+ TALLOC_CTX *names_ctx,
+ struct policy_handle *pol,
+ int num_sids,
+ const struct dom_sid *sids,
+ enum lsa_LookupNamesLevel level,
+ char **domains,
+ char **names,
+ enum lsa_SidType *types,
+ bool use_lookupsids3,
+ NTSTATUS *presult)
+{
+ NTSTATUS status;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ struct lsa_SidArray sid_array;
+ struct lsa_RefDomainList *ref_domains = NULL;
+ struct lsa_TransNameArray lsa_names = { .count = 0, };
+ uint32_t count = 0;
+ int i;
+
+ sid_array.num_sids = num_sids;
+ sid_array.sids = talloc_array(mem_ctx, struct lsa_SidPtr, num_sids);
+ if (sid_array.sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i<num_sids; i++) {
+ sid_array.sids[i].sid = dom_sid_dup(mem_ctx, &sids[i]);
+ if (!sid_array.sids[i].sid) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ if (use_lookupsids3) {
+ struct lsa_TransNameArray2 lsa_names2;
+ uint32_t n;
+
+ ZERO_STRUCT(lsa_names2);
+
+ status = dcerpc_lsa_LookupSids3(h,
+ mem_ctx,
+ &sid_array,
+ &ref_domains,
+ &lsa_names2,
+ level,
+ &count,
+ LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES,
+ LSA_CLIENT_REVISION_2,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!NT_STATUS_LOOKUP_ERR(result)) {
+ lsa_names.count = lsa_names2.count;
+ lsa_names.names = talloc_array(mem_ctx,
+ struct lsa_TranslatedName,
+ lsa_names.count);
+ if (lsa_names.names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (n=0; n < lsa_names.count; n++) {
+ lsa_names.names[n].sid_type = lsa_names2.names[n].sid_type;
+ lsa_names.names[n].name = lsa_names2.names[n].name;
+ lsa_names.names[n].sid_index = lsa_names2.names[n].sid_index;
+ }
+ }
+
+ } else {
+ status = dcerpc_lsa_LookupSids(h,
+ mem_ctx,
+ pol,
+ &sid_array,
+ &ref_domains,
+ &lsa_names,
+ level,
+ &count,
+ &result);
+ }
+
+ DEBUG(10, ("LSA_LOOKUPSIDS returned status: '%s', result: '%s', "
+ "mapped count = %d'\n",
+ nt_errstr(status), nt_errstr(result), count));
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (NT_STATUS_LOOKUP_ERR(result)) {
+ *presult = result;
+ return status;
+ }
+
+ /* Return output parameters */
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED) ||
+ (count == 0))
+ {
+ for (i = 0; i < num_sids; i++) {
+ (names)[i] = NULL;
+ (domains)[i] = NULL;
+ (types)[i] = SID_NAME_UNKNOWN;
+ }
+ *presult = NT_STATUS_NONE_MAPPED;
+ return status;
+ }
+
+ for (i = 0; i < num_sids; i++) {
+ const char *name, *dom_name;
+ uint32_t dom_idx;
+
+ if (i >= lsa_names.count) {
+ *presult = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ return status;
+ }
+
+ dom_idx = lsa_names.names[i].sid_index;
+
+ /* Translate optimised name through domain index array */
+
+ if (dom_idx != 0xffffffff) {
+ if (ref_domains == NULL) {
+ *presult = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ return status;
+ }
+ if (dom_idx >= ref_domains->count) {
+ *presult = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ return status;
+ }
+
+ dom_name = ref_domains->domains[dom_idx].name.string;
+ name = lsa_names.names[i].name.string;
+
+ if (name) {
+ (names)[i] = talloc_strdup(names_ctx, name);
+ if ((names)[i] == NULL) {
+ DEBUG(0, ("cli_lsa_lookup_sids_noalloc(): out of memory\n"));
+ *presult = NT_STATUS_UNSUCCESSFUL;
+ return status;
+ }
+ } else {
+ (names)[i] = NULL;
+ }
+ domains[i] = talloc_strdup(domains_ctx,
+ dom_name ? dom_name : "");
+ (types)[i] = lsa_names.names[i].sid_type;
+ if ((domains)[i] == NULL) {
+ DEBUG(0, ("cli_lsa_lookup_sids_noalloc(): out of memory\n"));
+ *presult = NT_STATUS_UNSUCCESSFUL;
+ return status;
+ }
+
+ } else {
+ (names)[i] = NULL;
+ (domains)[i] = NULL;
+ (types)[i] = SID_NAME_UNKNOWN;
+ }
+ }
+
+ *presult = NT_STATUS_OK;
+ return status;
+}
+
+/* Lookup a list of sids
+ *
+ * do it the right way: there is a limit (of 20480 for w2k3) entries
+ * returned by this call. when the sids list contains more entries,
+ * empty lists are returned. This version of lsa_lookup_sids passes
+ * the list of sids in hunks of LOOKUP_SIDS_HUNK_SIZE to the lsa call. */
+
+/* This constant defines the limit of how many sids to look up
+ * in one call (maximum). the limit from the server side is
+ * at 20480 for win2k3, but we keep it at a save 1000 for now. */
+#define LOOKUP_SIDS_HUNK_SIZE 1000
+
+NTSTATUS dcerpc_lsa_lookup_sids_generic(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *pol,
+ int num_sids,
+ const struct dom_sid *sids,
+ enum lsa_LookupNamesLevel level,
+ char ***pdomains,
+ char ***pnames,
+ enum lsa_SidType **ptypes,
+ bool use_lookupsids3,
+ NTSTATUS *presult)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ NTSTATUS result = NT_STATUS_OK;
+ int sids_left = 0;
+ int sids_processed = 0;
+ const struct dom_sid *hunk_sids = sids;
+ char **hunk_domains;
+ char **hunk_names;
+ enum lsa_SidType *hunk_types;
+ char **domains = NULL;
+ char **names = NULL;
+ enum lsa_SidType *types = NULL;
+ bool have_mapped = false;
+ bool have_unmapped = false;
+
+ if (num_sids) {
+ domains = talloc_zero_array(mem_ctx, char *, num_sids);
+ if (domains == NULL) {
+ DEBUG(0, ("rpccli_lsa_lookup_sids(): out of memory\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ names = talloc_zero_array(mem_ctx, char *, num_sids);
+ if (names == NULL) {
+ DEBUG(0, ("rpccli_lsa_lookup_sids(): out of memory\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ types = talloc_zero_array(mem_ctx, enum lsa_SidType, num_sids);
+ if (types == NULL) {
+ DEBUG(0, ("rpccli_lsa_lookup_sids(): out of memory\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ }
+
+ sids_left = num_sids;
+ hunk_domains = domains;
+ hunk_names = names;
+ hunk_types = types;
+
+ while (sids_left > 0) {
+ int hunk_num_sids;
+ NTSTATUS hunk_result = NT_STATUS_UNSUCCESSFUL;
+
+ hunk_num_sids = ((sids_left > LOOKUP_SIDS_HUNK_SIZE)
+ ? LOOKUP_SIDS_HUNK_SIZE
+ : sids_left);
+
+ DEBUG(10, ("rpccli_lsa_lookup_sids: processing items "
+ "%d -- %d of %d.\n",
+ sids_processed,
+ sids_processed + hunk_num_sids - 1,
+ num_sids));
+
+ status = dcerpc_lsa_lookup_sids_noalloc(h,
+ mem_ctx,
+ (TALLOC_CTX *)domains,
+ (TALLOC_CTX *)names,
+ pol,
+ hunk_num_sids,
+ hunk_sids,
+ level,
+ hunk_domains,
+ hunk_names,
+ hunk_types,
+ use_lookupsids3,
+ &hunk_result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ if (!NT_STATUS_IS_OK(hunk_result) &&
+ !NT_STATUS_EQUAL(hunk_result, STATUS_SOME_UNMAPPED) &&
+ !NT_STATUS_EQUAL(hunk_result, NT_STATUS_NONE_MAPPED))
+ {
+ /* An actual error occurred */
+ *presult = hunk_result;
+ goto fail;
+ }
+
+ if (NT_STATUS_IS_OK(hunk_result)) {
+ have_mapped = true;
+ }
+ if (NT_STATUS_EQUAL(hunk_result, NT_STATUS_NONE_MAPPED)) {
+ have_unmapped = true;
+ }
+ if (NT_STATUS_EQUAL(hunk_result, STATUS_SOME_UNMAPPED)) {
+ int i;
+ for (i=0; i<hunk_num_sids; i++) {
+ if (hunk_types[i] == SID_NAME_UNKNOWN) {
+ have_unmapped = true;
+ } else {
+ have_mapped = true;
+ }
+ }
+ }
+
+ sids_left -= hunk_num_sids;
+ sids_processed += hunk_num_sids;
+ hunk_sids += hunk_num_sids;
+ hunk_domains += hunk_num_sids;
+ hunk_names += hunk_num_sids;
+ hunk_types += hunk_num_sids;
+ }
+
+ *pdomains = domains;
+ *pnames = names;
+ *ptypes = types;
+
+ if (!have_mapped) {
+ result = NT_STATUS_NONE_MAPPED;
+ }
+ if (have_unmapped) {
+ result = STATUS_SOME_UNMAPPED;
+ }
+ *presult = result;
+
+ return status;
+
+fail:
+ TALLOC_FREE(domains);
+ TALLOC_FREE(names);
+ TALLOC_FREE(types);
+
+ return status;
+}
+
+NTSTATUS dcerpc_lsa_lookup_sids(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *pol,
+ int num_sids,
+ const struct dom_sid *sids,
+ char ***pdomains,
+ char ***pnames,
+ enum lsa_SidType **ptypes,
+ NTSTATUS *result)
+{
+ enum lsa_LookupNamesLevel level = LSA_LOOKUP_NAMES_ALL;
+ return dcerpc_lsa_lookup_sids_generic(h,
+ mem_ctx,
+ pol,
+ num_sids,
+ sids,
+ level,
+ pdomains,
+ pnames,
+ ptypes,
+ false,
+ result);
+}
+
+NTSTATUS rpccli_lsa_lookup_sids(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *pol,
+ int num_sids,
+ const struct dom_sid *sids,
+ char ***pdomains,
+ char ***pnames,
+ enum lsa_SidType **ptypes)
+{
+ NTSTATUS status;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ enum lsa_LookupNamesLevel level = LSA_LOOKUP_NAMES_ALL;
+
+ status = dcerpc_lsa_lookup_sids_generic(cli->binding_handle,
+ mem_ctx,
+ pol,
+ num_sids,
+ sids,
+ level,
+ pdomains,
+ pnames,
+ ptypes,
+ false,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return result;
+}
+
+NTSTATUS dcerpc_lsa_lookup_sids3(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *pol,
+ int num_sids,
+ const struct dom_sid *sids,
+ char ***pdomains,
+ char ***pnames,
+ enum lsa_SidType **ptypes,
+ NTSTATUS *result)
+{
+ enum lsa_LookupNamesLevel level = LSA_LOOKUP_NAMES_ALL;
+ return dcerpc_lsa_lookup_sids_generic(h,
+ mem_ctx,
+ pol,
+ num_sids,
+ sids,
+ level,
+ pdomains,
+ pnames,
+ ptypes,
+ true,
+ result);
+}
+
+/** Lookup a list of names */
+
+NTSTATUS dcerpc_lsa_lookup_names_generic(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *pol,
+ uint32_t num_names,
+ const char **names,
+ const char ***dom_names,
+ enum lsa_LookupNamesLevel level,
+ struct dom_sid **sids,
+ enum lsa_SidType **types,
+ bool use_lookupnames4,
+ NTSTATUS *presult)
+{
+ NTSTATUS status;
+ struct lsa_String *lsa_names = NULL;
+ struct lsa_RefDomainList *domains = NULL;
+ struct lsa_TransSidArray sid_array = { .count = 0, };
+ struct lsa_TransSidArray3 sid_array3 = { .count = 0, };
+ uint32_t count = 0;
+ uint32_t i;
+
+ lsa_names = talloc_array(mem_ctx, struct lsa_String, num_names);
+ if (lsa_names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < num_names; i++) {
+ init_lsa_String(&lsa_names[i], names[i]);
+ }
+
+ if (use_lookupnames4) {
+ status = dcerpc_lsa_LookupNames4(h,
+ mem_ctx,
+ num_names,
+ lsa_names,
+ &domains,
+ &sid_array3,
+ level,
+ &count,
+ LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES,
+ LSA_CLIENT_REVISION_2,
+ presult);
+ } else {
+ status = dcerpc_lsa_LookupNames(h,
+ mem_ctx,
+ pol,
+ num_names,
+ lsa_names,
+ &domains,
+ &sid_array,
+ level,
+ &count,
+ presult);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(*presult) &&
+ !NT_STATUS_EQUAL(*presult, STATUS_SOME_UNMAPPED)) {
+ /* An actual error occurred */
+ goto done;
+ }
+
+ /* Return output parameters */
+ if (count == 0) {
+ *presult = NT_STATUS_NONE_MAPPED;
+ goto done;
+ }
+
+ if (num_names) {
+ *sids = talloc_zero_array(mem_ctx, struct dom_sid, num_names);
+ if (*sids == NULL) {
+ DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n"));
+ *presult = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ *types = talloc_zero_array(mem_ctx, enum lsa_SidType, num_names);
+ if (*types == NULL) {
+ DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n"));
+ *presult = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ if (dom_names != NULL) {
+ *dom_names = talloc_zero_array(mem_ctx, const char *, num_names);
+ if (*dom_names == NULL) {
+ DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n"));
+ *presult = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ }
+ } else {
+ *sids = NULL;
+ *types = NULL;
+ if (dom_names != NULL) {
+ *dom_names = NULL;
+ }
+ }
+
+ for (i = 0; i < num_names; i++) {
+ uint32_t dom_idx;
+ struct dom_sid *sid = &(*sids)[i];
+
+ if (use_lookupnames4) {
+ if (i >= sid_array3.count) {
+ *presult = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto done;
+ }
+
+ dom_idx = sid_array3.sids[i].sid_index;
+ (*types)[i] = sid_array3.sids[i].sid_type;
+ } else {
+ if (i >= sid_array.count) {
+ *presult = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto done;
+ }
+
+ dom_idx = sid_array.sids[i].sid_index;
+ (*types)[i] = sid_array.sids[i].sid_type;
+ }
+
+ /* Translate optimised sid through domain index array */
+
+ if (dom_idx == 0xffffffff) {
+ /* Nothing to do, this is unknown */
+ ZERO_STRUCTP(sid);
+ (*types)[i] = SID_NAME_UNKNOWN;
+ continue;
+ }
+ if (domains == NULL) {
+ *presult = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto done;
+ }
+ if (dom_idx >= domains->count) {
+ *presult = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto done;
+ }
+
+ if (use_lookupnames4) {
+ sid_copy(sid, sid_array3.sids[i].sid);
+ } else {
+ sid_copy(sid, domains->domains[dom_idx].sid);
+
+ if (sid_array.sids[i].rid != 0xffffffff) {
+ sid_append_rid(sid, sid_array.sids[i].rid);
+ }
+ }
+
+ if (dom_names == NULL) {
+ continue;
+ }
+
+ (*dom_names)[i] = domains->domains[dom_idx].name.string;
+ }
+
+ done:
+ return status;
+}
+
+NTSTATUS dcerpc_lsa_lookup_names(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *pol,
+ uint32_t num_names,
+ const char **names,
+ const char ***dom_names,
+ enum lsa_LookupNamesLevel level,
+ struct dom_sid **sids,
+ enum lsa_SidType **types,
+ NTSTATUS *result)
+{
+ return dcerpc_lsa_lookup_names_generic(h,
+ mem_ctx,
+ pol,
+ num_names,
+ names,
+ dom_names,
+ level,
+ sids,
+ types,
+ false,
+ result);
+}
+
+NTSTATUS rpccli_lsa_lookup_names(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *pol,
+ int num_names,
+ const char **names,
+ const char ***dom_names,
+ int level,
+ struct dom_sid **sids,
+ enum lsa_SidType **types)
+{
+ NTSTATUS status;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ status = dcerpc_lsa_lookup_names(cli->binding_handle,
+ mem_ctx,
+ pol,
+ num_names,
+ names,
+ dom_names,
+ level,
+ sids,
+ types,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return result;
+}
+
+NTSTATUS dcerpc_lsa_lookup_names4(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *pol,
+ uint32_t num_names,
+ const char **names,
+ const char ***dom_names,
+ enum lsa_LookupNamesLevel level,
+ struct dom_sid **sids,
+ enum lsa_SidType **types,
+ NTSTATUS *result)
+{
+ return dcerpc_lsa_lookup_names_generic(h,
+ mem_ctx,
+ pol,
+ num_names,
+ names,
+ dom_names,
+ level,
+ sids,
+ types,
+ true,
+ result);
+}
diff --git a/source3/rpc_client/cli_lsarpc.h b/source3/rpc_client/cli_lsarpc.h
new file mode 100644
index 0000000..0a0f399
--- /dev/null
+++ b/source3/rpc_client/cli_lsarpc.h
@@ -0,0 +1,278 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * LSARPC client routines
+ *
+ * Copyright (c) 2000-2001 Tim Potter
+ * Copyright (c) 1992-2000 Andrew Tridgell
+ * Copyright (c) 2002 Rafal Szczesniak
+ * Copyright (c) 2005 Jeremy Allison
+ * Copyright (c) 2007 Michael Adam
+ * Copyright (c) 2008 Guenther Deschner
+ *
+ * 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 _CLI_LSARPC_H
+#define _CLI_LSARPC_H
+
+/* The following definitions come from rpc_client/cli_lsarpc.c */
+
+/**
+ * @brief Open a LSA policy.
+ *
+ * @param[in] h The dcerpc binding handle to use.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] sec_qos Enable security quality of services.
+ *
+ * @param[in] des_access The desired access rights to be granted.
+ *
+ * @param[out] pol A pointer to a rpc policy handle.
+ *
+ * @param[out] result A pointer for the NDR NTSTATUS error code.
+ *
+ * @return A corresponding NTSTATUS error code for the connection.
+ */
+NTSTATUS dcerpc_lsa_open_policy(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ bool sec_qos,
+ uint32_t des_access,
+ struct policy_handle *pol,
+ NTSTATUS *result);
+NTSTATUS rpccli_lsa_open_policy(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ bool sec_qos, uint32_t des_access,
+ struct policy_handle *pol);
+
+/**
+ * @brief Open a LSA policy.
+ *
+ * @param[in] h The dcerpc binding handle to use.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] sec_qos Enable security quality of services.
+ *
+ * @param[in] des_access The desired access rights to be granted.
+ *
+ * @param[out] pol A pointer to a rpc policy handle.
+ *
+ * @param[out] result A pointer for the NDR NTSTATUS error code.
+ *
+ * @return A corresponding NTSTATUS error code for the connection.
+ */
+NTSTATUS dcerpc_lsa_open_policy2(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ const char *srv_name_slash,
+ bool sec_qos,
+ uint32_t des_access,
+ struct policy_handle *pol,
+ NTSTATUS *result);
+
+/**
+ * @brief Open a LSA policy.
+ *
+ * @param[in] h The dcerpc binding handle to use.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] sec_qos Enable security quality of services.
+ *
+ * @param[in] des_access The desired access rights to be granted.
+ *
+ * @param[out] out_version A pointer to an uin32_t to store the version of the
+ * following data structure.
+ *
+ * @param[out] out_revision info A pointer to store the out_revision_info.
+ *
+ * @param[out] pol A pointer to a rpc policy handle.
+ *
+ * @param[out] result A pointer for the NDR NTSTATUS error code.
+ *
+ * @return A corresponding NTSTATUS error code for the connection.
+ */
+NTSTATUS dcerpc_lsa_open_policy3(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ const char *srv_name_slash,
+ bool sec_qos,
+ uint32_t des_access,
+ uint32_t *out_version,
+ union lsa_revision_info *out_revision_info,
+ struct policy_handle *pol,
+ NTSTATUS *result);
+
+/**
+ * @brief Open a LSA policy with fallback to previous version
+ *
+ * This first calls lsa_open_policy3 and falls back to lsa_open_policy2 in case
+ * it isn't implemented.
+ *
+ * @param[in] h The dcerpc binding handle to use.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] sec_qos Enable security quality of services.
+ *
+ * @param[in] des_access The desired access rights to be granted.
+ *
+ * @param[out] out_version A pointer to an uin32_t to store the version of the
+ * following data structure.
+ *
+ * @param[out] out_revision info A pointer to store the out_revision_info.
+ *
+ * @param[out] pol A pointer to a rpc policy handle.
+ *
+ * @param[out] result A pointer for the NDR NTSTATUS error code.
+ *
+ * @return A corresponding NTSTATUS error code for the connection.
+ */
+NTSTATUS dcerpc_lsa_open_policy_fallback(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ const char *srv_name_slash,
+ bool sec_qos,
+ uint32_t desired_access,
+ uint32_t *out_version,
+ union lsa_revision_info *out_revision_info,
+ struct policy_handle *pol,
+ NTSTATUS *result);
+
+/**
+ * @brief Look up the names that correspond to an array of sids.
+ *
+ * @param[in] h The initialized binding handle for a dcerpc connection.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] pol The opened domain policy handle.
+ *
+ * @param[in] num_sids The number of sids in the sids array to look up.
+ *
+ * @param[in] sids The array of sids to look up.
+ *
+ * @param[out] pdomains A pointer to store the refercenced domains.
+ *
+ * @param[out] pnames A pointer to an array for the translated names.
+ *
+ * @param[out] ptypes A pointer to an array for the types of the names.
+ *
+ * @param[out] result A pointer for the conversion result.
+ *
+ * @return A corresponding NTSTATUS error code.
+ */
+NTSTATUS dcerpc_lsa_lookup_sids(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *pol,
+ int num_sids,
+ const struct dom_sid *sids,
+ char ***pdomains,
+ char ***pnames,
+ enum lsa_SidType **ptypes,
+ NTSTATUS *result);
+NTSTATUS rpccli_lsa_lookup_sids(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *pol,
+ int num_sids,
+ const struct dom_sid *sids,
+ char ***pdomains,
+ char ***pnames,
+ enum lsa_SidType **ptypes);
+NTSTATUS dcerpc_lsa_lookup_sids_generic(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *pol,
+ int num_sids,
+ const struct dom_sid *sids,
+ enum lsa_LookupNamesLevel level,
+ char ***pdomains,
+ char ***pnames,
+ enum lsa_SidType **ptypes,
+ bool use_lookupsids3,
+ NTSTATUS *presult);
+/**
+ * @brief Look up the names that correspond to an array of sids.
+ *
+ * @param[in] h The initialized binding handle for a dcerpc connection.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] pol The opened domain policy handle.
+ *
+ * @param[in] num_sids The number of sids in the sids array to look up.
+ *
+ * @param[in] sids The array of sids to look up.
+ *
+ * @param[out] pdomains A pointer to store the refercenced domains.
+ *
+ * @param[out] pnames A pointer to an array for the translated names.
+ *
+ * @param[out] ptypes A pointer to an array for the types of the names.
+ *
+ * @param[out] result A pointer for the conversion result.
+ *
+ * @return A corresponding NTSTATUS error code.
+ */
+NTSTATUS dcerpc_lsa_lookup_sids3(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *pol,
+ int num_sids,
+ const struct dom_sid *sids,
+ char ***pdomains,
+ char ***pnames,
+ enum lsa_SidType **ptypes,
+ NTSTATUS *result);
+NTSTATUS dcerpc_lsa_lookup_names(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *pol,
+ uint32_t num_names,
+ const char **names,
+ const char ***dom_names,
+ enum lsa_LookupNamesLevel level,
+ struct dom_sid **sids,
+ enum lsa_SidType **types,
+ NTSTATUS *result);
+NTSTATUS rpccli_lsa_lookup_names(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *pol, int num_names,
+ const char **names,
+ const char ***dom_names,
+ int level,
+ struct dom_sid **sids,
+ enum lsa_SidType **types);
+
+NTSTATUS dcerpc_lsa_lookup_names4(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *pol,
+ uint32_t num_names,
+ const char **names,
+ const char ***dom_names,
+ enum lsa_LookupNamesLevel level,
+ struct dom_sid **sids,
+ enum lsa_SidType **types,
+ NTSTATUS *result);
+NTSTATUS dcerpc_lsa_lookup_names_generic(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *pol,
+ uint32_t num_names,
+ const char **names,
+ const char ***dom_names,
+ enum lsa_LookupNamesLevel level,
+ struct dom_sid **sids,
+ enum lsa_SidType **types,
+ bool use_lookupnames4,
+ NTSTATUS *presult);
+
+bool fetch_domain_sid( char *domain, char *remote_machine, struct dom_sid *psid);
+
+#endif /* _CLI_LSARPC_H */
diff --git a/source3/rpc_client/cli_mdssvc.c b/source3/rpc_client/cli_mdssvc.c
new file mode 100644
index 0000000..93e032f
--- /dev/null
+++ b/source3/rpc_client/cli_mdssvc.c
@@ -0,0 +1,1214 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main metadata server / Spotlight client functions
+
+ Copyright (C) Ralph Boehme 2019
+
+ 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 "rpc_client.h"
+#include "../librpc/gen_ndr/ndr_mdssvc_c.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "rpc_server/mdssvc/dalloc.h"
+#include "rpc_server/mdssvc/marshalling.h"
+#include "cli_mdssvc.h"
+#include "cli_mdssvc_private.h"
+#include "cli_mdssvc_util.h"
+
+struct mdsctx_id mdscli_new_ctx_id(struct mdscli_ctx *mdscli_ctx)
+{
+ mdscli_ctx->ctx_id.id++;
+ return mdscli_ctx->ctx_id;
+}
+
+char *mdscli_get_basepath(TALLOC_CTX *mem_ctx,
+ struct mdscli_ctx *mdscli_ctx)
+{
+ return talloc_strdup(mem_ctx, mdscli_ctx->mdscmd_open.share_path);
+}
+
+struct mdscli_connect_state {
+ struct tevent_context *ev;
+ struct mdscli_ctx *mdscli_ctx;
+ struct mdssvc_blob response_blob;
+};
+
+static void mdscli_connect_open_done(struct tevent_req *subreq);
+static void mdscli_connect_unknown1_done(struct tevent_req *subreq);
+static void mdscli_connect_fetch_props_done(struct tevent_req *subreq);
+
+struct tevent_req *mdscli_connect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dcerpc_binding_handle *bh,
+ const char *share_name,
+ const char *mount_path)
+{
+ struct tevent_req *req = NULL;
+ struct mdscli_connect_state *state = NULL;
+ struct tevent_req *subreq = NULL;
+ struct mdscli_ctx *ctx = NULL;
+
+ req = tevent_req_create(req, &state, struct mdscli_connect_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ ctx = talloc_zero(state, struct mdscli_ctx);
+ if (tevent_req_nomem(ctx, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ *state = (struct mdscli_connect_state) {
+ .ev = ev,
+ .mdscli_ctx = ctx,
+ };
+
+ *ctx = (struct mdscli_ctx) {
+ .bh = bh,
+ .max_fragment_size = 64 * 1024,
+ /*
+ * The connection id is a per tcon value sent by the client,
+ * 0x6b000060 is a value used most of the times for the first
+ * tcon.
+ */
+ .ctx_id.connection = UINT64_C(0x6b000060),
+ };
+
+ subreq = dcerpc_mdssvc_open_send(state,
+ state->ev,
+ ctx->bh,
+ &ctx->dev,
+ &ctx->mdscmd_open.unkn2,
+ &ctx->mdscmd_open.unkn3,
+ mount_path,
+ share_name,
+ ctx->mdscmd_open.share_path,
+ &ctx->ph);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, state->ev);
+ }
+ tevent_req_set_callback(subreq, mdscli_connect_open_done, req);
+ ctx->async_pending++;
+
+ return req;
+}
+
+static void mdscli_connect_open_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct mdscli_connect_state *state = tevent_req_data(
+ req, struct mdscli_connect_state);
+ struct mdscli_ctx *mdscli_ctx = state->mdscli_ctx;
+ size_t share_path_len;
+ NTSTATUS status;
+
+ status = dcerpc_mdssvc_open_recv(subreq, state);
+ TALLOC_FREE(subreq);
+ state->mdscli_ctx->async_pending--;
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ share_path_len = strlen(mdscli_ctx->mdscmd_open.share_path);
+ if (share_path_len < 1 || share_path_len > UINT16_MAX) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+ mdscli_ctx->mdscmd_open.share_path_len = share_path_len;
+
+ if (mdscli_ctx->mdscmd_open.share_path[share_path_len-1] == '/') {
+ mdscli_ctx->mdscmd_open.share_path[share_path_len-1] = '\0';
+ mdscli_ctx->mdscmd_open.share_path_len--;
+ }
+
+ subreq = dcerpc_mdssvc_unknown1_send(
+ state,
+ state->ev,
+ mdscli_ctx->bh,
+ &mdscli_ctx->ph,
+ 0,
+ mdscli_ctx->dev,
+ mdscli_ctx->mdscmd_open.unkn2,
+ 0,
+ geteuid(),
+ getegid(),
+ &mdscli_ctx->mdscmd_unknown1.status,
+ &mdscli_ctx->flags,
+ &mdscli_ctx->mdscmd_unknown1.unkn7);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, mdscli_connect_unknown1_done, req);
+}
+
+static void mdscli_connect_unknown1_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct mdscli_connect_state *state = tevent_req_data(
+ req, struct mdscli_connect_state);
+ struct mdscli_ctx *mdscli_ctx = state->mdscli_ctx;
+ struct mdssvc_blob request_blob;
+ NTSTATUS status;
+
+ status = dcerpc_mdssvc_unknown1_recv(subreq, state);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ status = mdscli_blob_fetch_props(state,
+ state->mdscli_ctx,
+ &request_blob);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = dcerpc_mdssvc_cmd_send(state,
+ state->ev,
+ mdscli_ctx->bh,
+ &mdscli_ctx->ph,
+ 0,
+ mdscli_ctx->dev,
+ mdscli_ctx->mdscmd_open.unkn2,
+ 0,
+ mdscli_ctx->flags,
+ request_blob,
+ 0,
+ mdscli_ctx->max_fragment_size,
+ 1,
+ mdscli_ctx->max_fragment_size,
+ 0,
+ 0,
+ &mdscli_ctx->mdscmd_cmd.fragment,
+ &state->response_blob,
+ &mdscli_ctx->mdscmd_cmd.unkn9);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, mdscli_connect_fetch_props_done, req);
+ mdscli_ctx->async_pending++;
+ return;
+}
+
+static void mdscli_connect_fetch_props_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct mdscli_connect_state *state = tevent_req_data(
+ req, struct mdscli_connect_state);
+ struct mdscli_ctx *mdscli_ctx = state->mdscli_ctx;
+ DALLOC_CTX *d = NULL;
+ sl_array_t *path_scope_array = NULL;
+ char *path_scope = NULL;
+ NTSTATUS status;
+ bool ok;
+
+ status = dcerpc_mdssvc_cmd_recv(subreq, state);
+ TALLOC_FREE(subreq);
+ state->mdscli_ctx->async_pending--;
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ d = dalloc_new(state);
+ if (tevent_req_nomem(d, req)) {
+ return;
+ }
+
+ ok = sl_unpack(d,
+ (char *)state->response_blob.spotlight_blob,
+ state->response_blob.length);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ path_scope_array = dalloc_value_for_key(d,
+ "DALLOC_CTX", 0,
+ "kMDSStorePathScopes",
+ "sl_array_t");
+ if (path_scope_array == NULL) {
+ DBG_ERR("Missing kMDSStorePathScopes\n");
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ path_scope = dalloc_get(path_scope_array, "char *", 0);
+ if (path_scope == NULL) {
+ DBG_ERR("Missing path in kMDSStorePathScopes\n");
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ mdscli_ctx->path_scope_len = strlen(path_scope);
+ if (mdscli_ctx->path_scope_len < 1 ||
+ mdscli_ctx->path_scope_len > UINT16_MAX)
+ {
+ DBG_ERR("Bad path_scope: %s\n", path_scope);
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+ mdscli_ctx->path_scope = talloc_strdup(mdscli_ctx, path_scope);
+ if (tevent_req_nomem(mdscli_ctx->path_scope, req)) {
+ return;
+ }
+
+ if (mdscli_ctx->path_scope[mdscli_ctx->path_scope_len-1] == '/') {
+ mdscli_ctx->path_scope[mdscli_ctx->path_scope_len-1] = '\0';
+ mdscli_ctx->path_scope_len--;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS mdscli_connect_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct mdscli_ctx **mdscli_ctx)
+{
+ struct mdscli_connect_state *state = tevent_req_data(
+ req, struct mdscli_connect_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *mdscli_ctx = talloc_move(mem_ctx, &state->mdscli_ctx);
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS mdscli_connect(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *bh,
+ const char *share_name,
+ const char *mount_path,
+ struct mdscli_ctx **mdscli_ctx)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_req *req = NULL;
+ struct tevent_context *ev = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ req = mdscli_connect_send(frame,
+ ev,
+ bh,
+ share_name,
+ mount_path);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = mdscli_connect_recv(req, mem_ctx, mdscli_ctx);
+fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct mdscli_search_state {
+ struct mdscli_search_ctx *search;
+ struct mdssvc_blob request_blob;
+ struct mdssvc_blob response_blob;
+};
+
+static void mdscli_search_cmd_done(struct tevent_req *subreq);
+
+struct tevent_req *mdscli_search_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct mdscli_ctx *mdscli_ctx,
+ const char *mds_query,
+ const char *path_scope_in,
+ bool live)
+{
+ struct tevent_req *req = NULL;
+ struct mdscli_search_state *state = NULL;
+ struct tevent_req *subreq = NULL;
+ struct mdscli_search_ctx *search = NULL;
+ char *path_scope = NULL;
+ NTSTATUS status;
+
+ req = tevent_req_create(req, &state, struct mdscli_search_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ search = talloc_zero(state, struct mdscli_search_ctx);
+ if (tevent_req_nomem(search, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (path_scope_in[0] == '/') {
+ path_scope = talloc_strdup(search, path_scope_in);
+ } else {
+ path_scope = talloc_asprintf(search,
+ "%s/%s",
+ mdscli_ctx->mdscmd_open.share_path,
+ path_scope_in);
+ }
+ if (tevent_req_nomem(path_scope, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ *search = (struct mdscli_search_ctx) {
+ .mdscli_ctx = mdscli_ctx,
+ .ctx_id = mdscli_new_ctx_id(mdscli_ctx),
+ .unique_id = generate_random_u64(),
+ .live = live,
+ .path_scope = path_scope,
+ .mds_query = talloc_strdup(search, mds_query),
+ };
+ if (tevent_req_nomem(search->mds_query, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ *state = (struct mdscli_search_state) {
+ .search = search,
+ };
+
+ status = mdscli_blob_search(state,
+ search,
+ &state->request_blob);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = dcerpc_mdssvc_cmd_send(state,
+ ev,
+ mdscli_ctx->bh,
+ &mdscli_ctx->ph,
+ 0,
+ mdscli_ctx->dev,
+ mdscli_ctx->mdscmd_open.unkn2,
+ 0,
+ mdscli_ctx->flags,
+ state->request_blob,
+ 0,
+ mdscli_ctx->max_fragment_size,
+ 1,
+ mdscli_ctx->max_fragment_size,
+ 0,
+ 0,
+ &mdscli_ctx->mdscmd_cmd.fragment,
+ &state->response_blob,
+ &mdscli_ctx->mdscmd_cmd.unkn9);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, mdscli_search_cmd_done, req);
+ mdscli_ctx->async_pending++;
+ return req;
+}
+
+static void mdscli_search_cmd_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct mdscli_search_state *state = tevent_req_data(
+ req, struct mdscli_search_state);
+ DALLOC_CTX *d = NULL;
+ uint64_t *uint64p = NULL;
+ NTSTATUS status;
+ bool ok;
+
+ status = dcerpc_mdssvc_cmd_recv(subreq, state);
+ TALLOC_FREE(subreq);
+ state->search->mdscli_ctx->async_pending--;
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ d = dalloc_new(state);
+ if (tevent_req_nomem(d, req)) {
+ return;
+ }
+
+ ok = sl_unpack(d,
+ (char *)state->response_blob.spotlight_blob,
+ state->response_blob.length);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ uint64p = dalloc_get(d,
+ "DALLOC_CTX", 0,
+ "uint64_t", 0);
+ if (uint64p == NULL) {
+ DBG_DEBUG("Unexpected mds response: %s", dalloc_dump(d, 0));
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ if (*uint64p != 0) {
+ DBG_DEBUG("Unexpected mds result: 0x%" PRIx64 "\n", *uint64p);
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+NTSTATUS mdscli_search_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct mdscli_search_ctx **search)
+{
+ struct mdscli_search_state *state = tevent_req_data(
+ req, struct mdscli_search_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *search = talloc_move(mem_ctx, &state->search);
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS mdscli_search(TALLOC_CTX *mem_ctx,
+ struct mdscli_ctx *mdscli_ctx,
+ const char *mds_query,
+ const char *path_scope,
+ bool live,
+ struct mdscli_search_ctx **search)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_req *req = NULL;
+ struct tevent_context *ev = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (mdscli_ctx->async_pending != 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ req = mdscli_search_send(frame,
+ ev,
+ mdscli_ctx,
+ mds_query,
+ path_scope,
+ live);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = mdscli_search_recv(req, mem_ctx, search);
+fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct mdscli_get_results_state {
+ struct tevent_context *ev;
+ struct mdscli_search_ctx *search;
+ struct mdssvc_blob request_blob;
+ struct mdssvc_blob response_fragment;
+ DATA_BLOB response_data;
+ uint64_t *cnids;
+ uint32_t fragment;
+};
+
+static void mdscli_get_results_cmd_done(struct tevent_req *subreq);
+
+struct tevent_req *mdscli_get_results_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct mdscli_search_ctx *search)
+{
+ struct tevent_req *req = NULL;
+ struct mdscli_get_results_state *state = NULL;
+ struct tevent_req *subreq = NULL;
+ struct mdscli_ctx *mdscli_ctx = search->mdscli_ctx;
+ NTSTATUS status;
+
+ req = tevent_req_create(req, &state, struct mdscli_get_results_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ *state = (struct mdscli_get_results_state) {
+ .ev = ev,
+ .search = search,
+ };
+
+ status = mdscli_blob_get_results(state,
+ search,
+ &state->request_blob);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = dcerpc_mdssvc_cmd_send(state,
+ ev,
+ mdscli_ctx->bh,
+ &mdscli_ctx->ph,
+ 0,
+ mdscli_ctx->dev,
+ mdscli_ctx->mdscmd_open.unkn2,
+ 0,
+ mdscli_ctx->flags,
+ state->request_blob,
+ 0,
+ mdscli_ctx->max_fragment_size,
+ 1,
+ mdscli_ctx->max_fragment_size,
+ 0,
+ 0,
+ &state->fragment,
+ &state->response_fragment,
+ &mdscli_ctx->mdscmd_cmd.unkn9);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, mdscli_get_results_cmd_done, req);
+ mdscli_ctx->async_pending++;
+ return req;
+}
+
+static void mdscli_get_results_cmd_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct mdscli_get_results_state *state = tevent_req_data(
+ req, struct mdscli_get_results_state);
+ struct mdscli_ctx *mdscli_ctx = state->search->mdscli_ctx;
+ size_t oldsize, newsize;
+ DALLOC_CTX *d = NULL;
+ uint64_t *uint64p = NULL;
+ bool search_in_progress = false;
+ sl_cnids_t *cnids = NULL;
+ size_t ncnids;
+ size_t i;
+ NTSTATUS status;
+ bool ok;
+
+ status = dcerpc_mdssvc_cmd_recv(subreq, state);
+ TALLOC_FREE(subreq);
+ state->search->mdscli_ctx->async_pending--;
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ oldsize = state->response_data.length;
+ newsize = oldsize + state->response_fragment.length;
+ if (newsize < oldsize) {
+ tevent_req_nterror(req, NT_STATUS_INTEGER_OVERFLOW);
+ return;
+ }
+
+ ok = data_blob_realloc(state, &state->response_data, newsize);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ (void)memcpy(state->response_data.data + oldsize,
+ state->response_fragment.spotlight_blob,
+ state->response_fragment.length);
+
+ TALLOC_FREE(state->response_fragment.spotlight_blob);
+ state->response_fragment.length = 0;
+ state->response_fragment.size = 0;
+
+ if (state->fragment != 0) {
+ subreq = dcerpc_mdssvc_cmd_send(
+ state,
+ state->ev,
+ mdscli_ctx->bh,
+ &mdscli_ctx->ph,
+ 0,
+ mdscli_ctx->dev,
+ mdscli_ctx->mdscmd_open.unkn2,
+ 1,
+ mdscli_ctx->flags,
+ state->request_blob,
+ 0,
+ mdscli_ctx->max_fragment_size,
+ 1,
+ mdscli_ctx->max_fragment_size,
+ 0,
+ 0,
+ &state->fragment,
+ &state->response_fragment,
+ &mdscli_ctx->mdscmd_cmd.unkn9);
+ if (tevent_req_nomem(subreq, req)) {
+ tevent_req_post(req, state->ev);
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ mdscli_get_results_cmd_done,
+ req);
+ mdscli_ctx->async_pending++;
+ return;
+ }
+
+ d = dalloc_new(state);
+ if (tevent_req_nomem(d, req)) {
+ return;
+ }
+
+ ok = sl_unpack(d,
+ (char *)state->response_data.data,
+ state->response_data.length);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ uint64p = dalloc_get(d,
+ "DALLOC_CTX", 0,
+ "uint64_t", 0);
+ if (uint64p == NULL) {
+ DBG_DEBUG("Unexpected mds response: %s", dalloc_dump(d, 0));
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ if (*uint64p == 35) {
+ DBG_DEBUG("Search in progress\n");
+ search_in_progress = true;
+ }
+
+ cnids = dalloc_get(d, "DALLOC_CTX", 0, "sl_cnids_t", 1);
+ if (cnids == NULL) {
+ DBG_DEBUG("cnids error: %s", dalloc_dump(d, 0));
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ ncnids = dalloc_size(cnids->ca_cnids);
+ if (ncnids == 0 && !search_in_progress) {
+ tevent_req_nterror(req, NT_STATUS_NO_MORE_MATCHES);
+ return;
+ }
+
+ if (cnids->ca_unkn1 != 0xadd) {
+ /*
+ * Whatever 0xadd means... but it seems to be the standard value
+ * macOS mdssvc returns here.
+ */
+ DBG_DEBUG("unexpected ca_unkn1: %s", dalloc_dump(d, 0));
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ if (cnids->ca_context != state->search->ctx_id.connection ) {
+ DBG_DEBUG("unexpected ca_context: %s", dalloc_dump(d, 0));
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ state->cnids = talloc_zero_array(state, uint64_t, ncnids);
+ if (tevent_req_nomem(state->cnids, req)) {
+ return;
+ }
+
+ for (i = 0; i < ncnids; i++) {
+ uint64_t *cnid = NULL;
+
+ cnid = dalloc_get(cnids->ca_cnids, "uint64_t", i);
+ if (cnid == NULL) {
+ DBG_DEBUG("cnids error: %s", dalloc_dump(d, 0));
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+ state->cnids[i] = *cnid;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+NTSTATUS mdscli_get_results_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint64_t **cnids)
+{
+ struct mdscli_get_results_state *state = tevent_req_data(
+ req, struct mdscli_get_results_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *cnids = talloc_move(mem_ctx, &state->cnids);
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS mdscli_get_results(TALLOC_CTX *mem_ctx,
+ struct mdscli_search_ctx *search,
+ uint64_t **_cnids)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_req *req = NULL;
+ struct tevent_context *ev = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (search->mdscli_ctx->async_pending != 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ req = mdscli_get_results_send(frame,
+ ev,
+ search);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = mdscli_get_results_recv(req, mem_ctx, _cnids);
+fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct mdscli_get_path_state {
+ struct mdscli_ctx *mdscli_ctx;
+ struct mdssvc_blob request_blob;
+ struct mdssvc_blob response_blob;
+ char *path;
+};
+
+static void mdscli_get_path_done(struct tevent_req *subreq);
+
+struct tevent_req *mdscli_get_path_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct mdscli_ctx *mdscli_ctx,
+ uint64_t cnid)
+{
+ struct tevent_req *req = NULL;
+ struct mdscli_get_path_state *state = NULL;
+ struct tevent_req *subreq = NULL;
+ NTSTATUS status;
+
+ req = tevent_req_create(req, &state, struct mdscli_get_path_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ *state = (struct mdscli_get_path_state) {
+ .mdscli_ctx = mdscli_ctx,
+ };
+
+ status = mdscli_blob_get_path(state,
+ mdscli_ctx,
+ cnid,
+ &state->request_blob);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = dcerpc_mdssvc_cmd_send(state,
+ ev,
+ mdscli_ctx->bh,
+ &mdscli_ctx->ph,
+ 0,
+ mdscli_ctx->dev,
+ mdscli_ctx->mdscmd_open.unkn2,
+ 0,
+ mdscli_ctx->flags,
+ state->request_blob,
+ 0,
+ mdscli_ctx->max_fragment_size,
+ 1,
+ mdscli_ctx->max_fragment_size,
+ 0,
+ 0,
+ &mdscli_ctx->mdscmd_cmd.fragment,
+ &state->response_blob,
+ &mdscli_ctx->mdscmd_cmd.unkn9);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, mdscli_get_path_done, req);
+ mdscli_ctx->async_pending++;
+ return req;
+}
+
+static void mdscli_get_path_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct mdscli_get_path_state *state = tevent_req_data(
+ req, struct mdscli_get_path_state);
+ DALLOC_CTX *d = NULL;
+ size_t pathlen;
+ size_t prefixlen;
+ char *path = NULL;
+ const char *p = NULL;
+ NTSTATUS status;
+ bool ok;
+
+ status = dcerpc_mdssvc_cmd_recv(subreq, state);
+ TALLOC_FREE(subreq);
+ state->mdscli_ctx->async_pending--;
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ d = dalloc_new(state);
+ if (tevent_req_nomem(d, req)) {
+ return;
+ }
+
+ ok = sl_unpack(d,
+ (char *)state->response_blob.spotlight_blob,
+ state->response_blob.length);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ path = dalloc_get(d,
+ "DALLOC_CTX", 0,
+ "DALLOC_CTX", 2,
+ "DALLOC_CTX", 0,
+ "DALLOC_CTX", 1,
+ "char *", 0);
+ if (path == NULL) {
+ DBG_DEBUG("No path in mds response: %s", dalloc_dump(d, 0));
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ /* Path is prefixed by /PATHSCOPE/SHARENAME/, strip it */
+ pathlen = strlen(path);
+
+ /*
+ * path_scope_len and share_path_len are already checked to be smaller
+ * then UINT16_MAX so this can't overflow
+ */
+ prefixlen = state->mdscli_ctx->path_scope_len
+ + state->mdscli_ctx->mdscmd_open.share_path_len;
+
+ if (pathlen < prefixlen) {
+ DBG_DEBUG("Bad path: %s\n", path);
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ p = path + prefixlen;
+ while (*p == '/') {
+ p++;
+ }
+ if (*p == '\0') {
+ DBG_DEBUG("Bad path: %s\n", path);
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ state->path = talloc_strdup(state, p);
+ if (state->path == NULL) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ DBG_DEBUG("path: %s\n", state->path);
+
+ tevent_req_done(req);
+ return;
+}
+
+NTSTATUS mdscli_get_path_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ char **path)
+{
+ struct mdscli_get_path_state *state = tevent_req_data(
+ req, struct mdscli_get_path_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *path = talloc_move(mem_ctx, &state->path);
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS mdscli_get_path(TALLOC_CTX *mem_ctx,
+ struct mdscli_ctx *mdscli_ctx,
+ uint64_t cnid,
+ char **path)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_req *req = NULL;
+ struct tevent_context *ev = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (mdscli_ctx->async_pending != 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ req = mdscli_get_path_send(frame, ev, mdscli_ctx, cnid);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = mdscli_get_path_recv(req, mem_ctx, path);
+fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct mdscli_close_search_state {
+ struct mdscli_search_ctx *search;
+ struct mdssvc_blob request_blob;
+ struct mdssvc_blob response_blob;
+};
+
+static void mdscli_close_search_done(struct tevent_req *subreq);
+
+struct tevent_req *mdscli_close_search_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct mdscli_search_ctx **search)
+{
+ struct mdscli_ctx *mdscli_ctx = NULL;
+ struct tevent_req *req = NULL;
+ struct mdscli_close_search_state *state = NULL;
+ struct tevent_req *subreq = NULL;
+ NTSTATUS status;
+
+ req = tevent_req_create(req, &state, struct mdscli_close_search_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ *state = (struct mdscli_close_search_state) {
+ .search = talloc_move(state, search),
+ };
+ mdscli_ctx = state->search->mdscli_ctx;
+
+ status = mdscli_blob_close_search(state,
+ state->search,
+ &state->request_blob);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = dcerpc_mdssvc_cmd_send(state,
+ ev,
+ mdscli_ctx->bh,
+ &mdscli_ctx->ph,
+ 0,
+ mdscli_ctx->dev,
+ mdscli_ctx->mdscmd_open.unkn2,
+ 0,
+ mdscli_ctx->flags,
+ state->request_blob,
+ 0,
+ mdscli_ctx->max_fragment_size,
+ 1,
+ mdscli_ctx->max_fragment_size,
+ 0,
+ 0,
+ &mdscli_ctx->mdscmd_cmd.fragment,
+ &state->response_blob,
+ &mdscli_ctx->mdscmd_cmd.unkn9);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, mdscli_close_search_done, req);
+ mdscli_ctx->async_pending++;
+ return req;
+}
+
+static void mdscli_close_search_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct mdscli_close_search_state *state = tevent_req_data(
+ req, struct mdscli_close_search_state);
+ NTSTATUS status;
+
+ status = dcerpc_mdssvc_cmd_recv(subreq, state);
+ TALLOC_FREE(subreq);
+ state->search->mdscli_ctx->async_pending--;
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+NTSTATUS mdscli_close_search_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS mdscli_close_search(struct mdscli_search_ctx **search)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_req *req = NULL;
+ struct tevent_context *ev = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if ((*search)->mdscli_ctx->async_pending != 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ req = mdscli_close_search_send(frame, ev, search);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = mdscli_close_search_recv(req);
+fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct mdscli_disconnect_state {
+ struct mdscli_ctx *mdscli_ctx;
+};
+
+static void mdscli_disconnect_done(struct tevent_req *subreq);
+
+struct tevent_req *mdscli_disconnect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct mdscli_ctx *mdscli_ctx)
+{
+ struct tevent_req *req = NULL;
+ struct mdscli_disconnect_state *state = NULL;
+ struct tevent_req *subreq = NULL;
+
+ req = tevent_req_create(req, &state, struct mdscli_disconnect_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ *state = (struct mdscli_disconnect_state) {
+ .mdscli_ctx = mdscli_ctx,
+ };
+
+ subreq = dcerpc_mdssvc_close_send(state,
+ ev,
+ mdscli_ctx->bh,
+ &mdscli_ctx->ph,
+ 0,
+ mdscli_ctx->dev,
+ mdscli_ctx->mdscmd_open.unkn2,
+ 0,
+ &mdscli_ctx->ph,
+ &mdscli_ctx->mdscmd_close.status);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, mdscli_disconnect_done, req);
+ mdscli_ctx->async_pending++;
+ return req;
+}
+
+static void mdscli_disconnect_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct mdscli_disconnect_state *state = tevent_req_data(
+ req, struct mdscli_disconnect_state);
+ NTSTATUS status;
+
+ status = dcerpc_mdssvc_close_recv(subreq, state);
+ TALLOC_FREE(subreq);
+ state->mdscli_ctx->async_pending--;
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+NTSTATUS mdscli_disconnect_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS mdscli_disconnect(struct mdscli_ctx *mdscli_ctx)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_req *req = NULL;
+ struct tevent_context *ev = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (mdscli_ctx->async_pending != 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ req = mdscli_disconnect_send(frame, ev, mdscli_ctx);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = mdscli_disconnect_recv(req);
+fail:
+ TALLOC_FREE(frame);
+ return status;
+}
diff --git a/source3/rpc_client/cli_mdssvc.h b/source3/rpc_client/cli_mdssvc.h
new file mode 100644
index 0000000..f8b1582
--- /dev/null
+++ b/source3/rpc_client/cli_mdssvc.h
@@ -0,0 +1,97 @@
+/*
+ Unix SMB/CIFS implementation.
+ mdssvc client functions
+
+ Copyright (C) Ralph Boehme 2019
+
+ 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 _MDSCLI_H_
+#define _MDSCLI_H_
+
+struct mdscli_ctx;
+struct mdscli_search_ctx;
+
+struct mdsctx_id mdscli_new_ctx_id(struct mdscli_ctx *mdscli_ctx);
+char *mdscli_get_basepath(TALLOC_CTX *mem_ctx,
+ struct mdscli_ctx *mdscli_ctx);
+struct mdscli_ctx *mdscli_search_get_ctx(struct mdscli_search_ctx *search);
+
+struct tevent_req *mdscli_connect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dcerpc_binding_handle *bh,
+ const char *share_name,
+ const char *mount_path);
+NTSTATUS mdscli_connect_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct mdscli_ctx **mdscli_ctx);
+NTSTATUS mdscli_connect(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *bh,
+ const char *share_name,
+ const char *mount_path,
+ struct mdscli_ctx **mdscli_ctx);
+
+struct tevent_req *mdscli_search_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct mdscli_ctx *mdscli_ctx,
+ const char *mds_query,
+ const char *path_scope,
+ bool live);
+NTSTATUS mdscli_search_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct mdscli_search_ctx **search);
+NTSTATUS mdscli_search(TALLOC_CTX *mem_ctx,
+ struct mdscli_ctx *mdscli_ctx,
+ const char *mds_query,
+ const char *path_scope,
+ bool live,
+ struct mdscli_search_ctx **search);
+
+struct tevent_req *mdscli_get_results_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct mdscli_search_ctx *search);
+NTSTATUS mdscli_get_results_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint64_t **cnids);
+NTSTATUS mdscli_get_results(TALLOC_CTX *mem_ctx,
+ struct mdscli_search_ctx *search,
+ uint64_t **_cnids);
+
+struct tevent_req *mdscli_get_path_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct mdscli_ctx *mdscli_ctx,
+ uint64_t cnid);
+NTSTATUS mdscli_get_path_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ char **path);
+NTSTATUS mdscli_get_path(TALLOC_CTX *mem_ctx,
+ struct mdscli_ctx *mdscli_ctx,
+ uint64_t cnid,
+ char **path);
+
+struct tevent_req *mdscli_close_search_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct mdscli_search_ctx **_search);
+NTSTATUS mdscli_close_search_recv(struct tevent_req *req);
+NTSTATUS mdscli_close_search(struct mdscli_search_ctx **search);
+
+struct tevent_req *mdscli_disconnect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct mdscli_ctx *mdscli_ctx);
+NTSTATUS mdscli_disconnect_recv(struct tevent_req *req);
+NTSTATUS mdscli_disconnect(struct mdscli_ctx *mdscli_ctx);
+
+#endif /* _MDSCLI_H_ */
diff --git a/source3/rpc_client/cli_mdssvc_private.h b/source3/rpc_client/cli_mdssvc_private.h
new file mode 100644
index 0000000..77f300c
--- /dev/null
+++ b/source3/rpc_client/cli_mdssvc_private.h
@@ -0,0 +1,74 @@
+/*
+ Unix SMB/CIFS implementation.
+ mdssvc client functions
+
+ Copyright (C) Ralph Boehme 2019
+
+ 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 _MDSCLI_PRIVATE_H_
+#define _MDSCLI_PRIVATE_H_
+
+struct mdsctx_id {
+ uint64_t id;
+ uint64_t connection;
+};
+
+struct mdscli_ctx {
+ uint64_t async_pending;
+
+ struct dcerpc_binding_handle *bh;
+ struct policy_handle ph;
+
+ struct mdsctx_id ctx_id;
+ size_t max_fragment_size;
+
+ /* Known fields used across multiple commands */
+ uint32_t dev;
+ uint32_t flags;
+
+ /* cmd specific or unknown fields */
+ struct {
+ char share_path[1025];
+ size_t share_path_len;
+ uint32_t unkn2;
+ uint32_t unkn3;
+ } mdscmd_open;
+ struct {
+ uint32_t status;
+ uint32_t unkn7;
+ } mdscmd_unknown1;
+ struct {
+ uint32_t fragment;
+ uint32_t unkn9;
+ } mdscmd_cmd;
+ struct {
+ uint32_t status;
+ } mdscmd_close;
+
+ char *path_scope;
+ size_t path_scope_len;
+};
+
+struct mdscli_search_ctx {
+ struct mdscli_ctx *mdscli_ctx;
+ struct mdsctx_id ctx_id;
+ uint64_t unique_id;
+ bool live;
+ char *path_scope;
+ char *mds_query;
+};
+
+#endif /* _MDSCLI_PRIVATE_H_ */
diff --git a/source3/rpc_client/cli_mdssvc_util.c b/source3/rpc_client/cli_mdssvc_util.c
new file mode 100644
index 0000000..1eaaca7
--- /dev/null
+++ b/source3/rpc_client/cli_mdssvc_util.c
@@ -0,0 +1,551 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main metadata server / Spotlight client functions
+
+ Copyright (C) Ralph Boehme 2019
+
+ 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 "rpc_client.h"
+#include "librpc/gen_ndr/mdssvc.h"
+#include "cli_mdssvc.h"
+#include "cli_mdssvc_private.h"
+#include "cli_mdssvc_util.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "rpc_server/mdssvc/dalloc.h"
+#include "rpc_server/mdssvc/marshalling.h"
+
+NTSTATUS mdscli_blob_fetch_props(TALLOC_CTX *mem_ctx,
+ struct mdscli_ctx *ctx,
+ struct mdssvc_blob *blob)
+{
+ DALLOC_CTX *d = NULL;
+ uint64_t *uint64p = NULL;
+ sl_array_t *array = NULL;
+ sl_array_t *cmd_array = NULL;
+ NTSTATUS status;
+ int ret;
+
+ d = dalloc_new(mem_ctx);
+ if (d == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ array = dalloc_zero(d, sl_array_t);
+ if (array == NULL) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dalloc_add(d, array, sl_array_t);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ cmd_array = dalloc_zero(d, sl_array_t);
+ if (cmd_array == NULL) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dalloc_add(array, cmd_array, sl_array_t);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dalloc_stradd(cmd_array, "fetchPropertiesForContext:");
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ uint64p = talloc_zero_array(cmd_array, uint64_t, 2);
+ if (uint64p == NULL) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ talloc_set_name(uint64p, "uint64_t *");
+
+ ret = dalloc_add(cmd_array, uint64p, uint64_t *);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = sl_pack_alloc(mem_ctx, d, blob, ctx->max_fragment_size);
+ TALLOC_FREE(d);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return NT_STATUS_OK;
+}
+
+NTSTATUS mdscli_blob_search(TALLOC_CTX *mem_ctx,
+ struct mdscli_search_ctx *search,
+ struct mdssvc_blob *blob)
+{
+ struct mdscli_ctx *ctx = search->mdscli_ctx;
+ DALLOC_CTX *d = NULL;
+ uint64_t *uint64p = NULL;
+ sl_array_t *array = NULL;
+ sl_array_t *cmd_array = NULL;
+ sl_dict_t *query_dict = NULL;
+ sl_array_t *attr_array = NULL;
+ sl_array_t *scope_array = NULL;
+ double dval;
+ uint64_t uint64val;
+ NTSTATUS status;
+ int ret;
+
+ d = dalloc_new(mem_ctx);
+ if (d == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ array = dalloc_zero(d, sl_array_t);
+ if (array == NULL) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dalloc_add(d, array, sl_array_t);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ cmd_array = dalloc_zero(d, sl_array_t);
+ if (cmd_array == NULL) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dalloc_add(array, cmd_array, sl_array_t);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dalloc_stradd(cmd_array, "openQueryWithParams:forContext:");
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ uint64p = talloc_zero_array(cmd_array, uint64_t, 2);
+ if (uint64p == NULL) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ talloc_set_name(uint64p, "uint64_t *");
+
+ uint64p[0] = search->ctx_id.id;
+ uint64p[1] = search->ctx_id.connection;
+
+ ret = dalloc_add(cmd_array, uint64p, uint64_t *);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ query_dict = dalloc_zero(array, sl_dict_t);
+ if (query_dict == NULL) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dalloc_add(array, query_dict, sl_dict_t);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dalloc_stradd(query_dict, "kMDQueryBatchFirstDelay");
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+ dval = 1;
+ ret = dalloc_add_copy(query_dict, &dval, double);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dalloc_stradd(query_dict, "kMDQueryUniqueId");
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ret = dalloc_add_copy(query_dict, &search->unique_id, uint64_t);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dalloc_stradd(query_dict, "kMDAttributeArray");
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+ attr_array = dalloc_zero(query_dict, sl_array_t);
+ if (attr_array == NULL) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ret = dalloc_add(query_dict, attr_array, sl_array_t);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ret = dalloc_stradd(attr_array, "kMDItemFSName");
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dalloc_stradd(query_dict, "kMDQueryBatchFirstCount");
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+ uint64val = 10;
+ ret = dalloc_add_copy(query_dict, &uint64val, uint64_t);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dalloc_stradd(query_dict, "kMDQueryBatchUpdateCount");
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+ uint64val = 100;
+ ret = dalloc_add_copy(query_dict, &uint64val, uint64_t);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dalloc_stradd(query_dict, "kMDQueryString");
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ret = dalloc_stradd(query_dict, search->mds_query);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dalloc_stradd(query_dict, "kMDScopeArray");
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+ scope_array = dalloc_zero(query_dict, sl_array_t);
+ if (scope_array == NULL) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ret = dalloc_add(query_dict, scope_array, sl_array_t);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ret = dalloc_stradd(scope_array, search->path_scope);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = sl_pack_alloc(mem_ctx, d, blob, ctx->max_fragment_size);
+ TALLOC_FREE(d);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return NT_STATUS_OK;
+}
+
+NTSTATUS mdscli_blob_get_results(TALLOC_CTX *mem_ctx,
+ struct mdscli_search_ctx *search,
+ struct mdssvc_blob *blob)
+{
+ struct mdscli_ctx *ctx = search->mdscli_ctx;
+ DALLOC_CTX *d = NULL;
+ uint64_t *uint64p = NULL;
+ sl_array_t *array = NULL;
+ sl_array_t *cmd_array = NULL;
+ NTSTATUS status;
+ int ret;
+
+ d = dalloc_new(mem_ctx);
+ if (d == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ array = dalloc_zero(d, sl_array_t);
+ if (array == NULL) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dalloc_add(d, array, sl_array_t);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ cmd_array = dalloc_zero(d, sl_array_t);
+ if (cmd_array == NULL) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dalloc_add(array, cmd_array, sl_array_t);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dalloc_stradd(cmd_array, "fetchQueryResultsForContext:");
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ uint64p = talloc_zero_array(cmd_array, uint64_t, 2);
+ if (uint64p == NULL) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ talloc_set_name(uint64p, "uint64_t *");
+
+ uint64p[0] = search->ctx_id.id;
+ uint64p[1] = search->ctx_id.connection;
+
+ ret = dalloc_add(cmd_array, uint64p, uint64_t *);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = sl_pack_alloc(mem_ctx, d, blob, ctx->max_fragment_size);
+ TALLOC_FREE(d);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return NT_STATUS_OK;
+}
+
+NTSTATUS mdscli_blob_get_path(TALLOC_CTX *mem_ctx,
+ struct mdscli_ctx *ctx,
+ uint64_t cnid,
+ struct mdssvc_blob *blob)
+{
+ struct mdsctx_id ctx_id = mdscli_new_ctx_id(ctx);
+ DALLOC_CTX *d = NULL;
+ uint64_t *uint64var = NULL;
+ sl_array_t *array = NULL;
+ sl_array_t *cmd_array = NULL;
+ sl_array_t *attr_array = NULL;
+ sl_cnids_t *cnids = NULL;
+ NTSTATUS status;
+ int ret;
+
+ d = dalloc_new(mem_ctx);
+ if (d == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ array = dalloc_zero(d, sl_array_t);
+ if (array == NULL) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dalloc_add(d, array, sl_array_t);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ cmd_array = dalloc_zero(d, sl_array_t);
+ if (cmd_array == NULL) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dalloc_add(array, cmd_array, sl_array_t);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dalloc_stradd(cmd_array, "fetchAttributes:forOIDArray:context:");
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ uint64var = talloc_zero_array(cmd_array, uint64_t, 2);
+ if (uint64var == NULL) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ talloc_set_name(uint64var, "uint64_t *");
+
+ uint64var[0] = ctx_id.id;
+ uint64var[1] = 0;
+
+ ret = dalloc_add(cmd_array, &uint64var[0], uint64_t *);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ attr_array = dalloc_zero(d, sl_array_t);
+ if (attr_array == NULL) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dalloc_add(array, attr_array, sl_array_t);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dalloc_stradd(attr_array, "kMDItemPath");
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* CNIDs */
+ cnids = talloc_zero(array, sl_cnids_t);
+ if (cnids == NULL) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ cnids->ca_cnids = dalloc_new(cnids);
+ if (cnids->ca_cnids == NULL) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ cnids->ca_unkn1 = 0xadd;
+ cnids->ca_context = 0x6b000020;
+
+ ret = dalloc_add_copy(cnids->ca_cnids, &cnid, uint64_t);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dalloc_add(array, cnids, sl_cnids_t);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = sl_pack_alloc(mem_ctx, d, blob, ctx->max_fragment_size);
+ TALLOC_FREE(d);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return NT_STATUS_OK;
+}
+
+NTSTATUS mdscli_blob_close_search(TALLOC_CTX *mem_ctx,
+ struct mdscli_search_ctx *search,
+ struct mdssvc_blob *blob)
+{
+ struct mdscli_ctx *ctx = search->mdscli_ctx;
+ DALLOC_CTX *d = NULL;
+ uint64_t *uint64p = NULL;
+ sl_array_t *array = NULL;
+ sl_array_t *cmd_array = NULL;
+ NTSTATUS status;
+ int ret;
+
+ d = dalloc_new(mem_ctx);
+ if (d == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ array = dalloc_zero(d, sl_array_t);
+ if (array == NULL) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dalloc_add(d, array, sl_array_t);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ cmd_array = dalloc_zero(d, sl_array_t);
+ if (cmd_array == NULL) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dalloc_add(array, cmd_array, sl_array_t);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dalloc_stradd(cmd_array, "closeQueryForContext:");
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ uint64p = talloc_zero_array(cmd_array, uint64_t, 2);
+ if (uint64p == NULL) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ talloc_set_name(uint64p, "uint64_t *");
+
+ uint64p[0] = search->ctx_id.id;
+ uint64p[1] = search->ctx_id.connection;
+
+ ret = dalloc_add(cmd_array, uint64p, uint64_t *);
+ if (ret != 0) {
+ TALLOC_FREE(d);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = sl_pack_alloc(mem_ctx, d, blob, ctx->max_fragment_size);
+ TALLOC_FREE(d);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return NT_STATUS_OK;
+}
diff --git a/source3/rpc_client/cli_mdssvc_util.h b/source3/rpc_client/cli_mdssvc_util.h
new file mode 100644
index 0000000..3f32475
--- /dev/null
+++ b/source3/rpc_client/cli_mdssvc_util.h
@@ -0,0 +1,45 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main metadata server / Spotlight client functions
+
+ Copyright (C) Ralph Boehme 2019
+
+ 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 _MDSCLI_UTIL_H_
+#define _MDSCLI_UTIL_H_
+
+NTSTATUS mdscli_blob_fetch_props(TALLOC_CTX *mem_ctx,
+ struct mdscli_ctx *ctx,
+ struct mdssvc_blob *blob);
+
+NTSTATUS mdscli_blob_search(TALLOC_CTX *mem_ctx,
+ struct mdscli_search_ctx *search,
+ struct mdssvc_blob *blob);
+
+NTSTATUS mdscli_blob_get_results(TALLOC_CTX *mem_ctx,
+ struct mdscli_search_ctx *search,
+ struct mdssvc_blob *blob);
+
+NTSTATUS mdscli_blob_get_path(TALLOC_CTX *mem_ctx,
+ struct mdscli_ctx *mdscli_ctx,
+ uint64_t cnid,
+ struct mdssvc_blob *blob);
+
+NTSTATUS mdscli_blob_close_search(TALLOC_CTX *mem_ctx,
+ struct mdscli_search_ctx *search,
+ struct mdssvc_blob *blob);
+
+#endif /* _MDSCLI_UTIL_H_ */
diff --git a/source3/rpc_client/cli_netlogon.c b/source3/rpc_client/cli_netlogon.c
new file mode 100644
index 0000000..762802d
--- /dev/null
+++ b/source3/rpc_client/cli_netlogon.c
@@ -0,0 +1,860 @@
+/*
+ Unix SMB/CIFS implementation.
+ NT Domain Authentication SMB / MSRPC client
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Jeremy Allison 1998.
+ Largely re-written by Jeremy Allison (C) 2005.
+ Copyright (C) Guenther Deschner 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 "system/filesys.h"
+#include "libsmb/libsmb.h"
+#include "rpc_client/rpc_client.h"
+#include "rpc_client/cli_pipe.h"
+#include "../libcli/auth/libcli_auth.h"
+#include "../libcli/auth/netlogon_creds_cli.h"
+#include "../librpc/gen_ndr/ndr_netlogon_c.h"
+#include "../librpc/gen_ndr/schannel.h"
+#include "rpc_client/cli_netlogon.h"
+#include "rpc_client/util_netlogon.h"
+#include "../libcli/security/security.h"
+#include "lib/param/param.h"
+#include "libcli/smb/smbXcli_base.h"
+#include "dbwrap/dbwrap.h"
+#include "dbwrap/dbwrap_open.h"
+#include "util_tdb.h"
+#include "lib/crypto/gnutls_helpers.h"
+
+
+NTSTATUS rpccli_pre_open_netlogon_creds(void)
+{
+ static bool already_open = false;
+ TALLOC_CTX *frame;
+ struct loadparm_context *lp_ctx;
+ char *fname;
+ struct db_context *global_db;
+ NTSTATUS status;
+
+ if (already_open) {
+ return NT_STATUS_OK;
+ }
+
+ frame = talloc_stackframe();
+
+ lp_ctx = loadparm_init_s3(frame, loadparm_s3_helpers());
+ if (lp_ctx == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ fname = lpcfg_private_db_path(frame, lp_ctx, "netlogon_creds_cli");
+ if (fname == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ global_db = db_open(frame, fname,
+ 0, TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
+ O_RDWR|O_CREAT, 0600, DBWRAP_LOCK_ORDER_2,
+ DBWRAP_FLAG_OPTIMIZE_READONLY_ACCESS);
+ if (global_db == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = netlogon_creds_cli_set_global_db(lp_ctx, &global_db);
+ TALLOC_FREE(frame);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ already_open = true;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS rpccli_create_netlogon_creds(
+ const char *server_computer,
+ const char *server_netbios_domain,
+ const char *server_dns_domain,
+ const char *client_account,
+ enum netr_SchannelType sec_chan_type,
+ struct messaging_context *msg_ctx,
+ TALLOC_CTX *mem_ctx,
+ struct netlogon_creds_cli_context **netlogon_creds)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct loadparm_context *lp_ctx;
+ NTSTATUS status;
+
+ status = rpccli_pre_open_netlogon_creds();
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ lp_ctx = loadparm_init_s3(frame, loadparm_s3_helpers());
+ if (lp_ctx == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = netlogon_creds_cli_context_global(lp_ctx,
+ msg_ctx,
+ client_account,
+ sec_chan_type,
+ server_computer,
+ server_netbios_domain,
+ server_dns_domain,
+ mem_ctx, netlogon_creds);
+ TALLOC_FREE(frame);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS rpccli_create_netlogon_creds_ctx(
+ struct cli_credentials *creds,
+ const char *server_computer,
+ struct messaging_context *msg_ctx,
+ TALLOC_CTX *mem_ctx,
+ struct netlogon_creds_cli_context **creds_ctx)
+{
+ enum netr_SchannelType sec_chan_type;
+ const char *server_netbios_domain;
+ const char *server_dns_domain;
+ const char *client_account;
+
+ sec_chan_type = cli_credentials_get_secure_channel_type(creds);
+ client_account = cli_credentials_get_username(creds);
+ server_netbios_domain = cli_credentials_get_domain(creds);
+ server_dns_domain = cli_credentials_get_realm(creds);
+
+ return rpccli_create_netlogon_creds(server_computer,
+ server_netbios_domain,
+ server_dns_domain,
+ client_account,
+ sec_chan_type,
+ msg_ctx, mem_ctx,
+ creds_ctx);
+}
+
+NTSTATUS rpccli_setup_netlogon_creds_locked(
+ struct cli_state *cli,
+ enum dcerpc_transport_t transport,
+ struct netlogon_creds_cli_context *creds_ctx,
+ bool force_reauth,
+ struct cli_credentials *cli_creds,
+ uint32_t *negotiate_flags)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct rpc_pipe_client *netlogon_pipe = NULL;
+ struct netlogon_creds_CredentialState *creds = NULL;
+ uint8_t num_nt_hashes = 0;
+ const struct samr_Password *nt_hashes[2] = { NULL, NULL };
+ uint8_t idx_nt_hashes = 0;
+ NTSTATUS status;
+ const char *remote_name = NULL;
+ const struct sockaddr_storage *remote_sockaddr = NULL;
+
+ status = netlogon_creds_cli_get(creds_ctx, frame, &creds);
+ if (NT_STATUS_IS_OK(status)) {
+ const char *action = "using";
+
+ if (force_reauth) {
+ action = "overwrite";
+ }
+
+ if (cli != NULL) {
+ remote_name = smbXcli_conn_remote_name(cli->conn);
+ } else {
+ remote_name = "<UNKNOWN>";
+ }
+
+ DEBUG(5,("%s: %s cached netlogon_creds cli[%s/%s] to %s\n",
+ __FUNCTION__, action,
+ creds->account_name, creds->computer_name,
+ remote_name));
+ if (!force_reauth) {
+ goto done;
+ }
+ TALLOC_FREE(creds);
+ }
+
+ nt_hashes[0] = cli_credentials_get_nt_hash(cli_creds, talloc_tos());
+ if (nt_hashes[0] == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ num_nt_hashes = 1;
+
+ nt_hashes[1] = cli_credentials_get_old_nt_hash(cli_creds,
+ talloc_tos());
+ if (nt_hashes[1] != NULL) {
+ num_nt_hashes = 2;
+ }
+
+ remote_name = smbXcli_conn_remote_name(cli->conn);
+ remote_sockaddr = smbXcli_conn_remote_sockaddr(cli->conn);
+
+ status = cli_rpc_pipe_open_noauth_transport(cli,
+ transport,
+ &ndr_table_netlogon,
+ remote_name,
+ remote_sockaddr,
+ &netlogon_pipe);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5,("%s: failed to open noauth netlogon connection to %s - %s\n",
+ __FUNCTION__,
+ remote_name,
+ nt_errstr(status)));
+ TALLOC_FREE(frame);
+ return status;
+ }
+ talloc_steal(frame, netlogon_pipe);
+
+ status = netlogon_creds_cli_auth(creds_ctx,
+ netlogon_pipe->binding_handle,
+ num_nt_hashes,
+ nt_hashes,
+ &idx_nt_hashes);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ status = netlogon_creds_cli_get(creds_ctx, frame, &creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ DEBUG(5,("%s: using new netlogon_creds cli[%s/%s] to %s\n",
+ __FUNCTION__,
+ creds->account_name, creds->computer_name,
+ remote_name));
+
+done:
+ if (negotiate_flags != NULL) {
+ *negotiate_flags = creds->negotiate_flags;
+ }
+
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS rpccli_setup_netlogon_creds(
+ struct cli_state *cli,
+ enum dcerpc_transport_t transport,
+ struct netlogon_creds_cli_context *creds_ctx,
+ bool force_reauth,
+ struct cli_credentials *cli_creds)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct netlogon_creds_cli_lck *lck;
+ NTSTATUS status;
+
+ status = netlogon_creds_cli_lck(
+ creds_ctx, NETLOGON_CREDS_CLI_LCK_EXCLUSIVE,
+ frame, &lck);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("netlogon_creds_cli_lck failed: %s\n",
+ nt_errstr(status));
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ status = rpccli_setup_netlogon_creds_locked(
+ cli, transport, creds_ctx, force_reauth, cli_creds, NULL);
+
+ TALLOC_FREE(frame);
+
+ return status;
+}
+
+NTSTATUS rpccli_connect_netlogon(
+ struct cli_state *cli,
+ enum dcerpc_transport_t transport,
+ struct netlogon_creds_cli_context *creds_ctx,
+ bool force_reauth,
+ struct cli_credentials *trust_creds,
+ struct rpc_pipe_client **_rpccli)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct netlogon_creds_CredentialState *creds = NULL;
+ enum netlogon_creds_cli_lck_type lck_type;
+ enum netr_SchannelType sec_chan_type;
+ struct netlogon_creds_cli_lck *lck = NULL;
+ uint32_t negotiate_flags;
+ uint8_t found_session_key[16] = {0};
+ bool found_existing_creds = false;
+ bool do_serverauth;
+ struct rpc_pipe_client *rpccli;
+ NTSTATUS status;
+ bool retry = false;
+ const char *remote_name = NULL;
+ const struct sockaddr_storage *remote_sockaddr = NULL;
+
+ sec_chan_type = cli_credentials_get_secure_channel_type(trust_creds);
+ if (sec_chan_type == SEC_CHAN_NULL) {
+ DBG_ERR("secure_channel_type gave SEC_CHAN_NULL\n");
+ status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ goto fail;
+ }
+
+again:
+
+ /*
+ * See whether we can use existing netlogon_creds or
+ * whether we have to serverauthenticate.
+ */
+ status = netlogon_creds_cli_get(creds_ctx, frame, &creds);
+
+ if (NT_STATUS_IS_OK(status)) {
+ bool cmp = mem_equal_const_time(found_session_key,
+ creds->session_key,
+ sizeof(found_session_key));
+ found_existing_creds = !cmp;
+
+ memcpy(found_session_key,
+ creds->session_key,
+ sizeof(found_session_key));
+
+ TALLOC_FREE(creds);
+ }
+
+ lck_type = (force_reauth || !found_existing_creds) ?
+ NETLOGON_CREDS_CLI_LCK_EXCLUSIVE :
+ NETLOGON_CREDS_CLI_LCK_SHARED;
+
+ status = netlogon_creds_cli_lck(creds_ctx, lck_type, frame, &lck);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("netlogon_creds_cli_lck failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ if (!found_existing_creds) {
+ /*
+ * Try to find creds under the lock again. Someone
+ * else might have done it for us.
+ */
+ status = netlogon_creds_cli_get(creds_ctx, frame, &creds);
+
+ if (NT_STATUS_IS_OK(status)) {
+ bool cmp = mem_equal_const_time(found_session_key,
+ creds->session_key,
+ sizeof(found_session_key));
+ found_existing_creds = !cmp;
+
+ memcpy(found_session_key, creds->session_key,
+ sizeof(found_session_key));
+
+ TALLOC_FREE(creds);
+ }
+ }
+
+ remote_name = smbXcli_conn_remote_name(cli->conn);
+ remote_sockaddr = smbXcli_conn_remote_sockaddr(cli->conn);
+
+ do_serverauth = force_reauth || !found_existing_creds;
+
+ if (!do_serverauth) {
+ /*
+ * Do the quick schannel bind without a reauth
+ */
+ status = cli_rpc_pipe_open_bind_schannel(cli,
+ &ndr_table_netlogon,
+ transport,
+ creds_ctx,
+ remote_name,
+ remote_sockaddr,
+ &rpccli);
+ if (!retry && NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED)) {
+ DBG_DEBUG("Retrying with serverauthenticate\n");
+ TALLOC_FREE(lck);
+ retry = true;
+ goto again;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("cli_rpc_pipe_open_bind_schannel "
+ "failed: %s\n", nt_errstr(status));
+ goto fail;
+ }
+ goto done;
+ }
+
+ if (cli_credentials_is_anonymous(trust_creds)) {
+ DBG_WARNING("get_trust_credential for %s only gave anonymous,"
+ "unable to negotiate NETLOGON credentials\n",
+ netlogon_creds_cli_debug_string(
+ creds_ctx, frame));
+ status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ goto fail;
+ }
+
+ status = rpccli_setup_netlogon_creds_locked(
+ cli, transport, creds_ctx, true, trust_creds,
+ &negotiate_flags);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("rpccli_setup_netlogon_creds failed for %s, "
+ "unable to setup NETLOGON credentials: %s\n",
+ netlogon_creds_cli_debug_string(
+ creds_ctx, frame),
+ nt_errstr(status));
+ goto fail;
+ }
+
+ if (!(negotiate_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
+ if (lp_winbind_sealed_pipes() || lp_require_strong_key()) {
+ status = NT_STATUS_DOWNGRADE_DETECTED;
+ DBG_WARNING("Unwilling to make connection to %s"
+ "without connection level security, "
+ "must set 'winbind sealed pipes = false'"
+ " and 'require strong key = false' "
+ "to proceed: %s\n",
+ netlogon_creds_cli_debug_string(
+ creds_ctx, frame),
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = cli_rpc_pipe_open_noauth_transport(cli,
+ transport,
+ &ndr_table_netlogon,
+ remote_name,
+ remote_sockaddr,
+ &rpccli);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("cli_rpc_pipe_open_noauth_transport "
+ "failed: %s\n", nt_errstr(status));
+ goto fail;
+ }
+ goto done;
+ }
+
+ status = cli_rpc_pipe_open_bind_schannel(cli,
+ &ndr_table_netlogon,
+ transport,
+ creds_ctx,
+ remote_name,
+ remote_sockaddr,
+ &rpccli);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("cli_rpc_pipe_open_bind_schannel "
+ "failed: %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ status = netlogon_creds_cli_check(creds_ctx, rpccli->binding_handle,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("netlogon_creds_cli_check failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+done:
+ *_rpccli = rpccli;
+ status = NT_STATUS_OK;
+fail:
+ ZERO_STRUCT(found_session_key);
+ TALLOC_FREE(lck);
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/* Logon domain user */
+
+NTSTATUS rpccli_netlogon_password_logon(
+ struct netlogon_creds_cli_context *creds_ctx,
+ struct dcerpc_binding_handle *binding_handle,
+ TALLOC_CTX *mem_ctx,
+ uint32_t logon_parameters,
+ const char *domain,
+ const char *username,
+ const char *password,
+ const char *workstation,
+ const uint64_t logon_id,
+ enum netr_LogonInfoClass logon_type,
+ uint8_t *authoritative,
+ uint32_t *flags,
+ uint16_t *_validation_level,
+ union netr_Validation **_validation)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status;
+ union netr_LogonLevel *logon;
+ uint16_t validation_level = 0;
+ union netr_Validation *validation = NULL;
+ char *workstation_slash = NULL;
+
+ unsigned char local_nt_response[24];
+ unsigned char local_lm_response[24];
+ struct samr_Password lmpassword = {.hash = {0}};
+ struct samr_Password ntpassword = {.hash = {0}};
+ struct netr_ChallengeResponse lm = {0};
+ struct netr_ChallengeResponse nt = {0};
+
+ logon = talloc_zero(frame, union netr_LogonLevel);
+ if (logon == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (workstation == NULL) {
+ workstation = lp_netbios_name();
+ }
+
+ workstation_slash = talloc_asprintf(frame, "\\\\%s", workstation);
+ if (workstation_slash == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Initialise input parameters */
+
+ switch (logon_type) {
+ case NetlogonInteractiveInformation:
+ case NetlogonInteractiveTransitiveInformation: {
+
+ struct netr_PasswordInfo *password_info;
+
+
+ password_info = talloc_zero(frame, struct netr_PasswordInfo);
+ if (password_info == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_lm_owf_gen(password, ntpassword.hash, lmpassword.hash);
+
+ password_info->identity_info.domain_name.string = domain;
+ password_info->identity_info.parameter_control = logon_parameters;
+ password_info->identity_info.logon_id = logon_id;
+ password_info->identity_info.account_name.string = username;
+ password_info->identity_info.workstation.string = workstation_slash;
+
+ password_info->lmpassword = lmpassword;
+ password_info->ntpassword = ntpassword;
+
+ logon->password = password_info;
+
+ break;
+ }
+ case NetlogonNetworkInformation:
+ case NetlogonNetworkTransitiveInformation: {
+ struct netr_NetworkInfo *network_info;
+ uint8_t chal[8];
+ int rc;
+
+ ZERO_STRUCT(lm);
+ ZERO_STRUCT(nt);
+
+ network_info = talloc_zero(frame, struct netr_NetworkInfo);
+ if (network_info == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ generate_random_buffer(chal, 8);
+
+ SMBencrypt(password, chal, local_lm_response);
+ rc = SMBNTencrypt(password, chal, local_nt_response);
+ if (rc != 0) {
+ TALLOC_FREE(frame);
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+
+ lm.length = 24;
+ lm.data = local_lm_response;
+
+ nt.length = 24;
+ nt.data = local_nt_response;
+
+ network_info->identity_info.domain_name.string = domain;
+ network_info->identity_info.parameter_control = logon_parameters;
+ network_info->identity_info.logon_id = logon_id;
+ network_info->identity_info.account_name.string = username;
+ network_info->identity_info.workstation.string = workstation_slash;
+
+ memcpy(network_info->challenge, chal, 8);
+ network_info->nt = nt;
+ network_info->lm = lm;
+
+ logon->network = network_info;
+
+ break;
+ }
+ default:
+ DEBUG(0, ("switch value %d not supported\n",
+ logon_type));
+ TALLOC_FREE(frame);
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ status = netlogon_creds_cli_LogonSamLogon(creds_ctx,
+ binding_handle,
+ logon_type,
+ logon,
+ mem_ctx,
+ &validation_level,
+ &validation,
+ authoritative,
+ flags);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ TALLOC_FREE(frame);
+ *_validation_level = validation_level;
+ *_validation = validation;
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * Logon domain user with an 'network' SAM logon
+ *
+ * @param info3 Pointer to a NET_USER_INFO_3 already allocated by the caller.
+ **/
+
+
+NTSTATUS rpccli_netlogon_network_logon(
+ struct netlogon_creds_cli_context *creds_ctx,
+ struct dcerpc_binding_handle *binding_handle,
+ TALLOC_CTX *mem_ctx,
+ uint32_t logon_parameters,
+ const char *username,
+ const char *domain,
+ const char *workstation,
+ const uint64_t logon_id,
+ DATA_BLOB chal,
+ DATA_BLOB lm_response,
+ DATA_BLOB nt_response,
+ enum netr_LogonInfoClass logon_type,
+ uint8_t *authoritative,
+ uint32_t *flags,
+ uint16_t *_validation_level,
+ union netr_Validation **_validation)
+{
+ NTSTATUS status;
+ const char *workstation_name_slash;
+ union netr_LogonLevel *logon = NULL;
+ struct netr_NetworkInfo *network_info;
+ uint16_t validation_level = 0;
+ union netr_Validation *validation = NULL;
+ struct netr_ChallengeResponse lm;
+ struct netr_ChallengeResponse nt;
+
+ *_validation = NULL;
+
+ ZERO_STRUCT(lm);
+ ZERO_STRUCT(nt);
+
+ switch (logon_type) {
+ case NetlogonNetworkInformation:
+ case NetlogonNetworkTransitiveInformation:
+ break;
+ default:
+ DEBUG(0, ("switch value %d not supported\n",
+ logon_type));
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ logon = talloc_zero(mem_ctx, union netr_LogonLevel);
+ if (!logon) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ network_info = talloc_zero(mem_ctx, struct netr_NetworkInfo);
+ if (!network_info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (workstation == NULL) {
+ workstation = lp_netbios_name();
+ }
+
+ if (workstation[0] != '\\' && workstation[1] != '\\') {
+ workstation_name_slash = talloc_asprintf(mem_ctx, "\\\\%s", workstation);
+ } else {
+ workstation_name_slash = workstation;
+ }
+
+ if (!workstation_name_slash) {
+ DEBUG(0, ("talloc_asprintf failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Initialise input parameters */
+
+ lm.data = lm_response.data;
+ lm.length = lm_response.length;
+ nt.data = nt_response.data;
+ nt.length = nt_response.length;
+
+ network_info->identity_info.domain_name.string = domain;
+ network_info->identity_info.parameter_control = logon_parameters;
+ network_info->identity_info.logon_id = logon_id;
+ network_info->identity_info.account_name.string = username;
+ network_info->identity_info.workstation.string = workstation_name_slash;
+
+ if (chal.length != 8) {
+ DBG_WARNING("Invalid challenge length %zd\n", chal.length);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ memcpy(network_info->challenge, chal.data, chal.length);
+ network_info->nt = nt;
+ network_info->lm = lm;
+
+ logon->network = network_info;
+
+ /* Marshall data and send request */
+
+ status = netlogon_creds_cli_LogonSamLogon(creds_ctx,
+ binding_handle,
+ logon_type,
+ logon,
+ mem_ctx,
+ &validation_level,
+ &validation,
+ authoritative,
+ flags);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ *_validation_level = validation_level;
+ *_validation = validation;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS rpccli_netlogon_interactive_logon(
+ struct netlogon_creds_cli_context *creds_ctx,
+ struct dcerpc_binding_handle *binding_handle,
+ TALLOC_CTX *mem_ctx,
+ uint32_t logon_parameters,
+ const char *username,
+ const char *domain,
+ const char *workstation,
+ const uint64_t logon_id,
+ DATA_BLOB lm_hash,
+ DATA_BLOB nt_hash,
+ enum netr_LogonInfoClass logon_type,
+ uint8_t *authoritative,
+ uint32_t *flags,
+ uint16_t *_validation_level,
+ union netr_Validation **_validation)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status;
+ const char *workstation_name_slash;
+ union netr_LogonLevel *logon = NULL;
+ struct netr_PasswordInfo *password_info = NULL;
+ uint16_t validation_level = 0;
+ union netr_Validation *validation = NULL;
+ struct netr_ChallengeResponse lm;
+ struct netr_ChallengeResponse nt;
+
+ *_validation = NULL;
+
+ ZERO_STRUCT(lm);
+ ZERO_STRUCT(nt);
+
+ switch (logon_type) {
+ case NetlogonInteractiveInformation:
+ case NetlogonInteractiveTransitiveInformation:
+ break;
+ default:
+ DEBUG(0, ("switch value %d not supported\n",
+ logon_type));
+ TALLOC_FREE(frame);
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ logon = talloc_zero(mem_ctx, union netr_LogonLevel);
+ if (logon == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ password_info = talloc_zero(logon, struct netr_PasswordInfo);
+ if (password_info == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (workstation[0] != '\\' && workstation[1] != '\\') {
+ workstation_name_slash = talloc_asprintf(frame, "\\\\%s", workstation);
+ } else {
+ workstation_name_slash = workstation;
+ }
+
+ if (workstation_name_slash == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Initialise input parameters */
+
+ password_info->identity_info.domain_name.string = domain;
+ password_info->identity_info.parameter_control = logon_parameters;
+ password_info->identity_info.logon_id = logon_id;
+ password_info->identity_info.account_name.string = username;
+ password_info->identity_info.workstation.string = workstation_name_slash;
+
+ if (nt_hash.length != sizeof(password_info->ntpassword.hash)) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ memcpy(password_info->ntpassword.hash, nt_hash.data, nt_hash.length);
+ if (lm_hash.length != 0) {
+ if (lm_hash.length != sizeof(password_info->lmpassword.hash)) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ memcpy(password_info->lmpassword.hash, lm_hash.data, lm_hash.length);
+ }
+
+ logon->password = password_info;
+
+ /* Marshall data and send request */
+
+ status = netlogon_creds_cli_LogonSamLogon(creds_ctx,
+ binding_handle,
+ logon_type,
+ logon,
+ mem_ctx,
+ &validation_level,
+ &validation,
+ authoritative,
+ flags);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ *_validation_level = validation_level;
+ *_validation = validation;
+
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+}
diff --git a/source3/rpc_client/cli_netlogon.h b/source3/rpc_client/cli_netlogon.h
new file mode 100644
index 0000000..4644925
--- /dev/null
+++ b/source3/rpc_client/cli_netlogon.h
@@ -0,0 +1,111 @@
+/*
+ Unix SMB/CIFS implementation.
+ NT Domain Authentication SMB / MSRPC client
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Jeremy Allison 1998.
+ Largely re-written by Jeremy Allison (C) 2005.
+ Copyright (C) Guenther Deschner 2008.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _RPC_CLIENT_CLI_NETLOGON_H_
+#define _RPC_CLIENT_CLI_NETLOGON_H_
+
+struct cli_state;
+struct messaging_context;
+struct cli_credentials;
+struct netlogon_creds_cli_context;
+struct dcerpc_binding_handle;
+#include "librpc/rpc/rpc_common.h"
+
+/* The following definitions come from rpc_client/cli_netlogon.c */
+
+NTSTATUS rpccli_pre_open_netlogon_creds(void);
+NTSTATUS rpccli_create_netlogon_creds_ctx(
+ struct cli_credentials *creds,
+ const char *server_computer,
+ struct messaging_context *msg_ctx,
+ TALLOC_CTX *mem_ctx,
+ struct netlogon_creds_cli_context **creds_ctx);
+NTSTATUS rpccli_setup_netlogon_creds_locked(
+ struct cli_state *cli,
+ enum dcerpc_transport_t transport,
+ struct netlogon_creds_cli_context *creds_ctx,
+ bool force_reauth,
+ struct cli_credentials *cli_creds,
+ uint32_t *negotiate_flags);
+NTSTATUS rpccli_setup_netlogon_creds(
+ struct cli_state *cli,
+ enum dcerpc_transport_t transport,
+ struct netlogon_creds_cli_context *creds_ctx,
+ bool force_reauth,
+ struct cli_credentials *cli_creds);
+NTSTATUS rpccli_connect_netlogon(
+ struct cli_state *cli,
+ enum dcerpc_transport_t transport,
+ struct netlogon_creds_cli_context *creds_ctx,
+ bool force_reauth,
+ struct cli_credentials *trust_creds,
+ struct rpc_pipe_client **_rpccli);
+NTSTATUS rpccli_netlogon_password_logon(
+ struct netlogon_creds_cli_context *creds,
+ struct dcerpc_binding_handle *binding_handle,
+ TALLOC_CTX *mem_ctx,
+ uint32_t logon_parameters,
+ const char *domain,
+ const char *username,
+ const char *password,
+ const char *workstation,
+ const uint64_t logon_id,
+ enum netr_LogonInfoClass logon_type,
+ uint8_t *authoritative,
+ uint32_t *flags,
+ uint16_t *_validation_level,
+ union netr_Validation **_validation);
+NTSTATUS rpccli_netlogon_network_logon(
+ struct netlogon_creds_cli_context *creds_ctx,
+ struct dcerpc_binding_handle *binding_handle,
+ TALLOC_CTX *mem_ctx,
+ uint32_t logon_parameters,
+ const char *username,
+ const char *domain,
+ const char *workstation,
+ const uint64_t logon_id,
+ DATA_BLOB chal,
+ DATA_BLOB lm_response,
+ DATA_BLOB nt_response,
+ enum netr_LogonInfoClass logon_type,
+ uint8_t *authoritative,
+ uint32_t *flags,
+ uint16_t *_validation_level,
+ union netr_Validation **_validation);
+NTSTATUS rpccli_netlogon_interactive_logon(
+ struct netlogon_creds_cli_context *creds_ctx,
+ struct dcerpc_binding_handle *binding_handle,
+ TALLOC_CTX *mem_ctx,
+ uint32_t logon_parameters,
+ const char *username,
+ const char *domain,
+ const char *workstation,
+ const uint64_t logon_id,
+ DATA_BLOB lm_hash,
+ DATA_BLOB nt_hash,
+ enum netr_LogonInfoClass logon_type,
+ uint8_t *authoritative,
+ uint32_t *flags,
+ uint16_t *_validation_level,
+ union netr_Validation **_validation);
+
+#endif /* _RPC_CLIENT_CLI_NETLOGON_H_ */
diff --git a/source3/rpc_client/cli_pipe.c b/source3/rpc_client/cli_pipe.c
new file mode 100644
index 0000000..b4289e9
--- /dev/null
+++ b/source3/rpc_client/cli_pipe.c
@@ -0,0 +1,3677 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client routines
+ * Largely rewritten by Jeremy Allison 2005.
+ * Heavily modified by Simo Sorce 2010.
+ * Copyright Andrew Bartlett 2011.
+ *
+ * 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 "libsmb/namequery.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "librpc/gen_ndr/ndr_epmapper_c.h"
+#include "../librpc/gen_ndr/ndr_dssetup.h"
+#include "../libcli/auth/schannel.h"
+#include "../libcli/auth/netlogon_creds_cli.h"
+#include "auth_generic.h"
+#include "librpc/gen_ndr/ndr_dcerpc.h"
+#include "librpc/gen_ndr/ndr_netlogon_c.h"
+#include "librpc/gen_ndr/auth.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_util.h"
+#include "rpc_dce.h"
+#include "cli_pipe.h"
+#include "libsmb/libsmb.h"
+#include "auth/gensec/gensec.h"
+#include "auth/credentials/credentials.h"
+#include "auth/auth_util.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "lib/tsocket/tsocket.h"
+#include "libcli/named_pipe_auth/npa_tstream.h"
+#include "librpc/gen_ndr/ndr_winreg.h"
+#include "local_np.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_CLI
+
+/********************************************************************
+ Pipe description for a DEBUG
+ ********************************************************************/
+static const char *rpccli_pipe_txt(TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client *cli)
+{
+ char *result = talloc_asprintf(mem_ctx, "host %s", cli->desthost);
+ if (result == NULL) {
+ return "pipe";
+ }
+ return result;
+}
+
+/********************************************************************
+ Rpc pipe call id.
+ ********************************************************************/
+
+static uint32_t get_rpc_call_id(void)
+{
+ static uint32_t call_id = 0;
+ return ++call_id;
+}
+
+/*******************************************************************
+ Use SMBreadX to get rest of one fragment's worth of rpc data.
+ Reads the whole size or give an error message
+ ********************************************************************/
+
+struct rpc_read_state {
+ struct tevent_context *ev;
+ struct rpc_cli_transport *transport;
+ uint8_t *data;
+ size_t size;
+ size_t num_read;
+};
+
+static void rpc_read_done(struct tevent_req *subreq);
+
+static struct tevent_req *rpc_read_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct rpc_cli_transport *transport,
+ uint8_t *data, size_t size)
+{
+ struct tevent_req *req, *subreq;
+ struct rpc_read_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct rpc_read_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->transport = transport;
+ state->data = data;
+ state->size = size;
+ state->num_read = 0;
+
+ DBG_INFO("data_to_read: %zu\n", size);
+
+ subreq = transport->read_send(state, ev, (uint8_t *)data, size,
+ transport->priv);
+ if (subreq == NULL) {
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, rpc_read_done, req);
+ return req;
+
+ fail:
+ TALLOC_FREE(req);
+ return NULL;
+}
+
+static void rpc_read_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct rpc_read_state *state = tevent_req_data(
+ req, struct rpc_read_state);
+ NTSTATUS status;
+ ssize_t received;
+
+ status = state->transport->read_recv(subreq, &received);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->num_read += received;
+ if (state->num_read == state->size) {
+ tevent_req_done(req);
+ return;
+ }
+
+ subreq = state->transport->read_send(state, state->ev,
+ state->data + state->num_read,
+ state->size - state->num_read,
+ state->transport->priv);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, rpc_read_done, req);
+}
+
+static NTSTATUS rpc_read_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+struct rpc_write_state {
+ struct tevent_context *ev;
+ struct rpc_cli_transport *transport;
+ const uint8_t *data;
+ size_t size;
+ size_t num_written;
+};
+
+static void rpc_write_done(struct tevent_req *subreq);
+
+static struct tevent_req *rpc_write_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct rpc_cli_transport *transport,
+ const uint8_t *data, size_t size)
+{
+ struct tevent_req *req, *subreq;
+ struct rpc_write_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct rpc_write_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->transport = transport;
+ state->data = data;
+ state->size = size;
+ state->num_written = 0;
+
+ DBG_INFO("data_to_write: %zu\n", size);
+
+ subreq = transport->write_send(state, ev, data, size, transport->priv);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, rpc_write_done, req);
+ return req;
+}
+
+static void rpc_write_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct rpc_write_state *state = tevent_req_data(
+ req, struct rpc_write_state);
+ NTSTATUS status;
+ ssize_t written;
+
+ status = state->transport->write_recv(subreq, &written);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->num_written += written;
+
+ if (state->num_written == state->size) {
+ tevent_req_done(req);
+ return;
+ }
+
+ subreq = state->transport->write_send(state, state->ev,
+ state->data + state->num_written,
+ state->size - state->num_written,
+ state->transport->priv);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, rpc_write_done, req);
+}
+
+static NTSTATUS rpc_write_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+
+/****************************************************************************
+ Try and get a PDU's worth of data from current_pdu. If not, then read more
+ from the wire.
+ ****************************************************************************/
+
+struct get_complete_frag_state {
+ struct tevent_context *ev;
+ struct rpc_pipe_client *cli;
+ uint16_t frag_len;
+ DATA_BLOB *pdu;
+};
+
+static void get_complete_frag_got_header(struct tevent_req *subreq);
+static void get_complete_frag_got_rest(struct tevent_req *subreq);
+
+static struct tevent_req *get_complete_frag_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct rpc_pipe_client *cli,
+ DATA_BLOB *pdu)
+{
+ struct tevent_req *req, *subreq;
+ struct get_complete_frag_state *state;
+ size_t received;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct get_complete_frag_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->frag_len = RPC_HEADER_LEN;
+ state->pdu = pdu;
+
+ received = pdu->length;
+ if (received < RPC_HEADER_LEN) {
+ if (!data_blob_realloc(mem_ctx, pdu, RPC_HEADER_LEN)) {
+ tevent_req_oom(req);
+ return tevent_req_post(req, ev);
+ }
+ subreq = rpc_read_send(state, state->ev,
+ state->cli->transport,
+ pdu->data + received,
+ RPC_HEADER_LEN - received);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, get_complete_frag_got_header,
+ req);
+ return req;
+ }
+
+ state->frag_len = dcerpc_get_frag_length(pdu);
+ if (state->frag_len < RPC_HEADER_LEN) {
+ tevent_req_nterror(req, NT_STATUS_RPC_PROTOCOL_ERROR);
+ return tevent_req_post(req, ev);
+ }
+
+ if (received >= state->frag_len) {
+ /*
+ * Got the whole fragment
+ */
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ if (!data_blob_realloc(NULL, pdu, state->frag_len)) {
+ tevent_req_oom(req);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = rpc_read_send(
+ state,
+ state->ev,
+ state->cli->transport,
+ pdu->data + received,
+ state->frag_len - received);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, get_complete_frag_got_rest, req);
+ return req;
+}
+
+static void get_complete_frag_got_header(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct get_complete_frag_state *state = tevent_req_data(
+ req, struct get_complete_frag_state);
+ NTSTATUS status;
+
+ status = rpc_read_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->frag_len = dcerpc_get_frag_length(state->pdu);
+ if (state->frag_len < RPC_HEADER_LEN) {
+ tevent_req_nterror(req, NT_STATUS_RPC_PROTOCOL_ERROR);
+ return;
+ }
+
+ if (!data_blob_realloc(NULL, state->pdu, state->frag_len)) {
+ tevent_req_oom(req);
+ return;
+ }
+
+ /*
+ * We're here in this piece of code because we've read exactly
+ * RPC_HEADER_LEN bytes into state->pdu.
+ */
+
+ subreq = rpc_read_send(state, state->ev, state->cli->transport,
+ state->pdu->data + RPC_HEADER_LEN,
+ state->frag_len - RPC_HEADER_LEN);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, get_complete_frag_got_rest, req);
+}
+
+static void get_complete_frag_got_rest(struct tevent_req *subreq)
+{
+ NTSTATUS status = rpc_read_recv(subreq);
+ return tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static NTSTATUS get_complete_frag_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+/****************************************************************************
+ Do basic authentication checks on an incoming pdu.
+ ****************************************************************************/
+
+static NTSTATUS cli_pipe_validate_current_pdu(TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client *cli,
+ struct ncacn_packet *pkt,
+ DATA_BLOB *pdu,
+ uint8_t expected_pkt_type,
+ uint32_t call_id,
+ DATA_BLOB *rdata,
+ DATA_BLOB *reply_pdu)
+{
+ const struct dcerpc_response *r = NULL;
+ DATA_BLOB tmp_stub = { .data = NULL };
+ NTSTATUS ret;
+
+ /*
+ * Point the return values at the real data including the RPC
+ * header. Just in case the caller wants it.
+ */
+ *rdata = *pdu;
+
+ if ((pkt->ptype == DCERPC_PKT_BIND_ACK) &&
+ !(pkt->pfc_flags & DCERPC_PFC_FLAG_LAST)) {
+ /*
+ * TODO: do we still need this hack which was introduced
+ * in commit a42afcdcc7ab9aa9ed193ae36d3dbb10843447f0.
+ *
+ * I don't even know what AS/U might be...
+ */
+ DEBUG(5, (__location__ ": bug in server (AS/U?), setting "
+ "fragment first/last ON.\n"));
+ pkt->pfc_flags |= DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+ }
+
+ /* Ensure we have the correct type. */
+ switch (pkt->ptype) {
+ case DCERPC_PKT_BIND_NAK:
+ DEBUG(1, (__location__ ": Bind NACK received from %s!\n",
+ rpccli_pipe_txt(talloc_tos(), cli)));
+
+ ret = dcerpc_verify_ncacn_packet_header(pkt,
+ DCERPC_PKT_BIND_NAK,
+ 0, /* max_auth_info */
+ DCERPC_PFC_FLAG_FIRST |
+ DCERPC_PFC_FLAG_LAST,
+ 0); /* optional flags */
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1, (__location__ ": Connection to %s got an unexpected "
+ "RPC packet type - %u, expected %u: %s\n",
+ rpccli_pipe_txt(talloc_tos(), cli),
+ pkt->ptype, expected_pkt_type,
+ nt_errstr(ret)));
+ NDR_PRINT_DEBUG(ncacn_packet, pkt);
+ return ret;
+ }
+
+ /* Use this for now... */
+ return NT_STATUS_NETWORK_ACCESS_DENIED;
+
+ case DCERPC_PKT_BIND_ACK:
+ ret = dcerpc_verify_ncacn_packet_header(pkt,
+ expected_pkt_type,
+ pkt->u.bind_ack.auth_info.length,
+ DCERPC_PFC_FLAG_FIRST |
+ DCERPC_PFC_FLAG_LAST,
+ DCERPC_PFC_FLAG_CONC_MPX |
+ DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1, (__location__ ": Connection to %s got an unexpected "
+ "RPC packet type - %u, expected %u: %s\n",
+ rpccli_pipe_txt(talloc_tos(), cli),
+ pkt->ptype, expected_pkt_type,
+ nt_errstr(ret)));
+ NDR_PRINT_DEBUG(ncacn_packet, pkt);
+ return ret;
+ }
+
+ break;
+
+ case DCERPC_PKT_ALTER_RESP:
+ ret = dcerpc_verify_ncacn_packet_header(pkt,
+ expected_pkt_type,
+ pkt->u.alter_resp.auth_info.length,
+ DCERPC_PFC_FLAG_FIRST |
+ DCERPC_PFC_FLAG_LAST,
+ DCERPC_PFC_FLAG_CONC_MPX |
+ DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1, (__location__ ": Connection to %s got an unexpected "
+ "RPC packet type - %u, expected %u: %s\n",
+ rpccli_pipe_txt(talloc_tos(), cli),
+ pkt->ptype, expected_pkt_type,
+ nt_errstr(ret)));
+ NDR_PRINT_DEBUG(ncacn_packet, pkt);
+ return ret;
+ }
+
+ break;
+
+ case DCERPC_PKT_RESPONSE:
+
+ r = &pkt->u.response;
+
+ ret = dcerpc_verify_ncacn_packet_header(pkt,
+ expected_pkt_type,
+ r->stub_and_verifier.length,
+ 0, /* required_flags */
+ DCERPC_PFC_FLAG_FIRST |
+ DCERPC_PFC_FLAG_LAST);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1, (__location__ ": Connection to %s got an unexpected "
+ "RPC packet type - %u, expected %u: %s\n",
+ rpccli_pipe_txt(talloc_tos(), cli),
+ pkt->ptype, expected_pkt_type,
+ nt_errstr(ret)));
+ NDR_PRINT_DEBUG(ncacn_packet, pkt);
+ return ret;
+ }
+
+ tmp_stub.data = r->stub_and_verifier.data;
+ tmp_stub.length = r->stub_and_verifier.length;
+
+ /* Here's where we deal with incoming sign/seal. */
+ ret = dcerpc_check_auth(cli->auth, pkt,
+ &tmp_stub,
+ DCERPC_RESPONSE_LENGTH,
+ pdu);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1, (__location__ ": Connection to %s got an unexpected "
+ "RPC packet type - %u, expected %u: %s\n",
+ rpccli_pipe_txt(talloc_tos(), cli),
+ pkt->ptype, expected_pkt_type,
+ nt_errstr(ret)));
+ NDR_PRINT_DEBUG(ncacn_packet, pkt);
+ return ret;
+ }
+
+ /* Point the return values at the NDR data. */
+ *rdata = tmp_stub;
+
+ DEBUG(10, ("Got pdu len %lu, data_len %lu\n",
+ (long unsigned int)pdu->length,
+ (long unsigned int)rdata->length));
+
+ /*
+ * If this is the first reply, and the allocation hint is
+ * reasonable, try and set up the reply_pdu DATA_BLOB to the
+ * correct size.
+ */
+
+ if ((reply_pdu->length == 0) &&
+ r->alloc_hint && (r->alloc_hint < 15*1024*1024)) {
+ if (!data_blob_realloc(mem_ctx, reply_pdu,
+ r->alloc_hint)) {
+ DEBUG(0, ("reply alloc hint %d too "
+ "large to allocate\n",
+ (int)r->alloc_hint));
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ break;
+
+ case DCERPC_PKT_FAULT:
+
+ ret = dcerpc_verify_ncacn_packet_header(pkt,
+ DCERPC_PKT_FAULT,
+ 0, /* max_auth_info */
+ DCERPC_PFC_FLAG_FIRST |
+ DCERPC_PFC_FLAG_LAST,
+ DCERPC_PFC_FLAG_DID_NOT_EXECUTE);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1, (__location__ ": Connection to %s got an unexpected "
+ "RPC packet type - %u, expected %u: %s\n",
+ rpccli_pipe_txt(talloc_tos(), cli),
+ pkt->ptype, expected_pkt_type,
+ nt_errstr(ret)));
+ NDR_PRINT_DEBUG(ncacn_packet, pkt);
+ return ret;
+ }
+
+ DEBUG(1, (__location__ ": RPC fault code %s received "
+ "from %s!\n",
+ dcerpc_errstr(talloc_tos(),
+ pkt->u.fault.status),
+ rpccli_pipe_txt(talloc_tos(), cli)));
+
+ return dcerpc_fault_to_nt_status(pkt->u.fault.status);
+
+ default:
+ DEBUG(0, (__location__ "Unknown packet type %u received "
+ "from %s!\n",
+ (unsigned int)pkt->ptype,
+ rpccli_pipe_txt(talloc_tos(), cli)));
+ return NT_STATUS_RPC_PROTOCOL_ERROR;
+ }
+
+
+ if (pkt->call_id != call_id) {
+ DEBUG(3, (__location__ ": Connection to %s got an unexpected "
+ "RPC call_id - %u, not %u\n",
+ rpccli_pipe_txt(talloc_tos(), cli),
+ pkt->call_id, call_id));
+ return NT_STATUS_RPC_PROTOCOL_ERROR;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Call a remote api on an arbitrary pipe. takes param, data and setup buffers.
+****************************************************************************/
+
+struct cli_api_pipe_state {
+ struct tevent_context *ev;
+ struct rpc_cli_transport *transport;
+ uint8_t *rdata;
+ uint32_t rdata_len;
+};
+
+static void cli_api_pipe_trans_done(struct tevent_req *subreq);
+static void cli_api_pipe_write_done(struct tevent_req *subreq);
+static void cli_api_pipe_read_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_api_pipe_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct rpc_cli_transport *transport,
+ uint8_t *data, size_t data_len,
+ uint32_t max_rdata_len)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_api_pipe_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_api_pipe_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->transport = transport;
+
+ if (max_rdata_len < RPC_HEADER_LEN) {
+ /*
+ * For a RPC reply we always need at least RPC_HEADER_LEN
+ * bytes. We check this here because we will receive
+ * RPC_HEADER_LEN bytes in cli_trans_sock_send_done.
+ */
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ if (transport->trans_send != NULL) {
+ subreq = transport->trans_send(state, ev, data, data_len,
+ max_rdata_len, transport->priv);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_api_pipe_trans_done, req);
+ return req;
+ }
+
+ /*
+ * If the transport does not provide a "trans" routine, i.e. for
+ * example the ncacn_ip_tcp transport, do the write/read step here.
+ */
+
+ subreq = rpc_write_send(state, ev, transport, data, data_len);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_api_pipe_write_done, req);
+ return req;
+}
+
+static void cli_api_pipe_trans_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_api_pipe_state *state = tevent_req_data(
+ req, struct cli_api_pipe_state);
+ NTSTATUS status;
+
+ status = state->transport->trans_recv(subreq, state, &state->rdata,
+ &state->rdata_len);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static void cli_api_pipe_write_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_api_pipe_state *state = tevent_req_data(
+ req, struct cli_api_pipe_state);
+ NTSTATUS status;
+
+ status = rpc_write_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->rdata = talloc_array(state, uint8_t, RPC_HEADER_LEN);
+ if (tevent_req_nomem(state->rdata, req)) {
+ return;
+ }
+
+ /*
+ * We don't need to use rpc_read_send here, the upper layer will cope
+ * with a short read, transport->trans_send could also return less
+ * than state->max_rdata_len.
+ */
+ subreq = state->transport->read_send(state, state->ev, state->rdata,
+ RPC_HEADER_LEN,
+ state->transport->priv);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_api_pipe_read_done, req);
+}
+
+static void cli_api_pipe_read_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_api_pipe_state *state = tevent_req_data(
+ req, struct cli_api_pipe_state);
+ NTSTATUS status;
+ ssize_t received;
+
+ status = state->transport->read_recv(subreq, &received);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ state->rdata_len = received;
+ tevent_req_done(req);
+}
+
+static NTSTATUS cli_api_pipe_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint8_t **prdata, uint32_t *prdata_len)
+{
+ struct cli_api_pipe_state *state = tevent_req_data(
+ req, struct cli_api_pipe_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ *prdata = talloc_move(mem_ctx, &state->rdata);
+ *prdata_len = state->rdata_len;
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Send data on an rpc pipe via trans. The data must be the last
+ pdu fragment of an NDR data stream.
+
+ Receive response data from an rpc pipe, which may be large...
+
+ Read the first fragment: unfortunately have to use SMBtrans for the first
+ bit, then SMBreadX for subsequent bits.
+
+ If first fragment received also wasn't the last fragment, continue
+ getting fragments until we _do_ receive the last fragment.
+
+ Request/Response PDU's look like the following...
+
+ |<------------------PDU len----------------------------------------------->|
+ |<-HDR_LEN-->|<--REQ LEN------>|.............|<-AUTH_HDRLEN->|<-AUTH_LEN-->|
+
+ +------------+-----------------+-------------+---------------+-------------+
+ | RPC HEADER | REQ/RESP HEADER | DATA ...... | AUTH_HDR | AUTH DATA |
+ +------------+-----------------+-------------+---------------+-------------+
+
+ Where the presence of the AUTH_HDR and AUTH DATA are dependent on the
+ signing & sealing being negotiated.
+
+ ****************************************************************************/
+
+struct rpc_api_pipe_state {
+ struct tevent_context *ev;
+ struct rpc_pipe_client *cli;
+ uint8_t expected_pkt_type;
+ uint32_t call_id;
+
+ DATA_BLOB incoming_frag;
+ struct ncacn_packet *pkt;
+
+ /* Incoming reply */
+ DATA_BLOB reply_pdu;
+ size_t reply_pdu_offset;
+ uint8_t endianness;
+};
+
+static void rpc_api_pipe_trans_done(struct tevent_req *subreq);
+static void rpc_api_pipe_got_pdu(struct tevent_req *subreq);
+static void rpc_api_pipe_auth3_done(struct tevent_req *subreq);
+
+static struct tevent_req *rpc_api_pipe_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct rpc_pipe_client *cli,
+ DATA_BLOB *data, /* Outgoing PDU */
+ uint8_t expected_pkt_type,
+ uint32_t call_id)
+{
+ struct tevent_req *req, *subreq;
+ struct rpc_api_pipe_state *state;
+ uint16_t max_recv_frag;
+
+ req = tevent_req_create(mem_ctx, &state, struct rpc_api_pipe_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->expected_pkt_type = expected_pkt_type;
+ state->call_id = call_id;
+ state->endianness = DCERPC_DREP_LE;
+
+ /*
+ * Ensure we're not sending too much.
+ */
+ if (data->length > cli->max_xmit_frag) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ DEBUG(5,("rpc_api_pipe: %s\n", rpccli_pipe_txt(talloc_tos(), cli)));
+
+ if (state->expected_pkt_type == DCERPC_PKT_AUTH3) {
+ subreq = rpc_write_send(state, ev, cli->transport,
+ data->data, data->length);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, rpc_api_pipe_auth3_done, req);
+ return req;
+ }
+
+ /* get the header first, then fetch the rest once we have
+ * the frag_length available */
+ max_recv_frag = RPC_HEADER_LEN;
+
+ subreq = cli_api_pipe_send(state, ev, cli->transport,
+ data->data, data->length, max_recv_frag);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, rpc_api_pipe_trans_done, req);
+ return req;
+}
+
+static void rpc_api_pipe_auth3_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = rpc_write_recv(subreq);
+ return tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static void rpc_api_pipe_trans_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct rpc_api_pipe_state *state = tevent_req_data(
+ req, struct rpc_api_pipe_state);
+ NTSTATUS status;
+ uint8_t *rdata = NULL;
+ uint32_t rdata_len = 0;
+
+ status = cli_api_pipe_recv(subreq, state, &rdata, &rdata_len);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {;
+ DEBUG(5, ("cli_api_pipe failed: %s\n", nt_errstr(status)));
+ return;
+ }
+
+ if (rdata == NULL) {
+ DEBUG(3,("rpc_api_pipe: %s failed to return data.\n",
+ rpccli_pipe_txt(talloc_tos(), state->cli)));
+ tevent_req_done(req);
+ return;
+ }
+
+ /*
+ * Move data on state->incoming_frag.
+ */
+ state->incoming_frag.data = talloc_move(state, &rdata);
+ state->incoming_frag.length = rdata_len;
+ if (!state->incoming_frag.data) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ /* Ensure we have enough data for a pdu. */
+ subreq = get_complete_frag_send(state, state->ev, state->cli,
+ &state->incoming_frag);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, rpc_api_pipe_got_pdu, req);
+}
+
+static void rpc_api_pipe_got_pdu(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct rpc_api_pipe_state *state = tevent_req_data(
+ req, struct rpc_api_pipe_state);
+ NTSTATUS status;
+ DATA_BLOB rdata = { .data = NULL };
+
+ status = get_complete_frag_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ DEBUG(5, ("get_complete_frag failed: %s\n",
+ nt_errstr(status)));
+ return;
+ }
+
+ state->pkt = talloc(state, struct ncacn_packet);
+ if (!state->pkt) {
+ /*
+ * TODO: do a real async disconnect ...
+ *
+ * For now do it sync...
+ */
+ TALLOC_FREE(state->cli->transport);
+ tevent_req_oom(req);
+ return;
+ }
+
+ status = dcerpc_pull_ncacn_packet(state->pkt,
+ &state->incoming_frag,
+ state->pkt);
+ if (tevent_req_nterror(req, status)) {
+ /*
+ * TODO: do a real async disconnect ...
+ *
+ * For now do it sync...
+ */
+ TALLOC_FREE(state->cli->transport);
+ return;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(ncacn_packet, state->pkt);
+ }
+
+ status = cli_pipe_validate_current_pdu(state,
+ state->cli, state->pkt,
+ &state->incoming_frag,
+ state->expected_pkt_type,
+ state->call_id,
+ &rdata,
+ &state->reply_pdu);
+
+ DBG_DEBUG("got frag len of %zu at offset %zu: %s\n",
+ state->incoming_frag.length,
+ state->reply_pdu_offset,
+ nt_errstr(status));
+
+ if (state->pkt->ptype != DCERPC_PKT_FAULT && !NT_STATUS_IS_OK(status)) {
+ /*
+ * TODO: do a real async disconnect ...
+ *
+ * For now do it sync...
+ */
+ TALLOC_FREE(state->cli->transport);
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTOCOL_ERROR)) {
+ /*
+ * TODO: do a real async disconnect ...
+ *
+ * For now do it sync...
+ */
+ TALLOC_FREE(state->cli->transport);
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR)) {
+ /*
+ * TODO: do a real async disconnect ...
+ *
+ * For now do it sync...
+ */
+ TALLOC_FREE(state->cli->transport);
+ }
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if ((state->pkt->pfc_flags & DCERPC_PFC_FLAG_FIRST)
+ && (state->pkt->drep[0] != DCERPC_DREP_LE)) {
+ /*
+ * Set the data type correctly for big-endian data on the
+ * first packet.
+ */
+ DEBUG(10,("rpc_api_pipe: On %s PDU data format is "
+ "big-endian.\n",
+ rpccli_pipe_txt(talloc_tos(), state->cli)));
+ state->endianness = 0x00; /* BIG ENDIAN */
+ }
+ /*
+ * Check endianness on subsequent packets.
+ */
+ if (state->endianness != state->pkt->drep[0]) {
+ DEBUG(0,("rpc_api_pipe: Error : Endianness changed from %s to "
+ "%s\n",
+ state->endianness?"little":"big",
+ state->pkt->drep[0]?"little":"big"));
+ /*
+ * TODO: do a real async disconnect ...
+ *
+ * For now do it sync...
+ */
+ TALLOC_FREE(state->cli->transport);
+ tevent_req_nterror(req, NT_STATUS_RPC_PROTOCOL_ERROR);
+ return;
+ }
+
+ if (state->reply_pdu_offset + rdata.length > MAX_RPC_DATA_SIZE) {
+ /*
+ * TODO: do a real async disconnect ...
+ *
+ * For now do it sync...
+ */
+ TALLOC_FREE(state->cli->transport);
+ tevent_req_nterror(req, NT_STATUS_RPC_PROTOCOL_ERROR);
+ return;
+ }
+
+ /* Now copy the data portion out of the pdu into rbuf. */
+ if (state->reply_pdu.length < state->reply_pdu_offset + rdata.length) {
+ if (!data_blob_realloc(NULL, &state->reply_pdu,
+ state->reply_pdu_offset + rdata.length)) {
+ /*
+ * TODO: do a real async disconnect ...
+ *
+ * For now do it sync...
+ */
+ TALLOC_FREE(state->cli->transport);
+ tevent_req_oom(req);
+ return;
+ }
+ }
+
+ memcpy(state->reply_pdu.data + state->reply_pdu_offset,
+ rdata.data, rdata.length);
+ state->reply_pdu_offset += rdata.length;
+
+ /* reset state->incoming_frag, there is no need to free it,
+ * it will be reallocated to the right size the next time
+ * it is used */
+ state->incoming_frag.length = 0;
+
+ if (state->pkt->pfc_flags & DCERPC_PFC_FLAG_LAST) {
+ /* make sure the pdu length is right now that we
+ * have all the data available (alloc hint may
+ * have allocated more than was actually used) */
+ state->reply_pdu.length = state->reply_pdu_offset;
+ DEBUG(10,("rpc_api_pipe: %s returned %u bytes.\n",
+ rpccli_pipe_txt(talloc_tos(), state->cli),
+ (unsigned)state->reply_pdu.length));
+ tevent_req_done(req);
+ return;
+ }
+
+ subreq = get_complete_frag_send(state, state->ev, state->cli,
+ &state->incoming_frag);
+ if (subreq == NULL) {
+ /*
+ * TODO: do a real async disconnect ...
+ *
+ * For now do it sync...
+ */
+ TALLOC_FREE(state->cli->transport);
+ }
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, rpc_api_pipe_got_pdu, req);
+}
+
+static NTSTATUS rpc_api_pipe_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct ncacn_packet **pkt,
+ DATA_BLOB *reply_pdu)
+{
+ struct rpc_api_pipe_state *state = tevent_req_data(
+ req, struct rpc_api_pipe_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ /* return data to caller and assign it ownership of memory */
+ if (reply_pdu) {
+ reply_pdu->data = talloc_move(mem_ctx, &state->reply_pdu.data);
+ reply_pdu->length = state->reply_pdu.length;
+ state->reply_pdu.length = 0;
+ } else {
+ data_blob_free(&state->reply_pdu);
+ }
+
+ if (pkt) {
+ *pkt = talloc_steal(mem_ctx, state->pkt);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Creates NTLMSSP auth bind.
+ ********************************************************************/
+
+static NTSTATUS create_generic_auth_rpc_bind_req(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *auth_token,
+ bool *client_hdr_signing)
+{
+ struct gensec_security *gensec_security;
+ DATA_BLOB null_blob = { .data = NULL };
+ NTSTATUS status;
+
+ gensec_security = cli->auth->auth_ctx;
+
+ DEBUG(5, ("create_generic_auth_rpc_bind_req: generate first token\n"));
+ status = gensec_update(gensec_security, mem_ctx, null_blob, auth_token);
+
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED))
+ {
+ return status;
+ }
+
+ if (client_hdr_signing == NULL) {
+ return status;
+ }
+
+ if (cli->auth->auth_level < DCERPC_AUTH_LEVEL_PACKET) {
+ *client_hdr_signing = false;
+ return status;
+ }
+
+ *client_hdr_signing = gensec_have_feature(gensec_security,
+ GENSEC_FEATURE_SIGN_PKT_HEADER);
+
+ return status;
+}
+
+/*******************************************************************
+ Creates the internals of a DCE/RPC bind request or alter context PDU.
+ ********************************************************************/
+
+static NTSTATUS create_bind_or_alt_ctx_internal(TALLOC_CTX *mem_ctx,
+ enum dcerpc_pkt_type ptype,
+ uint32_t rpc_call_id,
+ const struct ndr_syntax_id *abstract,
+ const struct ndr_syntax_id *transfer,
+ const DATA_BLOB *auth_info,
+ bool client_hdr_signing,
+ DATA_BLOB *blob)
+{
+ uint16_t auth_len = auth_info->length;
+ NTSTATUS status;
+ struct dcerpc_ctx_list ctx_list = {
+ .context_id = 0,
+ .num_transfer_syntaxes = 1,
+ .abstract_syntax = *abstract,
+ .transfer_syntaxes = (struct ndr_syntax_id *)discard_const(transfer),
+ };
+ union dcerpc_payload u = {
+ .bind.max_xmit_frag = RPC_MAX_PDU_FRAG_LEN,
+ .bind.max_recv_frag = RPC_MAX_PDU_FRAG_LEN,
+ .bind.num_contexts = 1,
+ .bind.ctx_list = &ctx_list,
+ .bind.auth_info = *auth_info,
+ };
+ uint8_t pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+
+ if (auth_len) {
+ auth_len -= DCERPC_AUTH_TRAILER_LENGTH;
+ }
+
+ if (client_hdr_signing) {
+ pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
+ }
+
+ status = dcerpc_push_ncacn_packet(mem_ctx,
+ ptype, pfc_flags,
+ auth_len,
+ rpc_call_id,
+ &u,
+ blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to marshall bind/alter ncacn_packet.\n"));
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Creates a DCE/RPC bind request.
+ ********************************************************************/
+
+static NTSTATUS create_rpc_bind_req(TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client *cli,
+ struct pipe_auth_data *auth,
+ uint32_t rpc_call_id,
+ const struct ndr_syntax_id *abstract,
+ const struct ndr_syntax_id *transfer,
+ DATA_BLOB *rpc_out)
+{
+ DATA_BLOB auth_token = { .data = NULL };
+ DATA_BLOB auth_info = { .data = NULL };
+ NTSTATUS ret;
+
+ if (auth->auth_type != DCERPC_AUTH_TYPE_NONE) {
+ ret = create_generic_auth_rpc_bind_req(
+ cli, mem_ctx, &auth_token, &auth->client_hdr_signing);
+
+ if (!NT_STATUS_IS_OK(ret) &&
+ !NT_STATUS_EQUAL(ret, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ return ret;
+ }
+ }
+
+ if (auth_token.length != 0) {
+ ret = dcerpc_push_dcerpc_auth(cli,
+ auth->auth_type,
+ auth->auth_level,
+ 0, /* auth_pad_length */
+ auth->auth_context_id,
+ &auth_token,
+ &auth_info);
+ if (!NT_STATUS_IS_OK(ret)) {
+ return ret;
+ }
+ data_blob_free(&auth_token);
+ }
+
+ ret = create_bind_or_alt_ctx_internal(mem_ctx,
+ DCERPC_PKT_BIND,
+ rpc_call_id,
+ abstract,
+ transfer,
+ &auth_info,
+ auth->client_hdr_signing,
+ rpc_out);
+ data_blob_free(&auth_info);
+
+ return ret;
+}
+
+/*******************************************************************
+ External interface.
+ Does an rpc request on a pipe. Incoming data is NDR encoded in in_data.
+ Reply is NDR encoded in out_data. Splits the data stream into RPC PDU's
+ and deals with signing/sealing details.
+ ********************************************************************/
+
+struct rpc_api_pipe_req_state {
+ struct tevent_context *ev;
+ struct rpc_pipe_client *cli;
+ uint8_t op_num;
+ uint32_t call_id;
+ const DATA_BLOB *req_data;
+ const struct GUID *object_uuid;
+ uint32_t req_data_sent;
+ DATA_BLOB req_trailer;
+ uint32_t req_trailer_sent;
+ bool verify_bitmask1;
+ bool verify_pcontext;
+ DATA_BLOB rpc_out;
+ DATA_BLOB reply_pdu;
+};
+
+static void rpc_api_pipe_req_write_done(struct tevent_req *subreq);
+static void rpc_api_pipe_req_done(struct tevent_req *subreq);
+static NTSTATUS prepare_verification_trailer(struct rpc_api_pipe_req_state *state);
+static NTSTATUS prepare_next_frag(struct rpc_api_pipe_req_state *state,
+ bool *is_last_frag);
+
+static struct tevent_req *rpc_api_pipe_req_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct rpc_pipe_client *cli,
+ uint8_t op_num,
+ const struct GUID *object_uuid,
+ const DATA_BLOB *req_data)
+{
+ struct tevent_req *req, *subreq;
+ struct rpc_api_pipe_req_state *state;
+ NTSTATUS status;
+ bool is_last_frag;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct rpc_api_pipe_req_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->op_num = op_num;
+ state->object_uuid = object_uuid;
+ state->req_data = req_data;
+ state->call_id = get_rpc_call_id();
+
+ if (cli->max_xmit_frag < DCERPC_REQUEST_LENGTH
+ + RPC_MAX_SIGN_SIZE) {
+ /* Server is screwed up ! */
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ status = prepare_verification_trailer(state);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ status = prepare_next_frag(state, &is_last_frag);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (is_last_frag) {
+ subreq = rpc_api_pipe_send(state, ev, state->cli,
+ &state->rpc_out,
+ DCERPC_PKT_RESPONSE,
+ state->call_id);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, rpc_api_pipe_req_done, req);
+ } else {
+ subreq = rpc_write_send(state, ev, cli->transport,
+ state->rpc_out.data,
+ state->rpc_out.length);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, rpc_api_pipe_req_write_done,
+ req);
+ }
+ return req;
+}
+
+static NTSTATUS prepare_verification_trailer(struct rpc_api_pipe_req_state *state)
+{
+ struct pipe_auth_data *a = state->cli->auth;
+ struct dcerpc_sec_verification_trailer *t;
+ struct ndr_push *ndr = NULL;
+ enum ndr_err_code ndr_err;
+ size_t align = 0;
+ size_t pad = 0;
+
+ if (a == NULL) {
+ return NT_STATUS_OK;
+ }
+
+ if (a->auth_level < DCERPC_AUTH_LEVEL_PACKET) {
+ return NT_STATUS_OK;
+ }
+
+ t = talloc_zero(state, struct dcerpc_sec_verification_trailer);
+ if (t == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!a->verified_bitmask1) {
+ t->commands = talloc_realloc(t, t->commands,
+ struct dcerpc_sec_vt,
+ t->count.count + 1);
+ if (t->commands == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ t->commands[t->count.count++] = (struct dcerpc_sec_vt) {
+ .command = DCERPC_SEC_VT_COMMAND_BITMASK1,
+ .u.bitmask1 = (a->client_hdr_signing) ?
+ DCERPC_SEC_VT_CLIENT_SUPPORTS_HEADER_SIGNING :
+ 0,
+ };
+ state->verify_bitmask1 = true;
+ }
+
+ if (!state->cli->verified_pcontext) {
+ t->commands = talloc_realloc(t, t->commands,
+ struct dcerpc_sec_vt,
+ t->count.count + 1);
+ if (t->commands == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ t->commands[t->count.count++] = (struct dcerpc_sec_vt) {
+ .command = DCERPC_SEC_VT_COMMAND_PCONTEXT,
+ .u.pcontext.abstract_syntax =
+ state->cli->abstract_syntax,
+ .u.pcontext.transfer_syntax =
+ state->cli->transfer_syntax,
+ };
+ state->verify_pcontext = true;
+ }
+
+ if (!a->hdr_signing) {
+ t->commands = talloc_realloc(t, t->commands,
+ struct dcerpc_sec_vt,
+ t->count.count + 1);
+ if (t->commands == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ t->commands[t->count.count++] = (struct dcerpc_sec_vt) {
+ .command = DCERPC_SEC_VT_COMMAND_HEADER2,
+ .u.header2.ptype = DCERPC_PKT_REQUEST,
+ .u.header2.drep[0] = DCERPC_DREP_LE,
+ .u.header2.call_id = state->call_id,
+ .u.header2.context_id = 0,
+ .u.header2.opnum = state->op_num,
+ };
+ }
+
+ if (t->count.count == 0) {
+ TALLOC_FREE(t);
+ return NT_STATUS_OK;
+ }
+
+ t->commands[t->count.count - 1].command |= DCERPC_SEC_VT_COMMAND_END;
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(dcerpc_sec_verification_trailer, t);
+ }
+
+ ndr = ndr_push_init_ctx(state);
+ if (ndr == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ndr_err = ndr_push_dcerpc_sec_verification_trailer(ndr,
+ NDR_SCALARS | NDR_BUFFERS,
+ t);
+ TALLOC_FREE(t);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+ state->req_trailer = ndr_push_blob(ndr);
+
+ align = state->req_data->length & 0x3;
+ if (align > 0) {
+ pad = 4 - align;
+ }
+ if (pad > 0) {
+ bool ok;
+ uint8_t *p;
+ const uint8_t zeros[4] = { 0, };
+
+ ok = data_blob_append(ndr, &state->req_trailer, zeros, pad);
+ if (!ok) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* move the padding to the start */
+ p = state->req_trailer.data;
+ memmove(p + pad, p, state->req_trailer.length - pad);
+ memset(p, 0, pad);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS prepare_next_frag(struct rpc_api_pipe_req_state *state,
+ bool *is_last_frag)
+{
+ size_t auth_len;
+ size_t frag_len;
+ uint8_t flags = 0;
+ size_t pad_len;
+ size_t data_left;
+ size_t data_thistime;
+ size_t trailer_left;
+ size_t trailer_thistime = 0;
+ size_t total_left;
+ size_t total_thistime;
+ NTSTATUS status;
+ bool ok;
+ union dcerpc_payload u;
+
+ data_left = state->req_data->length - state->req_data_sent;
+ trailer_left = state->req_trailer.length - state->req_trailer_sent;
+ total_left = data_left + trailer_left;
+ if ((total_left < data_left) || (total_left < trailer_left)) {
+ /*
+ * overflow
+ */
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ status = dcerpc_guess_sizes(state->cli->auth,
+ DCERPC_REQUEST_LENGTH, total_left,
+ state->cli->max_xmit_frag,
+ &total_thistime,
+ &frag_len, &auth_len, &pad_len);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (state->req_data_sent == 0) {
+ flags = DCERPC_PFC_FLAG_FIRST;
+ }
+
+ if (total_thistime == total_left) {
+ flags |= DCERPC_PFC_FLAG_LAST;
+ }
+
+ data_thistime = MIN(total_thistime, data_left);
+ if (data_thistime < total_thistime) {
+ trailer_thistime = total_thistime - data_thistime;
+ }
+
+ data_blob_free(&state->rpc_out);
+
+ u = (union dcerpc_payload) {
+ .request.alloc_hint = total_left,
+ .request.context_id = 0,
+ .request.opnum = state->op_num,
+ };
+
+ if (state->object_uuid) {
+ flags |= DCERPC_PFC_FLAG_OBJECT_UUID;
+ u.request.object.object = *state->object_uuid;
+ frag_len += ndr_size_GUID(state->object_uuid, 0);
+ }
+
+ status = dcerpc_push_ncacn_packet(state,
+ DCERPC_PKT_REQUEST,
+ flags,
+ auth_len,
+ state->call_id,
+ &u,
+ &state->rpc_out);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* explicitly set frag_len here as dcerpc_push_ncacn_packet() can't
+ * compute it right for requests because the auth trailer is missing
+ * at this stage */
+ dcerpc_set_frag_length(&state->rpc_out, frag_len);
+
+ if (data_thistime > 0) {
+ /* Copy in the data. */
+ ok = data_blob_append(NULL, &state->rpc_out,
+ state->req_data->data + state->req_data_sent,
+ data_thistime);
+ if (!ok) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ state->req_data_sent += data_thistime;
+ }
+
+ if (trailer_thistime > 0) {
+ /* Copy in the verification trailer. */
+ ok = data_blob_append(NULL, &state->rpc_out,
+ state->req_trailer.data + state->req_trailer_sent,
+ trailer_thistime);
+ if (!ok) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ state->req_trailer_sent += trailer_thistime;
+ }
+
+ switch (state->cli->auth->auth_level) {
+ case DCERPC_AUTH_LEVEL_NONE:
+ case DCERPC_AUTH_LEVEL_CONNECT:
+ break;
+ case DCERPC_AUTH_LEVEL_PACKET:
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ status = dcerpc_add_auth_footer(state->cli->auth, pad_len,
+ &state->rpc_out);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ *is_last_frag = ((flags & DCERPC_PFC_FLAG_LAST) != 0);
+
+ return status;
+}
+
+static void rpc_api_pipe_req_write_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct rpc_api_pipe_req_state *state = tevent_req_data(
+ req, struct rpc_api_pipe_req_state);
+ NTSTATUS status;
+ bool is_last_frag;
+
+ status = rpc_write_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ status = prepare_next_frag(state, &is_last_frag);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (is_last_frag) {
+ subreq = rpc_api_pipe_send(state, state->ev, state->cli,
+ &state->rpc_out,
+ DCERPC_PKT_RESPONSE,
+ state->call_id);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, rpc_api_pipe_req_done, req);
+ } else {
+ subreq = rpc_write_send(state, state->ev,
+ state->cli->transport,
+ state->rpc_out.data,
+ state->rpc_out.length);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, rpc_api_pipe_req_write_done,
+ req);
+ }
+}
+
+static void rpc_api_pipe_req_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct rpc_api_pipe_req_state *state = tevent_req_data(
+ req, struct rpc_api_pipe_req_state);
+ NTSTATUS status;
+
+ status = rpc_api_pipe_recv(subreq, state, NULL, &state->reply_pdu);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (state->cli->auth == NULL) {
+ tevent_req_done(req);
+ return;
+ }
+
+ if (state->verify_bitmask1) {
+ state->cli->auth->verified_bitmask1 = true;
+ }
+
+ if (state->verify_pcontext) {
+ state->cli->verified_pcontext = true;
+ }
+
+ tevent_req_done(req);
+}
+
+static NTSTATUS rpc_api_pipe_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ DATA_BLOB *reply_pdu)
+{
+ struct rpc_api_pipe_req_state *state = tevent_req_data(
+ req, struct rpc_api_pipe_req_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ /*
+ * We always have to initialize to reply pdu, even if there is
+ * none. The rpccli_* caller routines expect this.
+ */
+ *reply_pdu = data_blob_null;
+ return status;
+ }
+
+ /* return data to caller and assign it ownership of memory */
+ reply_pdu->data = talloc_move(mem_ctx, &state->reply_pdu.data);
+ reply_pdu->length = state->reply_pdu.length;
+ state->reply_pdu.length = 0;
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Check the rpc bind acknowledge response.
+****************************************************************************/
+
+static bool check_bind_response(const struct dcerpc_bind_ack *r,
+ const struct ndr_syntax_id *transfer)
+{
+ struct dcerpc_ack_ctx ctx;
+ bool equal;
+
+ if (r->secondary_address_size == 0) {
+ DEBUG(4,("Ignoring length check -- ASU bug (server didn't fill in the pipe name correctly)\n"));
+ }
+
+ if (r->num_results < 1 || !r->ctx_list) {
+ return false;
+ }
+
+ ctx = r->ctx_list[0];
+
+ /* check the transfer syntax */
+ equal = ndr_syntax_id_equal(&ctx.syntax, transfer);
+ if (!equal) {
+ DEBUG(2,("bind_rpc_pipe: transfer syntax differs\n"));
+ return False;
+ }
+
+ if (r->num_results != 0x1 || ctx.result != 0) {
+ DEBUG(2,("bind_rpc_pipe: bind denied results: %d reason: %x\n",
+ r->num_results, ctx.reason.value));
+ }
+
+ DEBUG(5,("check_bind_response: accepted!\n"));
+ return True;
+}
+
+/*******************************************************************
+ Creates a DCE/RPC bind authentication response.
+ This is the packet that is sent back to the server once we
+ have received a BIND-ACK, to finish the third leg of
+ the authentication handshake.
+ ********************************************************************/
+
+static NTSTATUS create_rpc_bind_auth3(TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client *cli,
+ struct pipe_auth_data *auth,
+ uint32_t rpc_call_id,
+ DATA_BLOB *pauth_blob,
+ DATA_BLOB *rpc_out)
+{
+ NTSTATUS status;
+ union dcerpc_payload u = { .auth3._pad = 0, };
+
+ status = dcerpc_push_dcerpc_auth(mem_ctx,
+ auth->auth_type,
+ auth->auth_level,
+ 0, /* auth_pad_length */
+ auth->auth_context_id,
+ pauth_blob,
+ &u.auth3.auth_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = dcerpc_push_ncacn_packet(mem_ctx,
+ DCERPC_PKT_AUTH3,
+ DCERPC_PFC_FLAG_FIRST |
+ DCERPC_PFC_FLAG_LAST,
+ pauth_blob->length,
+ rpc_call_id,
+ &u,
+ rpc_out);
+ data_blob_free(&u.auth3.auth_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("create_bind_or_alt_ctx_internal: failed to marshall RPC_HDR_RB.\n"));
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Creates a DCE/RPC bind alter context authentication request which
+ may contain a spnego auth blob
+ ********************************************************************/
+
+static NTSTATUS create_rpc_alter_context(TALLOC_CTX *mem_ctx,
+ struct pipe_auth_data *auth,
+ uint32_t rpc_call_id,
+ const struct ndr_syntax_id *abstract,
+ const struct ndr_syntax_id *transfer,
+ const DATA_BLOB *pauth_blob, /* spnego auth blob already created. */
+ DATA_BLOB *rpc_out)
+{
+ DATA_BLOB auth_info;
+ NTSTATUS status;
+
+ status = dcerpc_push_dcerpc_auth(mem_ctx,
+ auth->auth_type,
+ auth->auth_level,
+ 0, /* auth_pad_length */
+ auth->auth_context_id,
+ pauth_blob,
+ &auth_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = create_bind_or_alt_ctx_internal(mem_ctx,
+ DCERPC_PKT_ALTER,
+ rpc_call_id,
+ abstract,
+ transfer,
+ &auth_info,
+ false, /* client_hdr_signing */
+ rpc_out);
+ data_blob_free(&auth_info);
+ return status;
+}
+
+/****************************************************************************
+ Do an rpc bind.
+****************************************************************************/
+
+struct rpc_pipe_bind_state {
+ struct tevent_context *ev;
+ struct rpc_pipe_client *cli;
+ DATA_BLOB rpc_out;
+ bool auth3;
+ uint32_t rpc_call_id;
+};
+
+static void rpc_pipe_bind_step_one_done(struct tevent_req *subreq);
+static NTSTATUS rpc_bind_next_send(struct tevent_req *req,
+ struct rpc_pipe_bind_state *state,
+ DATA_BLOB *credentials);
+static NTSTATUS rpc_bind_finish_send(struct tevent_req *req,
+ struct rpc_pipe_bind_state *state,
+ DATA_BLOB *credentials);
+
+struct tevent_req *rpc_pipe_bind_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct rpc_pipe_client *cli,
+ struct pipe_auth_data *auth)
+{
+ struct tevent_req *req, *subreq;
+ struct rpc_pipe_bind_state *state;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state, struct rpc_pipe_bind_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ DEBUG(5,("Bind RPC Pipe: %s auth_type %u, auth_level %u\n",
+ rpccli_pipe_txt(talloc_tos(), cli),
+ (unsigned int)auth->auth_type,
+ (unsigned int)auth->auth_level ));
+
+ state->ev = ev;
+ state->cli = cli;
+ state->rpc_call_id = get_rpc_call_id();
+
+ cli->auth = talloc_move(cli, &auth);
+
+ /* Marshall the outgoing data. */
+ status = create_rpc_bind_req(state, cli,
+ cli->auth,
+ state->rpc_call_id,
+ &cli->abstract_syntax,
+ &cli->transfer_syntax,
+ &state->rpc_out);
+
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = rpc_api_pipe_send(state, ev, cli, &state->rpc_out,
+ DCERPC_PKT_BIND_ACK, state->rpc_call_id);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, rpc_pipe_bind_step_one_done, req);
+ return req;
+}
+
+static void rpc_pipe_bind_step_one_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct rpc_pipe_bind_state *state = tevent_req_data(
+ req, struct rpc_pipe_bind_state);
+ struct pipe_auth_data *pauth = state->cli->auth;
+ struct gensec_security *gensec_security;
+ struct ncacn_packet *pkt = NULL;
+ struct dcerpc_auth auth;
+ DATA_BLOB auth_token = { .data = NULL };
+ NTSTATUS status;
+
+ status = rpc_api_pipe_recv(subreq, talloc_tos(), &pkt, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ DEBUG(3, ("rpc_pipe_bind: %s bind request returned %s\n",
+ rpccli_pipe_txt(talloc_tos(), state->cli),
+ nt_errstr(status)));
+ return;
+ }
+
+ if (state->auth3) {
+ tevent_req_done(req);
+ return;
+ }
+
+ if (!check_bind_response(&pkt->u.bind_ack, &state->cli->transfer_syntax)) {
+ DEBUG(2, ("rpc_pipe_bind: check_bind_response failed.\n"));
+ tevent_req_nterror(req, NT_STATUS_BUFFER_TOO_SMALL);
+ return;
+ }
+
+ if (pkt->ptype == DCERPC_PKT_BIND_ACK) {
+ if (pkt->pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN) {
+ if (pauth->client_hdr_signing) {
+ pauth->hdr_signing = true;
+ }
+ }
+ }
+
+ state->cli->max_xmit_frag = pkt->u.bind_ack.max_xmit_frag;
+
+ if (pauth->auth_type == DCERPC_AUTH_TYPE_NONE) {
+ /* Bind complete. */
+ tevent_req_done(req);
+ return;
+ }
+
+ if (pkt->auth_length == 0) {
+ tevent_req_nterror(req, NT_STATUS_RPC_PROTOCOL_ERROR);
+ return;
+ }
+
+ /* get auth credentials */
+ status = dcerpc_pull_auth_trailer(pkt, talloc_tos(),
+ &pkt->u.bind_ack.auth_info,
+ &auth, NULL, true);
+ if (tevent_req_nterror(req, status)) {
+ DEBUG(0, ("Failed to pull dcerpc auth: %s.\n",
+ nt_errstr(status)));
+ return;
+ }
+
+ if (auth.auth_type != pauth->auth_type) {
+ DBG_ERR("Auth type %u mismatch expected %u.\n",
+ auth.auth_type, pauth->auth_type);
+ tevent_req_nterror(req, NT_STATUS_RPC_PROTOCOL_ERROR);
+ return;
+ }
+
+ if (auth.auth_level != pauth->auth_level) {
+ DBG_ERR("Auth level %u mismatch expected %u.\n",
+ auth.auth_level, pauth->auth_level);
+ tevent_req_nterror(req, NT_STATUS_RPC_PROTOCOL_ERROR);
+ return;
+ }
+
+ if (auth.auth_context_id != pauth->auth_context_id) {
+ DBG_ERR("Auth context id %"PRIu32" mismatch "
+ "expected %"PRIu32".\n",
+ auth.auth_context_id,
+ pauth->auth_context_id);
+ tevent_req_nterror(req, NT_STATUS_RPC_PROTOCOL_ERROR);
+ return;
+ }
+
+ /*
+ * For authenticated binds we may need to do 3 or 4 leg binds.
+ */
+
+ if (pauth->auth_type == DCERPC_AUTH_TYPE_NONE) {
+ /* Bind complete. */
+ tevent_req_done(req);
+ return;
+ }
+
+ gensec_security = pauth->auth_ctx;
+
+ status = gensec_update(gensec_security, state,
+ auth.credentials, &auth_token);
+ if (NT_STATUS_EQUAL(status,
+ NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ status = rpc_bind_next_send(req, state,
+ &auth_token);
+ } else if (NT_STATUS_IS_OK(status)) {
+ if (pauth->hdr_signing) {
+ gensec_want_feature(gensec_security,
+ GENSEC_FEATURE_SIGN_PKT_HEADER);
+ }
+
+ if (auth_token.length == 0) {
+ /* Bind complete. */
+ tevent_req_done(req);
+ return;
+ }
+ status = rpc_bind_finish_send(req, state,
+ &auth_token);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ }
+ return;
+}
+
+static NTSTATUS rpc_bind_next_send(struct tevent_req *req,
+ struct rpc_pipe_bind_state *state,
+ DATA_BLOB *auth_token)
+{
+ struct pipe_auth_data *auth = state->cli->auth;
+ struct tevent_req *subreq;
+ NTSTATUS status;
+
+ /* Now prepare the alter context pdu. */
+ data_blob_free(&state->rpc_out);
+
+ status = create_rpc_alter_context(state, auth,
+ state->rpc_call_id,
+ &state->cli->abstract_syntax,
+ &state->cli->transfer_syntax,
+ auth_token,
+ &state->rpc_out);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ subreq = rpc_api_pipe_send(state, state->ev, state->cli,
+ &state->rpc_out, DCERPC_PKT_ALTER_RESP,
+ state->rpc_call_id);
+ if (subreq == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ tevent_req_set_callback(subreq, rpc_pipe_bind_step_one_done, req);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS rpc_bind_finish_send(struct tevent_req *req,
+ struct rpc_pipe_bind_state *state,
+ DATA_BLOB *auth_token)
+{
+ struct pipe_auth_data *auth = state->cli->auth;
+ struct tevent_req *subreq;
+ NTSTATUS status;
+
+ state->auth3 = true;
+
+ /* Now prepare the auth3 context pdu. */
+ data_blob_free(&state->rpc_out);
+
+ status = create_rpc_bind_auth3(state, state->cli, auth,
+ state->rpc_call_id,
+ auth_token,
+ &state->rpc_out);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ subreq = rpc_api_pipe_send(state, state->ev, state->cli,
+ &state->rpc_out, DCERPC_PKT_AUTH3,
+ state->rpc_call_id);
+ if (subreq == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ tevent_req_set_callback(subreq, rpc_pipe_bind_step_one_done, req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS rpc_pipe_bind_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS rpc_pipe_bind(struct rpc_pipe_client *cli,
+ struct pipe_auth_data *auth)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_OK;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = rpc_pipe_bind_send(frame, ev, cli, auth);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = rpc_pipe_bind_recv(req);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+#define RPCCLI_DEFAULT_TIMEOUT 10000 /* 10 seconds. */
+
+unsigned int rpccli_set_timeout(struct rpc_pipe_client *rpc_cli,
+ unsigned int timeout)
+{
+ if (rpc_cli == NULL) {
+ return RPCCLI_DEFAULT_TIMEOUT;
+ }
+
+ if (rpc_cli->binding_handle == NULL) {
+ return RPCCLI_DEFAULT_TIMEOUT;
+ }
+
+ return dcerpc_binding_handle_set_timeout(rpc_cli->binding_handle,
+ timeout);
+}
+
+bool rpccli_is_connected(struct rpc_pipe_client *rpc_cli)
+{
+ if (rpc_cli == NULL) {
+ return false;
+ }
+
+ if (rpc_cli->binding_handle == NULL) {
+ return false;
+ }
+
+ return dcerpc_binding_handle_is_connected(rpc_cli->binding_handle);
+}
+
+struct rpccli_bh_state {
+ struct rpc_pipe_client *rpc_cli;
+};
+
+static bool rpccli_bh_is_connected(struct dcerpc_binding_handle *h)
+{
+ struct rpccli_bh_state *hs = dcerpc_binding_handle_data(h,
+ struct rpccli_bh_state);
+ struct rpc_cli_transport *transport = hs->rpc_cli->transport;
+
+ if (transport == NULL) {
+ return false;
+ }
+
+ if (transport->is_connected == NULL) {
+ return false;
+ }
+
+ return transport->is_connected(transport->priv);
+}
+
+static uint32_t rpccli_bh_set_timeout(struct dcerpc_binding_handle *h,
+ uint32_t timeout)
+{
+ struct rpccli_bh_state *hs = dcerpc_binding_handle_data(h,
+ struct rpccli_bh_state);
+ struct rpc_cli_transport *transport = hs->rpc_cli->transport;
+ unsigned int old;
+
+ if (transport == NULL) {
+ return RPCCLI_DEFAULT_TIMEOUT;
+ }
+
+ if (transport->set_timeout == NULL) {
+ return RPCCLI_DEFAULT_TIMEOUT;
+ }
+
+ old = transport->set_timeout(transport->priv, timeout);
+ if (old == 0) {
+ return RPCCLI_DEFAULT_TIMEOUT;
+ }
+
+ return old;
+}
+
+static void rpccli_bh_auth_info(struct dcerpc_binding_handle *h,
+ enum dcerpc_AuthType *auth_type,
+ enum dcerpc_AuthLevel *auth_level)
+{
+ struct rpccli_bh_state *hs = dcerpc_binding_handle_data(h,
+ struct rpccli_bh_state);
+
+ if (hs->rpc_cli == NULL) {
+ return;
+ }
+
+ if (hs->rpc_cli->auth == NULL) {
+ return;
+ }
+
+ *auth_type = hs->rpc_cli->auth->auth_type;
+ *auth_level = hs->rpc_cli->auth->auth_level;
+}
+
+struct rpccli_bh_raw_call_state {
+ DATA_BLOB in_data;
+ DATA_BLOB out_data;
+ uint32_t out_flags;
+};
+
+static void rpccli_bh_raw_call_done(struct tevent_req *subreq);
+
+static struct tevent_req *rpccli_bh_raw_call_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dcerpc_binding_handle *h,
+ const struct GUID *object,
+ uint32_t opnum,
+ uint32_t in_flags,
+ const uint8_t *in_data,
+ size_t in_length)
+{
+ struct rpccli_bh_state *hs = dcerpc_binding_handle_data(h,
+ struct rpccli_bh_state);
+ struct tevent_req *req;
+ struct rpccli_bh_raw_call_state *state;
+ bool ok;
+ struct tevent_req *subreq;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct rpccli_bh_raw_call_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->in_data.data = discard_const_p(uint8_t, in_data);
+ state->in_data.length = in_length;
+
+ ok = rpccli_bh_is_connected(h);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = rpc_api_pipe_req_send(state, ev, hs->rpc_cli,
+ opnum, object, &state->in_data);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, rpccli_bh_raw_call_done, req);
+
+ return req;
+}
+
+static void rpccli_bh_raw_call_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct rpccli_bh_raw_call_state *state =
+ tevent_req_data(req,
+ struct rpccli_bh_raw_call_state);
+ NTSTATUS status;
+
+ state->out_flags = 0;
+
+ /* TODO: support bigendian responses */
+
+ status = rpc_api_pipe_req_recv(subreq, state, &state->out_data);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static NTSTATUS rpccli_bh_raw_call_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **out_data,
+ size_t *out_length,
+ uint32_t *out_flags)
+{
+ struct rpccli_bh_raw_call_state *state =
+ tevent_req_data(req,
+ struct rpccli_bh_raw_call_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *out_data = talloc_move(mem_ctx, &state->out_data.data);
+ *out_length = state->out_data.length;
+ *out_flags = state->out_flags;
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+struct rpccli_bh_disconnect_state {
+ uint8_t _dummy;
+};
+
+static struct tevent_req *rpccli_bh_disconnect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dcerpc_binding_handle *h)
+{
+ struct rpccli_bh_state *hs = dcerpc_binding_handle_data(h,
+ struct rpccli_bh_state);
+ struct tevent_req *req;
+ struct rpccli_bh_disconnect_state *state;
+ bool ok;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct rpccli_bh_disconnect_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ ok = rpccli_bh_is_connected(h);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * TODO: do a real async disconnect ...
+ *
+ * For now we do it sync...
+ */
+ TALLOC_FREE(hs->rpc_cli->transport);
+ hs->rpc_cli = NULL;
+
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+}
+
+static NTSTATUS rpccli_bh_disconnect_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+static bool rpccli_bh_ref_alloc(struct dcerpc_binding_handle *h)
+{
+ return true;
+}
+
+static void rpccli_bh_do_ndr_print(struct dcerpc_binding_handle *h,
+ ndr_flags_type ndr_flags,
+ const void *_struct_ptr,
+ const struct ndr_interface_call *call)
+{
+ void *struct_ptr = discard_const(_struct_ptr);
+
+ if (DEBUGLEVEL < 10) {
+ return;
+ }
+
+ if (ndr_flags & NDR_IN) {
+ ndr_print_function_debug(call->ndr_print,
+ call->name,
+ ndr_flags,
+ struct_ptr);
+ }
+ if (ndr_flags & NDR_OUT) {
+ ndr_print_function_debug(call->ndr_print,
+ call->name,
+ ndr_flags,
+ struct_ptr);
+ }
+}
+
+static const struct dcerpc_binding_handle_ops rpccli_bh_ops = {
+ .name = "rpccli",
+ .is_connected = rpccli_bh_is_connected,
+ .set_timeout = rpccli_bh_set_timeout,
+ .auth_info = rpccli_bh_auth_info,
+ .raw_call_send = rpccli_bh_raw_call_send,
+ .raw_call_recv = rpccli_bh_raw_call_recv,
+ .disconnect_send = rpccli_bh_disconnect_send,
+ .disconnect_recv = rpccli_bh_disconnect_recv,
+
+ .ref_alloc = rpccli_bh_ref_alloc,
+ .do_ndr_print = rpccli_bh_do_ndr_print,
+};
+
+/* initialise a rpc_pipe_client binding handle */
+struct dcerpc_binding_handle *rpccli_bh_create(struct rpc_pipe_client *c,
+ const struct GUID *object,
+ const struct ndr_interface_table *table)
+{
+ struct dcerpc_binding_handle *h;
+ struct rpccli_bh_state *hs;
+
+ h = dcerpc_binding_handle_create(c,
+ &rpccli_bh_ops,
+ object,
+ table,
+ &hs,
+ struct rpccli_bh_state,
+ __location__);
+ if (h == NULL) {
+ return NULL;
+ }
+ hs->rpc_cli = c;
+
+ return h;
+}
+
+NTSTATUS rpccli_anon_bind_data(TALLOC_CTX *mem_ctx,
+ struct pipe_auth_data **presult)
+{
+ struct pipe_auth_data *result;
+ struct auth_generic_state *auth_generic_ctx;
+ NTSTATUS status;
+
+ result = talloc_zero(mem_ctx, struct pipe_auth_data);
+ if (result == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ result->auth_type = DCERPC_AUTH_TYPE_NONE;
+ result->auth_level = DCERPC_AUTH_LEVEL_NONE;
+ result->auth_context_id = 0;
+
+ status = auth_generic_client_prepare(result,
+ &auth_generic_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to create auth_generic context: %s\n",
+ nt_errstr(status)));
+ }
+
+ status = auth_generic_set_username(auth_generic_ctx, "");
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to set username: %s\n",
+ nt_errstr(status)));
+ }
+
+ status = auth_generic_set_domain(auth_generic_ctx, "");
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to set domain: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ status = gensec_set_credentials(auth_generic_ctx->gensec_security,
+ auth_generic_ctx->credentials);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to set GENSEC credentials: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+ talloc_unlink(auth_generic_ctx, auth_generic_ctx->credentials);
+ auth_generic_ctx->credentials = NULL;
+
+ result->auth_ctx = talloc_move(result, &auth_generic_ctx->gensec_security);
+ talloc_free(auth_generic_ctx);
+ *presult = result;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS rpccli_generic_bind_data(TALLOC_CTX *mem_ctx,
+ enum dcerpc_AuthType auth_type,
+ enum dcerpc_AuthLevel auth_level,
+ const char *server,
+ const char *target_service,
+ const char *domain,
+ const char *username,
+ const char *password,
+ enum credentials_use_kerberos use_kerberos,
+ struct netlogon_creds_CredentialState *creds,
+ struct pipe_auth_data **presult)
+{
+ struct auth_generic_state *auth_generic_ctx;
+ struct pipe_auth_data *result;
+ NTSTATUS status;
+
+ result = talloc_zero(mem_ctx, struct pipe_auth_data);
+ if (result == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ result->auth_type = auth_type;
+ result->auth_level = auth_level;
+ result->auth_context_id = 1;
+
+ status = auth_generic_client_prepare(result,
+ &auth_generic_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = auth_generic_set_username(auth_generic_ctx, username);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = auth_generic_set_domain(auth_generic_ctx, domain);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = auth_generic_set_password(auth_generic_ctx, password);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = gensec_set_target_service(auth_generic_ctx->gensec_security, target_service);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = gensec_set_target_hostname(auth_generic_ctx->gensec_security, server);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ cli_credentials_set_kerberos_state(auth_generic_ctx->credentials,
+ use_kerberos,
+ CRED_SPECIFIED);
+ cli_credentials_set_netlogon_creds(auth_generic_ctx->credentials, creds);
+
+ status = auth_generic_client_start_by_authtype(auth_generic_ctx, auth_type, auth_level);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ result->auth_ctx = talloc_move(result, &auth_generic_ctx->gensec_security);
+ talloc_free(auth_generic_ctx);
+ *presult = result;
+ return NT_STATUS_OK;
+
+ fail:
+ TALLOC_FREE(result);
+ return status;
+}
+
+/* This routine steals the creds pointer that is passed in */
+static NTSTATUS rpccli_generic_bind_data_from_creds(TALLOC_CTX *mem_ctx,
+ enum dcerpc_AuthType auth_type,
+ enum dcerpc_AuthLevel auth_level,
+ const char *server,
+ const char *target_service,
+ struct cli_credentials *creds,
+ struct pipe_auth_data **presult)
+{
+ struct auth_generic_state *auth_generic_ctx;
+ struct pipe_auth_data *result;
+ NTSTATUS status;
+
+ result = talloc_zero(mem_ctx, struct pipe_auth_data);
+ if (result == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ result->auth_type = auth_type;
+ result->auth_level = auth_level;
+ result->auth_context_id = 1;
+
+ status = auth_generic_client_prepare(result,
+ &auth_generic_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = auth_generic_set_creds(auth_generic_ctx, creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = gensec_set_target_service(auth_generic_ctx->gensec_security, target_service);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = gensec_set_target_hostname(auth_generic_ctx->gensec_security, server);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = auth_generic_client_start_by_authtype(auth_generic_ctx, auth_type, auth_level);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ result->auth_ctx = talloc_move(result, &auth_generic_ctx->gensec_security);
+ talloc_free(auth_generic_ctx);
+ *presult = result;
+ return NT_STATUS_OK;
+
+ fail:
+ TALLOC_FREE(result);
+ return status;
+}
+
+NTSTATUS rpccli_ncalrpc_bind_data(TALLOC_CTX *mem_ctx,
+ struct pipe_auth_data **presult)
+{
+ return rpccli_generic_bind_data(mem_ctx,
+ DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM,
+ DCERPC_AUTH_LEVEL_CONNECT,
+ NULL, /* server */
+ "host", /* target_service */
+ NAME_NT_AUTHORITY, /* domain */
+ "SYSTEM",
+ NULL, /* password */
+ CRED_USE_KERBEROS_DISABLED,
+ NULL, /* netlogon_creds_CredentialState */
+ presult);
+}
+
+/**
+ * Create an rpc pipe client struct, connecting to a tcp port.
+ */
+static NTSTATUS rpc_pipe_open_tcp_port(TALLOC_CTX *mem_ctx, const char *host,
+ const struct sockaddr_storage *ss_addr,
+ uint16_t port,
+ const struct ndr_interface_table *table,
+ struct rpc_pipe_client **presult)
+{
+ struct rpc_pipe_client *result;
+ struct sockaddr_storage addr;
+ NTSTATUS status;
+ int fd;
+
+ result = talloc_zero(mem_ctx, struct rpc_pipe_client);
+ if (result == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ result->abstract_syntax = table->syntax_id;
+ result->transfer_syntax = ndr_transfer_syntax_ndr;
+
+ result->desthost = talloc_strdup(result, host);
+ if (result->desthost == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ result->srv_name_slash = talloc_asprintf_strupper_m(
+ result, "\\\\%s", result->desthost);
+ if (result->srv_name_slash == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ result->max_xmit_frag = RPC_MAX_PDU_FRAG_LEN;
+
+ if (ss_addr == NULL) {
+ if (!resolve_name(host, &addr, NBT_NAME_SERVER, false)) {
+ status = NT_STATUS_NOT_FOUND;
+ goto fail;
+ }
+ } else {
+ addr = *ss_addr;
+ }
+
+ status = open_socket_out(&addr, port, 60*1000, &fd);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ set_socket_options(fd, lp_socket_options());
+
+ status = rpc_transport_sock_init(result, fd, &result->transport);
+ if (!NT_STATUS_IS_OK(status)) {
+ close(fd);
+ goto fail;
+ }
+
+ result->transport->transport = NCACN_IP_TCP;
+
+ result->binding_handle = rpccli_bh_create(result, NULL, table);
+ if (result->binding_handle == NULL) {
+ TALLOC_FREE(result);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *presult = result;
+ return NT_STATUS_OK;
+
+ fail:
+ TALLOC_FREE(result);
+ return status;
+}
+
+static NTSTATUS rpccli_epm_map_binding(
+ struct dcerpc_binding_handle *epm_connection,
+ struct dcerpc_binding *binding,
+ TALLOC_CTX *mem_ctx,
+ char **pendpoint)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ enum dcerpc_transport_t transport =
+ dcerpc_binding_get_transport(binding);
+ enum dcerpc_transport_t res_transport;
+ struct dcerpc_binding *res_binding = NULL;
+ struct epm_twr_t *map_tower = NULL;
+ struct epm_twr_p_t res_towers = { .twr = NULL };
+ struct policy_handle *entry_handle = NULL;
+ uint32_t num_towers = 0;
+ const uint32_t max_towers = 1;
+ const char *endpoint = NULL;
+ char *tmp = NULL;
+ uint32_t result;
+ NTSTATUS status;
+
+ map_tower = talloc_zero(frame, struct epm_twr_t);
+ if (map_tower == NULL) {
+ goto nomem;
+ }
+
+ status = dcerpc_binding_build_tower(
+ frame, binding, &(map_tower->tower));
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("dcerpc_binding_build_tower failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ res_towers.twr = talloc_array(frame, struct epm_twr_t, max_towers);
+ if (res_towers.twr == NULL) {
+ goto nomem;
+ }
+
+ entry_handle = talloc_zero(frame, struct policy_handle);
+ if (entry_handle == NULL) {
+ goto nomem;
+ }
+
+ status = dcerpc_epm_Map(
+ epm_connection,
+ frame,
+ NULL,
+ map_tower,
+ entry_handle,
+ max_towers,
+ &num_towers,
+ &res_towers,
+ &result);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("dcerpc_epm_Map failed: %s\n", nt_errstr(status));
+ goto done;
+ }
+
+ if (result != EPMAPPER_STATUS_OK) {
+ DBG_DEBUG("dcerpc_epm_Map returned %"PRIu32"\n", result);
+ status = NT_STATUS_NOT_FOUND;
+ goto done;
+ }
+
+ if (num_towers != 1) {
+ DBG_DEBUG("dcerpc_epm_Map returned %"PRIu32" towers\n",
+ num_towers);
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto done;
+ }
+
+ status = dcerpc_binding_from_tower(
+ frame, &(res_towers.twr->tower), &res_binding);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("dcerpc_binding_from_tower failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ res_transport = dcerpc_binding_get_transport(res_binding);
+ if (res_transport != transport) {
+ DBG_DEBUG("dcerpc_epm_Map returned transport %d, "
+ "expected %d\n",
+ (int)res_transport,
+ (int)transport);
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto done;
+ }
+
+ endpoint = dcerpc_binding_get_string_option(res_binding, "endpoint");
+ if (endpoint == NULL) {
+ DBG_DEBUG("dcerpc_epm_Map returned no endpoint\n");
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto done;
+ }
+
+ tmp = talloc_strdup(mem_ctx, endpoint);
+ if (tmp == NULL) {
+ goto nomem;
+ }
+ *pendpoint = tmp;
+
+ status = NT_STATUS_OK;
+ goto done;
+
+nomem:
+ status = NT_STATUS_NO_MEMORY;
+done:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+static NTSTATUS rpccli_epm_map_interface(
+ struct dcerpc_binding_handle *epm_connection,
+ enum dcerpc_transport_t transport,
+ const struct ndr_syntax_id *iface,
+ TALLOC_CTX *mem_ctx,
+ char **pendpoint)
+{
+ struct dcerpc_binding *binding = NULL;
+ char *endpoint = NULL;
+ NTSTATUS status;
+
+ status = dcerpc_parse_binding(mem_ctx, "", &binding);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("dcerpc_parse_binding failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = dcerpc_binding_set_transport(binding, transport);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("dcerpc_binding_set_transport failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = dcerpc_binding_set_abstract_syntax(binding, iface);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("dcerpc_binding_set_abstract_syntax failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = rpccli_epm_map_binding(
+ epm_connection, binding, mem_ctx, &endpoint);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("rpccli_epm_map_binding failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+ *pendpoint = endpoint;
+
+done:
+ TALLOC_FREE(binding);
+ return status;
+}
+
+/**
+ * Determine the tcp port on which a dcerpc interface is listening
+ * for the ncacn_ip_tcp transport via the endpoint mapper of the
+ * target host.
+ */
+static NTSTATUS rpc_pipe_get_tcp_port(const char *host,
+ const struct sockaddr_storage *addr,
+ const struct ndr_interface_table *table,
+ uint16_t *pport)
+{
+ NTSTATUS status;
+ struct rpc_pipe_client *epm_pipe = NULL;
+ struct pipe_auth_data *auth = NULL;
+ char *endpoint = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+
+ if (pport == NULL) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (ndr_syntax_id_equal(&table->syntax_id,
+ &ndr_table_epmapper.syntax_id)) {
+ *pport = 135;
+ status = NT_STATUS_OK;
+ goto done;
+ }
+
+ /* open the connection to the endpoint mapper */
+ status = rpc_pipe_open_tcp_port(tmp_ctx, host, addr, 135,
+ &ndr_table_epmapper,
+ &epm_pipe);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = rpccli_anon_bind_data(tmp_ctx, &auth);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = rpc_pipe_bind(epm_pipe, auth);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = rpccli_epm_map_interface(
+ epm_pipe->binding_handle,
+ NCACN_IP_TCP,
+ &table->syntax_id,
+ tmp_ctx,
+ &endpoint);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("rpccli_epm_map_interface failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ *pport = (uint16_t)atoi(endpoint);
+
+done:
+ TALLOC_FREE(tmp_ctx);
+ return status;
+}
+
+/**
+ * Create a rpc pipe client struct, connecting to a host via tcp.
+ * The port is determined by asking the endpoint mapper on the given
+ * host.
+ */
+static NTSTATUS rpc_pipe_open_tcp(
+ TALLOC_CTX *mem_ctx,
+ const char *host,
+ const struct sockaddr_storage *addr,
+ const struct ndr_interface_table *table,
+ struct rpc_pipe_client **presult)
+{
+ NTSTATUS status;
+ uint16_t port = 0;
+
+ status = rpc_pipe_get_tcp_port(host, addr, table, &port);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return rpc_pipe_open_tcp_port(mem_ctx, host, addr, port,
+ table, presult);
+}
+
+static NTSTATUS rpc_pipe_get_ncalrpc_name(
+ const struct ndr_syntax_id *iface,
+ TALLOC_CTX *mem_ctx,
+ char **psocket_name)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct rpc_pipe_client *epm_pipe = NULL;
+ struct pipe_auth_data *auth = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+ bool is_epm;
+
+ is_epm = ndr_syntax_id_equal(iface, &ndr_table_epmapper.syntax_id);
+ if (is_epm) {
+ char *endpoint = talloc_strdup(mem_ctx, "EPMAPPER");
+ if (endpoint == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ *psocket_name = endpoint;
+ goto done;
+ }
+
+ status = rpc_pipe_open_ncalrpc(
+ frame, &ndr_table_epmapper, &epm_pipe);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("rpc_pipe_open_ncalrpc failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = rpccli_anon_bind_data(epm_pipe, &auth);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("rpccli_anon_bind_data failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = rpc_pipe_bind(epm_pipe, auth);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("rpc_pipe_bind failed: %s\n", nt_errstr(status));
+ goto done;
+ }
+
+ status = rpccli_epm_map_interface(
+ epm_pipe->binding_handle,
+ NCALRPC,
+ iface,
+ mem_ctx,
+ psocket_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("rpccli_epm_map_interface failed: %s\n",
+ nt_errstr(status));
+ }
+
+done:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/********************************************************************
+ Create a rpc pipe client struct, connecting to a unix domain socket
+ ********************************************************************/
+NTSTATUS rpc_pipe_open_ncalrpc(TALLOC_CTX *mem_ctx,
+ const struct ndr_interface_table *table,
+ struct rpc_pipe_client **presult)
+{
+ char *socket_name = NULL;
+ struct rpc_pipe_client *result;
+ struct sockaddr_un addr = { .sun_family = AF_UNIX };
+ socklen_t salen = sizeof(addr);
+ int pathlen;
+ NTSTATUS status;
+ int fd = -1;
+
+ result = talloc_zero(mem_ctx, struct rpc_pipe_client);
+ if (result == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = rpc_pipe_get_ncalrpc_name(
+ &table->syntax_id, result, &socket_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("rpc_pipe_get_ncalrpc_name failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ pathlen = snprintf(
+ addr.sun_path,
+ sizeof(addr.sun_path),
+ "%s/%s",
+ lp_ncalrpc_dir(),
+ socket_name);
+ if ((pathlen < 0) || ((size_t)pathlen >= sizeof(addr.sun_path))) {
+ DBG_DEBUG("socket_path for %s too long\n", socket_name);
+ status = NT_STATUS_NAME_TOO_LONG;
+ goto fail;
+ }
+ TALLOC_FREE(socket_name);
+
+ result->abstract_syntax = table->syntax_id;
+ result->transfer_syntax = ndr_transfer_syntax_ndr;
+
+ result->desthost = get_myname(result);
+ if (result->desthost == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ result->srv_name_slash = talloc_asprintf_strupper_m(
+ result, "\\\\%s", result->desthost);
+ if (result->srv_name_slash == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ result->max_xmit_frag = RPC_MAX_PDU_FRAG_LEN;
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd == -1) {
+ status = map_nt_error_from_unix(errno);
+ goto fail;
+ }
+
+ if (connect(fd, (struct sockaddr *)(void *)&addr, salen) == -1) {
+ DBG_WARNING("connect(%s) failed: %s\n",
+ addr.sun_path,
+ strerror(errno));
+ status = map_nt_error_from_unix(errno);
+ goto fail;
+ }
+
+ status = rpc_transport_sock_init(result, fd, &result->transport);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ fd = -1;
+
+ result->transport->transport = NCALRPC;
+
+ result->binding_handle = rpccli_bh_create(result, NULL, table);
+ if (result->binding_handle == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ *presult = result;
+ return NT_STATUS_OK;
+
+ fail:
+ if (fd != -1) {
+ close(fd);
+ }
+ TALLOC_FREE(result);
+ return status;
+}
+
+NTSTATUS rpc_pipe_open_local_np(
+ TALLOC_CTX *mem_ctx,
+ const struct ndr_interface_table *table,
+ const char *remote_client_name,
+ const struct tsocket_address *remote_client_addr,
+ const char *local_server_name,
+ const struct tsocket_address *local_server_addr,
+ const struct auth_session_info *session_info,
+ struct rpc_pipe_client **presult)
+{
+ struct rpc_pipe_client *result = NULL;
+ struct pipe_auth_data *auth = NULL;
+ const char *pipe_name = NULL;
+ struct tstream_context *npa_stream = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+ int ret;
+
+ result = talloc_zero(mem_ctx, struct rpc_pipe_client);
+ if (result == NULL) {
+ goto fail;
+ }
+ result->abstract_syntax = table->syntax_id;
+ result->transfer_syntax = ndr_transfer_syntax_ndr;
+ result->max_xmit_frag = RPC_MAX_PDU_FRAG_LEN;
+
+ pipe_name = dcerpc_default_transport_endpoint(
+ result, NCACN_NP, table);
+ if (pipe_name == NULL) {
+ DBG_DEBUG("dcerpc_default_transport_endpoint failed\n");
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ goto fail;
+ }
+
+ if (local_server_name == NULL) {
+ result->desthost = get_myname(result);
+ } else {
+ result->desthost = talloc_strdup(result, local_server_name);
+ }
+ if (result->desthost == NULL) {
+ goto fail;
+ }
+ result->srv_name_slash = talloc_asprintf_strupper_m(
+ result, "\\\\%s", result->desthost);
+ if (result->srv_name_slash == NULL) {
+ goto fail;
+ }
+
+ ret = local_np_connect(
+ pipe_name,
+ NCALRPC,
+ remote_client_name,
+ remote_client_addr,
+ local_server_name,
+ local_server_addr,
+ session_info,
+ true,
+ result,
+ &npa_stream);
+ if (ret != 0) {
+ DBG_DEBUG("local_np_connect for %s and "
+ "user %s\\%s failed: %s\n",
+ pipe_name,
+ session_info->info->domain_name,
+ session_info->info->account_name,
+ strerror(ret));
+ status = map_nt_error_from_unix(ret);
+ goto fail;
+ }
+
+ status = rpc_transport_tstream_init(
+ result, &npa_stream, &result->transport);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("rpc_transport_tstream_init failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ result->binding_handle = rpccli_bh_create(result, NULL, table);
+ if (result->binding_handle == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ DBG_DEBUG("Failed to create binding handle.\n");
+ goto fail;
+ }
+
+ status = rpccli_anon_bind_data(result, &auth);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("rpccli_anon_bind_data failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = rpc_pipe_bind(result, auth);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("rpc_pipe_bind failed: %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ *presult = result;
+ return NT_STATUS_OK;
+
+fail:
+ TALLOC_FREE(result);
+ return status;
+}
+
+struct rpc_pipe_client_np_ref {
+ struct cli_state *cli;
+ struct rpc_pipe_client *pipe;
+};
+
+static int rpc_pipe_client_np_ref_destructor(struct rpc_pipe_client_np_ref *np_ref)
+{
+ DLIST_REMOVE(np_ref->cli->pipe_list, np_ref->pipe);
+ return 0;
+}
+
+/****************************************************************************
+ Open a named pipe over SMB to a remote server.
+ *
+ * CAVEAT CALLER OF THIS FUNCTION:
+ * The returned rpc_pipe_client saves a copy of the cli_state cli pointer,
+ * so be sure that this function is called AFTER any structure (vs pointer)
+ * assignment of the cli. In particular, libsmbclient does structure
+ * assignments of cli, which invalidates the data in the returned
+ * rpc_pipe_client if this function is called before the structure assignment
+ * of cli.
+ *
+ ****************************************************************************/
+
+struct rpc_pipe_open_np_state {
+ struct cli_state *cli;
+ const struct ndr_interface_table *table;
+ struct rpc_pipe_client *result;
+};
+
+static void rpc_pipe_open_np_done(struct tevent_req *subreq);
+
+struct tevent_req *rpc_pipe_open_np_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const struct ndr_interface_table *table)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct rpc_pipe_open_np_state *state = NULL;
+ struct rpc_pipe_client *result = NULL;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct rpc_pipe_open_np_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->cli = cli;
+ state->table = table;
+
+ state->result = talloc_zero(state, struct rpc_pipe_client);
+ if (tevent_req_nomem(state->result, req)) {
+ return tevent_req_post(req, ev);
+ }
+ result = state->result;
+
+ result->abstract_syntax = table->syntax_id;
+ result->transfer_syntax = ndr_transfer_syntax_ndr;
+
+ result->desthost = talloc_strdup(
+ result, smbXcli_conn_remote_name(cli->conn));
+ if (tevent_req_nomem(result->desthost, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ result->srv_name_slash = talloc_asprintf_strupper_m(
+ result, "\\\\%s", result->desthost);
+ if (tevent_req_nomem(result->srv_name_slash, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ result->max_xmit_frag = RPC_MAX_PDU_FRAG_LEN;
+
+ subreq = rpc_transport_np_init_send(state, ev, cli, table);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, rpc_pipe_open_np_done, req);
+ return req;
+}
+
+static void rpc_pipe_open_np_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct rpc_pipe_open_np_state *state = tevent_req_data(
+ req, struct rpc_pipe_open_np_state);
+ struct rpc_pipe_client *result = state->result;
+ struct rpc_pipe_client_np_ref *np_ref = NULL;
+ NTSTATUS status;
+
+ status = rpc_transport_np_init_recv(
+ subreq, result, &result->transport);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ result->transport->transport = NCACN_NP;
+
+ np_ref = talloc(result->transport, struct rpc_pipe_client_np_ref);
+ if (tevent_req_nomem(np_ref, req)) {
+ return;
+ }
+ np_ref->cli = state->cli;
+ np_ref->pipe = result;
+
+ DLIST_ADD(np_ref->cli->pipe_list, np_ref->pipe);
+ talloc_set_destructor(np_ref, rpc_pipe_client_np_ref_destructor);
+
+ result->binding_handle = rpccli_bh_create(result, NULL, state->table);
+ if (tevent_req_nomem(result->binding_handle, req)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS rpc_pipe_open_np_recv(
+ struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client **_result)
+{
+ struct rpc_pipe_open_np_state *state = tevent_req_data(
+ req, struct rpc_pipe_open_np_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *_result = talloc_move(mem_ctx, &state->result);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS rpc_pipe_open_np(struct cli_state *cli,
+ const struct ndr_interface_table *table,
+ struct rpc_pipe_client **presult)
+{
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(cli);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = rpc_pipe_open_np_send(ev, ev, cli, table);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = rpc_pipe_open_np_recv(req, NULL, presult);
+fail:
+ TALLOC_FREE(req);
+ TALLOC_FREE(ev);
+ return status;
+}
+
+/****************************************************************************
+ Open a pipe to a remote server.
+ ****************************************************************************/
+
+static NTSTATUS cli_rpc_pipe_open(struct cli_state *cli,
+ enum dcerpc_transport_t transport,
+ const struct ndr_interface_table *table,
+ const char *remote_name,
+ const struct sockaddr_storage *remote_sockaddr,
+ struct rpc_pipe_client **presult)
+{
+ switch (transport) {
+ case NCACN_IP_TCP:
+ return rpc_pipe_open_tcp(NULL,
+ remote_name,
+ remote_sockaddr,
+ table, presult);
+ case NCACN_NP:
+ return rpc_pipe_open_np(cli, table, presult);
+ default:
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+}
+
+/****************************************************************************
+ Open a named pipe to an SMB server and bind anonymously.
+ ****************************************************************************/
+
+NTSTATUS cli_rpc_pipe_open_noauth_transport(struct cli_state *cli,
+ enum dcerpc_transport_t transport,
+ const struct ndr_interface_table *table,
+ const char *remote_name,
+ const struct sockaddr_storage *remote_sockaddr,
+ struct rpc_pipe_client **presult)
+{
+ struct rpc_pipe_client *result;
+ struct pipe_auth_data *auth;
+ NTSTATUS status;
+
+ status = cli_rpc_pipe_open(cli,
+ transport,
+ table,
+ remote_name,
+ remote_sockaddr,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = rpccli_anon_bind_data(result, &auth);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("rpccli_anon_bind_data returned %s\n",
+ nt_errstr(status)));
+ TALLOC_FREE(result);
+ return status;
+ }
+
+ /*
+ * This is a bit of an abstraction violation due to the fact that an
+ * anonymous bind on an authenticated SMB inherits the user/domain
+ * from the enclosing SMB creds
+ */
+
+ if (transport == NCACN_NP) {
+ struct smbXcli_session *session;
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ session = cli->smb2.session;
+ } else {
+ session = cli->smb1.session;
+ }
+
+ status = smbXcli_session_application_key(session, auth,
+ &auth->transport_session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ auth->transport_session_key = data_blob_null;
+ }
+ }
+
+ status = rpc_pipe_bind(result, auth);
+ if (!NT_STATUS_IS_OK(status)) {
+ int lvl = 0;
+ if (ndr_syntax_id_equal(&table->syntax_id,
+ &ndr_table_dssetup.syntax_id)) {
+ /* non AD domains just don't have this pipe, avoid
+ * level 0 statement in that case - gd */
+ lvl = 3;
+ }
+ DEBUG(lvl, ("cli_rpc_pipe_open_noauth: rpc_pipe_bind for pipe "
+ "%s failed with error %s\n",
+ table->name,
+ nt_errstr(status) ));
+ TALLOC_FREE(result);
+ return status;
+ }
+
+ DEBUG(10,("cli_rpc_pipe_open_noauth: opened pipe %s to machine "
+ "%s and bound anonymously.\n",
+ table->name,
+ result->desthost));
+
+ *presult = result;
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ ****************************************************************************/
+
+NTSTATUS cli_rpc_pipe_open_noauth(struct cli_state *cli,
+ const struct ndr_interface_table *table,
+ struct rpc_pipe_client **presult)
+{
+ const char *remote_name = smbXcli_conn_remote_name(cli->conn);
+ const struct sockaddr_storage *remote_sockaddr =
+ smbXcli_conn_remote_sockaddr(cli->conn);
+
+ return cli_rpc_pipe_open_noauth_transport(cli, NCACN_NP,
+ table,
+ remote_name,
+ remote_sockaddr,
+ presult);
+}
+
+/****************************************************************************
+ Open a named pipe to an SMB server and bind using the mech specified
+
+ This routine references the creds pointer that is passed in
+ ****************************************************************************/
+
+NTSTATUS cli_rpc_pipe_open_with_creds(struct cli_state *cli,
+ const struct ndr_interface_table *table,
+ enum dcerpc_transport_t transport,
+ enum dcerpc_AuthType auth_type,
+ enum dcerpc_AuthLevel auth_level,
+ const char *server,
+ const struct sockaddr_storage *remote_sockaddr,
+ struct cli_credentials *creds,
+ struct rpc_pipe_client **presult)
+{
+ struct rpc_pipe_client *result;
+ struct pipe_auth_data *auth = NULL;
+ const char *target_service = table->authservices->names[0];
+ NTSTATUS status;
+
+ status = cli_rpc_pipe_open(cli,
+ transport,
+ table,
+ server,
+ remote_sockaddr,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = rpccli_generic_bind_data_from_creds(result,
+ auth_type, auth_level,
+ server, target_service,
+ creds,
+ &auth);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("rpccli_generic_bind_data_from_creds returned %s\n",
+ nt_errstr(status));
+ goto err;
+ }
+
+ status = rpc_pipe_bind(result, auth);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("cli_rpc_pipe_bind failed with error %s\n",
+ nt_errstr(status));
+ goto err;
+ }
+
+ DBG_DEBUG("opened pipe %s to machine %s and bound as user %s.\n",
+ table->name,
+ result->desthost,
+ cli_credentials_get_unparsed_name(creds, talloc_tos()));
+
+ *presult = result;
+ return NT_STATUS_OK;
+
+ err:
+
+ TALLOC_FREE(result);
+ return status;
+}
+
+NTSTATUS cli_rpc_pipe_open_bind_schannel(
+ struct cli_state *cli,
+ const struct ndr_interface_table *table,
+ enum dcerpc_transport_t transport,
+ struct netlogon_creds_cli_context *netlogon_creds,
+ const char *remote_name,
+ const struct sockaddr_storage *remote_sockaddr,
+ struct rpc_pipe_client **_rpccli)
+{
+ struct rpc_pipe_client *rpccli;
+ struct pipe_auth_data *rpcauth;
+ const char *target_service = table->authservices->names[0];
+ struct cli_credentials *cli_creds;
+ enum dcerpc_AuthLevel auth_level;
+ NTSTATUS status;
+
+ status = cli_rpc_pipe_open(cli,
+ transport,
+ table,
+ remote_name,
+ remote_sockaddr,
+ &rpccli);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ auth_level = netlogon_creds_cli_auth_level(netlogon_creds);
+
+ status = netlogon_creds_bind_cli_credentials(
+ netlogon_creds, rpccli, &cli_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("netlogon_creds_bind_cli_credentials failed: %s\n",
+ nt_errstr(status));
+ TALLOC_FREE(rpccli);
+ return status;
+ }
+
+ status = rpccli_generic_bind_data_from_creds(rpccli,
+ DCERPC_AUTH_TYPE_SCHANNEL,
+ auth_level,
+ rpccli->desthost,
+ target_service,
+ cli_creds,
+ &rpcauth);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("rpccli_generic_bind_data_from_creds returned %s\n",
+ nt_errstr(status)));
+ TALLOC_FREE(rpccli);
+ return status;
+ }
+
+ status = rpc_pipe_bind(rpccli, rpcauth);
+
+ /* No TALLOC_FREE, gensec takes references */
+ talloc_unlink(rpccli, cli_creds);
+ cli_creds = NULL;
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("rpc_pipe_bind failed with error %s\n",
+ nt_errstr(status));
+ TALLOC_FREE(rpccli);
+ return status;
+ }
+
+ *_rpccli = rpccli;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_rpc_pipe_open_schannel_with_creds(struct cli_state *cli,
+ const struct ndr_interface_table *table,
+ enum dcerpc_transport_t transport,
+ struct netlogon_creds_cli_context *netlogon_creds,
+ const char *remote_name,
+ const struct sockaddr_storage *remote_sockaddr,
+ struct rpc_pipe_client **_rpccli)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct rpc_pipe_client *rpccli;
+ struct netlogon_creds_cli_lck *lck;
+ NTSTATUS status;
+
+ status = netlogon_creds_cli_lck(
+ netlogon_creds, NETLOGON_CREDS_CLI_LCK_EXCLUSIVE,
+ frame, &lck);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("netlogon_creds_cli_lck returned %s\n",
+ nt_errstr(status));
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ status = cli_rpc_pipe_open_bind_schannel(cli,
+ table,
+ transport,
+ netlogon_creds,
+ remote_name,
+ remote_sockaddr,
+ &rpccli);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED)) {
+ netlogon_creds_cli_delete_lck(netlogon_creds);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("cli_rpc_pipe_open_bind_schannel failed: %s\n",
+ nt_errstr(status));
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ if (ndr_syntax_id_equal(&table->syntax_id,
+ &ndr_table_netlogon.syntax_id)) {
+ status = netlogon_creds_cli_check(netlogon_creds,
+ rpccli->binding_handle,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("netlogon_creds_cli_check failed with %s\n",
+ nt_errstr(status)));
+ TALLOC_FREE(frame);
+ return status;
+ }
+ }
+
+ DBG_DEBUG("opened pipe %s to machine %s with key %s "
+ "and bound using schannel.\n",
+ table->name, rpccli->desthost,
+ netlogon_creds_cli_debug_string(netlogon_creds, lck));
+
+ TALLOC_FREE(frame);
+
+ *_rpccli = rpccli;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_get_session_key(TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client *cli,
+ DATA_BLOB *session_key)
+{
+ NTSTATUS status;
+ struct pipe_auth_data *a;
+ struct gensec_security *gensec_security;
+ DATA_BLOB sk = { .data = NULL };
+ bool make_dup = false;
+
+ if (!session_key || !cli) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ a = cli->auth;
+
+ if (a == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ switch (cli->auth->auth_type) {
+ case DCERPC_AUTH_TYPE_NONE:
+ sk = data_blob_const(a->transport_session_key.data,
+ a->transport_session_key.length);
+ make_dup = true;
+ break;
+ default:
+ gensec_security = a->auth_ctx;
+ status = gensec_session_key(gensec_security, mem_ctx, &sk);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ make_dup = false;
+ break;
+ }
+
+ if (!sk.data) {
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ if (make_dup) {
+ *session_key = data_blob_dup_talloc(mem_ctx, sk);
+ } else {
+ *session_key = sk;
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/rpc_client/cli_pipe.h b/source3/rpc_client/cli_pipe.h
new file mode 100644
index 0000000..d9826ca
--- /dev/null
+++ b/source3/rpc_client/cli_pipe.h
@@ -0,0 +1,143 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * RPC Pipe client routines
+ *
+ * Copyright (c) 2005 Jeremy Allison
+ * Copyright (c) 2010 Simo Sorce
+ *
+ * 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 _CLI_PIPE_H
+#define _CLI_PIPE_H
+
+#include "rpc_client/rpc_client.h"
+#include "auth/credentials/credentials.h"
+
+/* The following definitions come from rpc_client/cli_pipe.c */
+
+struct tevent_req *rpc_pipe_bind_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct rpc_pipe_client *cli,
+ struct pipe_auth_data *auth);
+
+NTSTATUS rpc_pipe_bind_recv(struct tevent_req *req);
+
+NTSTATUS rpc_pipe_bind(struct rpc_pipe_client *cli,
+ struct pipe_auth_data *auth);
+
+struct tevent_req *rpc_pipe_open_np_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const struct ndr_interface_table *table);
+NTSTATUS rpc_pipe_open_np_recv(
+ struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client **_result);
+NTSTATUS rpc_pipe_open_np(struct cli_state *cli,
+ const struct ndr_interface_table *table,
+ struct rpc_pipe_client **presult);
+
+unsigned int rpccli_set_timeout(struct rpc_pipe_client *cli,
+ unsigned int timeout);
+
+bool rpccli_is_connected(struct rpc_pipe_client *rpc_cli);
+
+NTSTATUS rpccli_ncalrpc_bind_data(TALLOC_CTX *mem_ctx,
+ struct pipe_auth_data **presult);
+
+NTSTATUS rpccli_anon_bind_data(TALLOC_CTX *mem_ctx,
+ struct pipe_auth_data **presult);
+
+NTSTATUS rpc_pipe_open_ncalrpc(TALLOC_CTX *mem_ctx,
+ const struct ndr_interface_table *table,
+ struct rpc_pipe_client **presult);
+
+NTSTATUS rpc_pipe_open_local_np(
+ TALLOC_CTX *mem_ctx,
+ const struct ndr_interface_table *table,
+ const char *remote_client_name,
+ const struct tsocket_address *remote_client_addr,
+ const char *local_server_name,
+ const struct tsocket_address *local_server_addr,
+ const struct auth_session_info *session_info,
+ struct rpc_pipe_client **presult);
+
+struct dcerpc_binding_handle *rpccli_bh_create(struct rpc_pipe_client *c,
+ const struct GUID *object,
+ const struct ndr_interface_table *table);
+
+NTSTATUS cli_rpc_pipe_open_noauth(struct cli_state *cli,
+ const struct ndr_interface_table *table,
+ struct rpc_pipe_client **presult);
+
+NTSTATUS cli_rpc_pipe_open_noauth_transport(struct cli_state *cli,
+ enum dcerpc_transport_t transport,
+ const struct ndr_interface_table *table,
+ const char *remote_name,
+ const struct sockaddr_storage *remote_sockaddr,
+ struct rpc_pipe_client **presult);
+
+/****************************************************************************
+ Open a named pipe to an SMB server and bind using the mech specified
+
+ This routine steals the creds pointer that is passed in
+ ****************************************************************************/
+
+NTSTATUS cli_rpc_pipe_open_with_creds(struct cli_state *cli,
+ const struct ndr_interface_table *table,
+ enum dcerpc_transport_t transport,
+ enum dcerpc_AuthType auth_type,
+ enum dcerpc_AuthLevel auth_level,
+ const char *server,
+ const struct sockaddr_storage *remote_sockaddr,
+ struct cli_credentials *creds,
+ struct rpc_pipe_client **presult);
+
+NTSTATUS cli_rpc_pipe_open_bind_schannel(
+ struct cli_state *cli,
+ const struct ndr_interface_table *table,
+ enum dcerpc_transport_t transport,
+ struct netlogon_creds_cli_context *netlogon_creds,
+ const char *remote_name,
+ const struct sockaddr_storage *remote_sockaddr,
+ struct rpc_pipe_client **_rpccli);
+NTSTATUS cli_rpc_pipe_open_schannel_with_creds(struct cli_state *cli,
+ const struct ndr_interface_table *table,
+ enum dcerpc_transport_t transport,
+ struct netlogon_creds_cli_context *netlogon_creds,
+ const char *remote_name,
+ const struct sockaddr_storage *remote_sockaddr,
+ struct rpc_pipe_client **_rpccli);
+
+NTSTATUS cli_rpc_pipe_open_schannel(struct cli_state *cli,
+ struct messaging_context *msg_ctx,
+ const struct ndr_interface_table *table,
+ enum dcerpc_transport_t transport,
+ const char *domain,
+ const char *remote_name,
+ const struct sockaddr_storage *remote_sockaddr,
+ struct rpc_pipe_client **presult,
+ TALLOC_CTX *mem_ctx,
+ struct netlogon_creds_cli_context **pcreds);
+
+NTSTATUS cli_get_session_key(TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client *cli,
+ DATA_BLOB *session_key);
+
+#endif /* _CLI_PIPE_H */
+
+/* vim: set ts=8 sw=8 noet cindent ft=c.doxygen: */
diff --git a/source3/rpc_client/cli_pipe_schannel.c b/source3/rpc_client/cli_pipe_schannel.c
new file mode 100644
index 0000000..c33fc5b
--- /dev/null
+++ b/source3/rpc_client/cli_pipe_schannel.c
@@ -0,0 +1,120 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Largely rewritten by Jeremy Allison 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 "../librpc/gen_ndr/ndr_schannel.h"
+#include "../librpc/gen_ndr/ndr_netlogon.h"
+#include "../libcli/auth/schannel.h"
+#include "rpc_client/cli_netlogon.h"
+#include "rpc_client/cli_pipe.h"
+#include "librpc/rpc/dcerpc.h"
+#include "passdb.h"
+#include "libsmb/libsmb.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "libcli/auth/netlogon_creds_cli.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_CLI
+
+/****************************************************************************
+ Open a named pipe to an SMB server and bind using schannel (bind type 68).
+ Fetch the session key ourselves using a temporary netlogon pipe.
+ ****************************************************************************/
+
+NTSTATUS cli_rpc_pipe_open_schannel(struct cli_state *cli,
+ struct messaging_context *msg_ctx,
+ const struct ndr_interface_table *table,
+ enum dcerpc_transport_t transport,
+ const char *domain,
+ const char *remote_name,
+ const struct sockaddr_storage *remote_sockaddr,
+ struct rpc_pipe_client **presult,
+ TALLOC_CTX *mem_ctx,
+ struct netlogon_creds_cli_context **pcreds)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct rpc_pipe_client *result = NULL;
+ NTSTATUS status;
+ struct cli_credentials *cli_creds = NULL;
+ struct netlogon_creds_cli_context *netlogon_creds = NULL;
+ struct netlogon_creds_CredentialState *creds = NULL;
+ uint32_t netlogon_flags;
+
+ status = pdb_get_trust_credentials(domain, NULL,
+ frame, &cli_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ status = rpccli_create_netlogon_creds_ctx(cli_creds,
+ remote_name,
+ msg_ctx,
+ frame,
+ &netlogon_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ status = rpccli_setup_netlogon_creds(cli, transport,
+ netlogon_creds,
+ false, /* force_reauth */
+ cli_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ status = netlogon_creds_cli_get(netlogon_creds, frame, &creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ netlogon_flags = creds->negotiate_flags;
+ TALLOC_FREE(creds);
+
+ if (netlogon_flags & NETLOGON_NEG_AUTHENTICATED_RPC) {
+ status = cli_rpc_pipe_open_schannel_with_creds(cli, table,
+ transport,
+ netlogon_creds,
+ remote_name,
+ remote_sockaddr,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
+ } else {
+ status = cli_rpc_pipe_open_noauth(cli, table, &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
+ }
+
+ *presult = result;
+ if (pcreds != NULL) {
+ *pcreds = talloc_move(mem_ctx, &netlogon_creds);
+ }
+
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+}
diff --git a/source3/rpc_client/cli_samr.c b/source3/rpc_client/cli_samr.c
new file mode 100644
index 0000000..f5a6b17
--- /dev/null
+++ b/source3/rpc_client/cli_samr.c
@@ -0,0 +1,654 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+ Copyright (C) Tim Potter 2000-2001,
+ Copyright (C) Andrew Tridgell 1992-1997,2000,
+ Copyright (C) Rafal Szczesniak 2002.
+ Copyright (C) Jeremy Allison 2005.
+ Copyright (C) Guenther Deschner 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 "rpc_client/rpc_client.h"
+#include "../libcli/auth/libcli_auth.h"
+#include "../librpc/gen_ndr/ndr_samr_c.h"
+#include "rpc_client/cli_samr.h"
+#include "rpc_client/init_lsa.h"
+#include "rpc_client/init_samr.h"
+#include "librpc/rpc/dcerpc_samr.h"
+#include "lib/crypto/gnutls_helpers.h"
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+/* User change password */
+
+NTSTATUS dcerpc_samr_chgpasswd_user(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *user_handle,
+ const char *newpassword,
+ const char *oldpassword,
+ NTSTATUS *presult)
+{
+ NTSTATUS status;
+ int rc;
+ struct samr_Password hash1, hash2, hash3, hash4, hash5, hash6;
+
+ uint8_t old_nt_hash[16] = {0};
+ uint8_t old_lm_hash[16] = {0};
+ uint8_t new_nt_hash[16] = {0};
+ uint8_t new_lm_hash[16] = {0};
+
+ DEBUG(10,("rpccli_samr_chgpasswd_user\n"));
+
+ E_md4hash(oldpassword, old_nt_hash);
+ E_md4hash(newpassword, new_nt_hash);
+
+ E_deshash(oldpassword, old_lm_hash);
+ E_deshash(newpassword, new_lm_hash);
+
+ rc = E_old_pw_hash(new_lm_hash, old_lm_hash, hash1.hash);
+ if (rc != 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ goto done;
+ }
+ rc = E_old_pw_hash(old_lm_hash, new_lm_hash, hash2.hash);
+ if (rc != 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ goto done;
+ }
+ rc = E_old_pw_hash(new_nt_hash, old_nt_hash, hash3.hash);
+ if (rc != 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ goto done;
+ }
+ rc = E_old_pw_hash(old_nt_hash, new_nt_hash, hash4.hash);
+ if (rc != 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ goto done;
+ }
+ rc = E_old_pw_hash(old_lm_hash, new_nt_hash, hash5.hash);
+ if (rc != 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ goto done;
+ }
+ rc = E_old_pw_hash(old_nt_hash, new_lm_hash, hash6.hash);
+ if (rc != 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ goto done;
+ }
+
+ status = dcerpc_samr_ChangePasswordUser(h,
+ mem_ctx,
+ user_handle,
+ true,
+ &hash1,
+ &hash2,
+ true,
+ &hash3,
+ &hash4,
+ true,
+ &hash5,
+ true,
+ &hash6,
+ presult);
+
+done:
+ ZERO_ARRAY(old_nt_hash);
+ ZERO_ARRAY(old_lm_hash);
+ ZERO_ARRAY(new_nt_hash);
+ ZERO_ARRAY(new_lm_hash);
+
+ return status;
+}
+
+NTSTATUS rpccli_samr_chgpasswd_user(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *user_handle,
+ const char *newpassword,
+ const char *oldpassword)
+{
+ NTSTATUS status;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ status = dcerpc_samr_chgpasswd_user(cli->binding_handle,
+ mem_ctx,
+ user_handle,
+ newpassword,
+ oldpassword,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return result;
+}
+
+/* User change password */
+
+NTSTATUS dcerpc_samr_chgpasswd_user2(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ const char *srv_name_slash,
+ const char *username,
+ const char *newpassword,
+ const char *oldpassword,
+ NTSTATUS *presult)
+{
+ NTSTATUS status;
+ int rc;
+ struct samr_CryptPassword new_nt_password;
+ struct samr_CryptPassword new_lm_password;
+ struct samr_Password old_nt_hash_enc;
+ struct samr_Password old_lanman_hash_enc;
+
+ uint8_t old_nt_hash[16] = { 0 };
+ uint8_t old_lanman_hash[16];
+ uint8_t new_nt_hash[16];
+ uint8_t new_lanman_hash[16];
+ struct lsa_String server, account;
+
+ DATA_BLOB session_key = data_blob_const(old_nt_hash, 16);
+
+ DEBUG(10,("rpccli_samr_chgpasswd_user2\n"));
+
+ init_lsa_String(&server, srv_name_slash);
+ init_lsa_String(&account, username);
+
+ /* Calculate the MD4 hash (NT compatible) of the password */
+ E_md4hash(oldpassword, old_nt_hash);
+ E_md4hash(newpassword, new_nt_hash);
+
+ if (lp_client_lanman_auth() &&
+ E_deshash(newpassword, new_lanman_hash) &&
+ E_deshash(oldpassword, old_lanman_hash)) {
+ /* E_deshash returns false for 'long' passwords (> 14
+ DOS chars). This allows us to match Win2k, which
+ does not store a LM hash for these passwords (which
+ would reduce the effective password length to 14) */
+ status = init_samr_CryptPassword(newpassword,
+ &session_key,
+ &new_lm_password);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ rc = E_old_pw_hash(new_nt_hash, old_lanman_hash, old_lanman_hash_enc.hash);
+ if (rc != 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ goto done;
+ }
+ } else {
+ ZERO_STRUCT(new_lm_password);
+ ZERO_STRUCT(old_lanman_hash_enc);
+ }
+
+ status = init_samr_CryptPassword(newpassword,
+ &session_key,
+ &new_nt_password);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ rc = E_old_pw_hash(new_nt_hash, old_nt_hash, old_nt_hash_enc.hash);
+ if (rc != 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ goto done;
+ }
+
+ status = dcerpc_samr_ChangePasswordUser2(h,
+ mem_ctx,
+ &server,
+ &account,
+ &new_nt_password,
+ &old_nt_hash_enc,
+ true,
+ &new_lm_password,
+ &old_lanman_hash_enc,
+ presult);
+
+done:
+ ZERO_STRUCT(new_nt_password);
+ ZERO_STRUCT(new_lm_password);
+ ZERO_STRUCT(old_nt_hash_enc);
+ ZERO_STRUCT(old_lanman_hash_enc);
+ ZERO_ARRAY(new_nt_hash);
+ ZERO_ARRAY(new_lanman_hash);
+ ZERO_ARRAY(old_nt_hash);
+ ZERO_ARRAY(old_lanman_hash);
+
+ return status;
+}
+
+NTSTATUS rpccli_samr_chgpasswd_user2(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ const char *username,
+ const char *newpassword,
+ const char *oldpassword)
+{
+ NTSTATUS status;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ status = dcerpc_samr_chgpasswd_user2(cli->binding_handle,
+ mem_ctx,
+ cli->srv_name_slash,
+ username,
+ newpassword,
+ oldpassword,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return result;
+}
+
+/* User change password given blobs */
+
+NTSTATUS dcerpc_samr_chng_pswd_auth_crap(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ const char *srv_name_slash,
+ const char *username,
+ DATA_BLOB new_nt_password_blob,
+ DATA_BLOB old_nt_hash_enc_blob,
+ DATA_BLOB new_lm_password_blob,
+ DATA_BLOB old_lm_hash_enc_blob,
+ NTSTATUS *presult)
+{
+ NTSTATUS status;
+ struct samr_CryptPassword new_nt_password;
+ struct samr_CryptPassword new_lm_password;
+ struct samr_Password old_nt_hash_enc;
+ struct samr_Password old_lm_hash_enc;
+ struct lsa_String server, account;
+
+ DEBUG(10,("rpccli_samr_chng_pswd_auth_crap\n"));
+
+ ZERO_STRUCT(new_nt_password);
+ ZERO_STRUCT(new_lm_password);
+ ZERO_STRUCT(old_nt_hash_enc);
+ ZERO_STRUCT(old_lm_hash_enc);
+
+ init_lsa_String(&server, srv_name_slash);
+ init_lsa_String(&account, username);
+
+ if (new_nt_password_blob.data && new_nt_password_blob.length >= 516) {
+ memcpy(&new_nt_password.data, new_nt_password_blob.data, 516);
+ }
+
+ if (new_lm_password_blob.data && new_lm_password_blob.length >= 516) {
+ memcpy(&new_lm_password.data, new_lm_password_blob.data, 516);
+ }
+
+ if (old_nt_hash_enc_blob.data && old_nt_hash_enc_blob.length >= 16) {
+ memcpy(&old_nt_hash_enc.hash, old_nt_hash_enc_blob.data, 16);
+ }
+
+ if (old_lm_hash_enc_blob.data && old_lm_hash_enc_blob.length >= 16) {
+ memcpy(&old_lm_hash_enc.hash, old_lm_hash_enc_blob.data, 16);
+ }
+
+ status = dcerpc_samr_ChangePasswordUser2(h,
+ mem_ctx,
+ &server,
+ &account,
+ &new_nt_password,
+ &old_nt_hash_enc,
+ true,
+ &new_lm_password,
+ &old_lm_hash_enc,
+ presult);
+
+ return status;
+}
+
+NTSTATUS rpccli_samr_chng_pswd_auth_crap(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ const char *username,
+ DATA_BLOB new_nt_password_blob,
+ DATA_BLOB old_nt_hash_enc_blob,
+ DATA_BLOB new_lm_password_blob,
+ DATA_BLOB old_lm_hash_enc_blob)
+{
+ NTSTATUS status;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ status = dcerpc_samr_chng_pswd_auth_crap(cli->binding_handle,
+ mem_ctx,
+ cli->srv_name_slash,
+ username,
+ new_nt_password_blob,
+ old_nt_hash_enc_blob,
+ new_lm_password_blob,
+ old_lm_hash_enc_blob,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return result;
+}
+
+/* change password 3 */
+
+NTSTATUS dcerpc_samr_chgpasswd_user3(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ const char *srv_name_slash,
+ const char *username,
+ const char *newpassword,
+ const char *oldpassword,
+ struct samr_DomInfo1 **dominfo1,
+ struct userPwdChangeFailureInformation **reject,
+ NTSTATUS *presult)
+{
+ NTSTATUS status;
+ int rc;
+
+ struct samr_CryptPassword new_nt_password;
+ struct samr_CryptPassword new_lm_password;
+ struct samr_Password old_nt_hash_enc;
+ struct samr_Password old_lanman_hash_enc;
+
+ uint8_t old_nt_hash[16] = { 0 };
+ uint8_t old_lanman_hash[16];
+ uint8_t new_nt_hash[16];
+ uint8_t new_lanman_hash[16];
+
+ struct lsa_String server, account;
+
+ DATA_BLOB session_key = data_blob_const(old_nt_hash, 16);
+
+ DEBUG(10,("rpccli_samr_chgpasswd_user3\n"));
+
+ init_lsa_String(&server, srv_name_slash);
+ init_lsa_String(&account, username);
+
+ /* Calculate the MD4 hash (NT compatible) of the password */
+ E_md4hash(oldpassword, old_nt_hash);
+ E_md4hash(newpassword, new_nt_hash);
+
+ if (lp_client_lanman_auth() &&
+ E_deshash(newpassword, new_lanman_hash) &&
+ E_deshash(oldpassword, old_lanman_hash)) {
+ /* E_deshash returns false for 'long' passwords (> 14
+ DOS chars). This allows us to match Win2k, which
+ does not store a LM hash for these passwords (which
+ would reduce the effective password length to 14) */
+ status = init_samr_CryptPassword(newpassword,
+ &session_key,
+ &new_lm_password);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ rc = E_old_pw_hash(new_nt_hash, old_lanman_hash, old_lanman_hash_enc.hash);
+ if (rc != 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ goto done;
+ }
+ } else {
+ ZERO_STRUCT(new_lm_password);
+ ZERO_STRUCT(old_lanman_hash_enc);
+ }
+
+ status = init_samr_CryptPassword(newpassword,
+ &session_key,
+ &new_nt_password);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ rc = E_old_pw_hash(new_nt_hash, old_nt_hash, old_nt_hash_enc.hash);
+ if (rc != 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ goto done;
+ }
+
+ status = dcerpc_samr_ChangePasswordUser3(h,
+ mem_ctx,
+ &server,
+ &account,
+ &new_nt_password,
+ &old_nt_hash_enc,
+ true,
+ &new_lm_password,
+ &old_lanman_hash_enc,
+ NULL,
+ dominfo1,
+ reject,
+ presult);
+
+done:
+ ZERO_STRUCT(new_nt_password);
+ ZERO_STRUCT(new_lm_password);
+ ZERO_STRUCT(old_nt_hash_enc);
+ ZERO_STRUCT(old_lanman_hash_enc);
+ ZERO_ARRAY(new_nt_hash);
+ ZERO_ARRAY(new_lanman_hash);
+ ZERO_ARRAY(old_nt_hash);
+ ZERO_ARRAY(old_lanman_hash);
+
+ return status;
+}
+
+NTSTATUS rpccli_samr_chgpasswd_user3(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ const char *username,
+ const char *newpassword,
+ const char *oldpassword,
+ struct samr_DomInfo1 **dominfo1,
+ struct userPwdChangeFailureInformation **reject)
+{
+ NTSTATUS status;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ status = dcerpc_samr_chgpasswd_user3(cli->binding_handle,
+ mem_ctx,
+ cli->srv_name_slash,
+ username,
+ newpassword,
+ oldpassword,
+ dominfo1,
+ reject,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return result;
+}
+
+NTSTATUS dcerpc_samr_chgpasswd_user4(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ const char *srv_name_slash,
+ const char *username,
+ const char *oldpassword,
+ const char *newpassword,
+ NTSTATUS *presult)
+{
+ struct lsa_String server, user_account;
+ uint8_t old_nt_key_data[16] = {0};
+ gnutls_datum_t old_nt_key = {
+ .data = old_nt_key_data,
+ .size = sizeof(old_nt_key),
+ };
+ struct samr_EncryptedPasswordAES pwd_buf = {
+ .cipher_len = 0,
+ };
+ DATA_BLOB iv = {
+ .data = pwd_buf.salt,
+ .length = sizeof(pwd_buf.salt),
+ };
+ gnutls_datum_t iv_datum = {
+ .data = iv.data,
+ .size = iv.length,
+ };
+ uint8_t cek_data[16] = {0};
+ DATA_BLOB cek = {
+ .data = cek_data,
+ .length = sizeof(cek_data),
+ };
+ uint64_t pbkdf2_iterations = 0;
+ uint8_t pw_data[514] = {0};
+ DATA_BLOB plaintext = {
+ .data = pw_data,
+ .length = sizeof(pw_data),
+ };
+ DATA_BLOB ciphertext = data_blob_null;
+ NTSTATUS status;
+ bool ok;
+ int rc;
+
+ generate_nonce_buffer(iv.data, iv.length);
+
+ /* Calculate the MD4 hash (NT compatible) of the password */
+ E_md4hash(oldpassword, old_nt_key_data);
+
+ init_lsa_String(&server, srv_name_slash);
+ init_lsa_String(&user_account, username);
+
+ pbkdf2_iterations = generate_random_u64_range(5000, 1000000);
+
+ rc = gnutls_pbkdf2(GNUTLS_MAC_SHA512,
+ &old_nt_key,
+ &iv_datum,
+ pbkdf2_iterations,
+ cek.data,
+ cek.length);
+ BURN_DATA(old_nt_key_data);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_WRONG_PASSWORD);
+ return status;
+ }
+
+ ok = encode_pwd_buffer514_from_str(pw_data, newpassword, STR_UNICODE);
+ if (!ok) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ status = samba_gnutls_aead_aes_256_cbc_hmac_sha512_encrypt(
+ mem_ctx,
+ &plaintext,
+ &cek,
+ &samr_aes256_enc_key_salt,
+ &samr_aes256_mac_key_salt,
+ &iv,
+ &ciphertext,
+ pwd_buf.auth_data);
+ BURN_DATA(pw_data);
+ BURN_DATA(cek_data);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ pwd_buf.cipher_len = ciphertext.length;
+ pwd_buf.cipher = ciphertext.data;
+ pwd_buf.PBKDF2Iterations = pbkdf2_iterations;
+
+ status = dcerpc_samr_ChangePasswordUser4(h,
+ mem_ctx,
+ &server,
+ &user_account,
+ &pwd_buf,
+ presult);
+ data_blob_free(&ciphertext);
+
+ return status;
+}
+
+/* This function returns the bizarre set of (max_entries, max_size) required
+ for the QueryDisplayInfo RPC to actually work against a domain controller
+ with large (10k and higher) numbers of users. These values were
+ obtained by inspection using ethereal and NT4 running User Manager. */
+
+void dcerpc_get_query_dispinfo_params(int loop_count,
+ uint32_t *max_entries,
+ uint32_t *max_size)
+{
+ switch(loop_count) {
+ case 0:
+ *max_entries = 512;
+ *max_size = 16383;
+ break;
+ case 1:
+ *max_entries = 1024;
+ *max_size = 32766;
+ break;
+ case 2:
+ *max_entries = 2048;
+ *max_size = 65532;
+ break;
+ case 3:
+ *max_entries = 4096;
+ *max_size = 131064;
+ break;
+ default: /* loop_count >= 4 */
+ *max_entries = 4096;
+ *max_size = 131071;
+ break;
+ }
+}
+
+NTSTATUS dcerpc_try_samr_connects(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ const char *srv_name_slash,
+ uint32_t access_mask,
+ struct policy_handle *connect_pol,
+ NTSTATUS *presult)
+{
+ NTSTATUS status;
+ union samr_ConnectInfo info_in, info_out;
+ struct samr_ConnectInfo1 info1;
+ uint32_t lvl_out = 0;
+
+ ZERO_STRUCT(info1);
+
+ info1.client_version = SAMR_CONNECT_W2K;
+ info_in.info1 = info1;
+
+ status = dcerpc_samr_Connect5(h,
+ mem_ctx,
+ srv_name_slash,
+ access_mask,
+ 1,
+ &info_in,
+ &lvl_out,
+ &info_out,
+ connect_pol,
+ presult);
+ if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(*presult)) {
+ return status;
+ }
+
+ status = dcerpc_samr_Connect4(h,
+ mem_ctx,
+ srv_name_slash,
+ SAMR_CONNECT_W2K,
+ access_mask,
+ connect_pol,
+ presult);
+ if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(*presult)) {
+ return status;
+ }
+
+ status = dcerpc_samr_Connect2(h,
+ mem_ctx,
+ srv_name_slash,
+ access_mask,
+ connect_pol,
+ presult);
+
+ return status;
+}
+
+/* vim: set ts=8 sw=8 noet cindent: */
diff --git a/source3/rpc_client/cli_samr.h b/source3/rpc_client/cli_samr.h
new file mode 100644
index 0000000..5a4f39c
--- /dev/null
+++ b/source3/rpc_client/cli_samr.h
@@ -0,0 +1,229 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * SAMR client routines
+ *
+ * Copyright (c) 2000-2001 Tim Potter
+ * Copyright (c) 1992-2000 Andrew Tridgell
+ * Copyright (c) 2002 Rafal Szczesniak
+ * Copyright (c) 2005 Jeremy Allison
+ * Copyright (c) 2007 Michael Adam
+ * Copyright (c) 2008 Guenther Deschner
+ *
+ * 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 _CLI_SAMR_H
+#define _CLI_SAMR_H
+
+/* The following definitions come from rpc_client/cli_samr.c */
+
+/**
+ * @brief Change the password of a user.
+ *
+ * @param[in] h The dcerpc binding handle to use.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] user_handle The password of the user to chang the handle
+ *
+ * @param[in] newpassword The new password to set.
+ *
+ * @param[in] oldpassword The old password for verification
+ *
+ * @param[out] presult A pointer for the NDR NTSTATUS error code.
+ *
+ * @return A corresponding NTSTATUS error code for the connection.
+ */
+NTSTATUS dcerpc_samr_chgpasswd_user(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *user_handle,
+ const char *newpassword,
+ const char *oldpassword,
+ NTSTATUS *presult);
+
+NTSTATUS rpccli_samr_chgpasswd_user(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *user_handle,
+ const char *newpassword,
+ const char *oldpassword);
+
+/**
+ * @brief Change the password of a user based on username.
+ *
+ * @param[in] h The dcerpc binding handle to use.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] srv_name_slash The server name with leading slashes.
+ *
+ * @param[in] username The name of the user.
+ *
+ * @param[in] newpassword The new password to set.
+ *
+ * @param[in] oldpassword The old password for verification
+ *
+ * @param[out] presult A pointer for the NDR NTSTATUS error code.
+ *
+ * @return A corresponding NTSTATUS error code for the connection.
+ */
+NTSTATUS dcerpc_samr_chgpasswd_user2(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ const char *srv_name_slash,
+ const char *username,
+ const char *newpassword,
+ const char *oldpassword,
+ NTSTATUS *presult);
+
+NTSTATUS rpccli_samr_chgpasswd_user2(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ const char *username,
+ const char *newpassword,
+ const char *oldpassword);
+
+/**
+ * @brief Change the password of a user based on the user name given and using
+ * blobs.
+ *
+ * @param[in] h The dcerpc binding handle to use.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] srv_name_slash The server name with leading slashes.
+ *
+ * @param[in] username The name of the user.
+ *
+ * @param[in] new_nt_password_blob The new password as a encrypted blob.
+ *
+ * @param[in] old_nt_hash_enc_blob The old password as a hash encoded blob.
+ *
+ * @param[in] new_lm_password_blob The new password as a lanman encoded blob.
+ *
+ * @param[in] old_lm_hash_enc_blob The old password as a lanman encoded blob.
+ *
+ * @param[out] presult A pointer for the NDR NTSTATUS error code.
+ *
+ * @return A corresponding NTSTATUS error code for the connection.
+ */
+NTSTATUS dcerpc_samr_chng_pswd_auth_crap(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ const char *srv_name_slash,
+ const char *username,
+ DATA_BLOB new_nt_password_blob,
+ DATA_BLOB old_nt_hash_enc_blob,
+ DATA_BLOB new_lm_password_blob,
+ DATA_BLOB old_lm_hash_enc_blob,
+ NTSTATUS *presult);
+
+NTSTATUS rpccli_samr_chng_pswd_auth_crap(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ const char *username,
+ DATA_BLOB new_nt_password_blob,
+ DATA_BLOB old_nt_hash_enc_blob,
+ DATA_BLOB new_lm_password_blob,
+ DATA_BLOB old_lm_hash_enc_blob);
+
+/**
+ * @brief
+ *
+ * @param[in] h The dcerpc binding handle to use.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] srv_name_slash The server name with leading slashes.
+ *
+ * @param[in] username The name of the user.
+ *
+ * @param[in] newpassword The new password to set.
+ *
+ * @param[in] oldpassword The old password to set.
+ *
+ * @param[in] dominfo1 A pointer to hold the domain information.
+ *
+ * @param[in] reject A pointer to store the result of a possible reject.
+ *
+ * @param[out] presult A pointer for the NDR NTSTATUS error code.
+ *
+ * @return A corresponding NTSTATUS error code for the connection.
+ */
+NTSTATUS dcerpc_samr_chgpasswd_user3(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ const char *srv_name_slash,
+ const char *username,
+ const char *newpassword,
+ const char *oldpassword,
+ struct samr_DomInfo1 **dominfo1,
+ struct userPwdChangeFailureInformation **reject,
+ NTSTATUS *presult);
+
+NTSTATUS rpccli_samr_chgpasswd_user3(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ const char *username,
+ const char *newpassword,
+ const char *oldpassword,
+ struct samr_DomInfo1 **dominfo1,
+ struct userPwdChangeFailureInformation **reject);
+
+NTSTATUS dcerpc_samr_chgpasswd_user4(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ const char *srv_name_slash,
+ const char *username,
+ const char *oldpassword,
+ const char *newpassword,
+ NTSTATUS *presult);
+
+/**
+ * @brief Create a set of max_entries, max_size for QueryDisplayInfo.
+ *
+ * This function returns a set of (max_entries, max_size) required
+ * for the QueryDisplayInfo RPC to actually work against a domain controller
+ * with large (10k and higher) numbers of users. These values were
+ * obtained by inspection using wireshark and NT4 running User Manager.
+ *
+ * @param[in] loop_count The loop count.
+ *
+ * @param[out] max_entries A pointer to store maximum entries value.
+ *
+ * @param[out] max_size A pointer to store the maximum size value.
+ */
+void dcerpc_get_query_dispinfo_params(int loop_count,
+ uint32_t *max_entries,
+ uint32_t *max_size);
+
+/**
+ * @brief Try if we can connect to samr.
+ *
+ * @param[in] h The dcerpc binding handle to use.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] srv_name_slash The server name with leading slashes.
+ *
+ * @param[in] access_mask The access mask to use to open the connection.
+ *
+ * @param[in] connect_pol A pointer to store the policy handle for the
+ * connection.
+ *
+ * @param[out] presult A pointer for the NDR NTSTATUS error code.
+ *
+ * @return A corresponding NTSTATUS error code for the connection.
+ */
+NTSTATUS dcerpc_try_samr_connects(struct dcerpc_binding_handle *h,
+ TALLOC_CTX *mem_ctx,
+ const char *srv_name_slash,
+ uint32_t access_mask,
+ struct policy_handle *connect_pol,
+ NTSTATUS *presult);
+
+#endif /* _CLI_SAMR_H */
diff --git a/source3/rpc_client/cli_spoolss.c b/source3/rpc_client/cli_spoolss.c
new file mode 100644
index 0000000..094381c
--- /dev/null
+++ b/source3/rpc_client/cli_spoolss.c
@@ -0,0 +1,1006 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Gerald Carter 2001-2005,
+ Copyright (C) Tim Potter 2000-2002,
+ Copyright (C) Andrew Tridgell 1994-2000,
+ Copyright (C) Jean-Francois Micouleau 1999-2000.
+ Copyright (C) Jeremy Allison 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 "rpc_client/rpc_client.h"
+#include "../librpc/gen_ndr/ndr_spoolss_c.h"
+#include "rpc_client/cli_spoolss.h"
+#include "auth/gensec/gensec.h"
+#include "auth/credentials/credentials.h"
+#include "rpc_client/init_spoolss.h"
+
+/**********************************************************************
+ convenience wrapper around rpccli_spoolss_OpenPrinterEx
+**********************************************************************/
+
+WERROR rpccli_spoolss_openprinter_ex(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ const char *printername,
+ uint32_t access_desired,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ WERROR werror;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct spoolss_UserLevelCtr userlevel_ctr;
+ struct spoolss_UserLevel1 level1;
+ struct dcerpc_binding_handle *b = cli->binding_handle;
+ struct cli_credentials *creds = gensec_get_credentials(cli->auth->auth_ctx);
+
+ ZERO_STRUCT(devmode_ctr);
+
+ werror = spoolss_init_spoolss_UserLevel1(mem_ctx,
+ cli_credentials_get_username(creds),
+ &level1);
+ if (!W_ERROR_IS_OK(werror)) {
+ return werror;
+ }
+
+ userlevel_ctr.level = 1;
+ userlevel_ctr.user_info.level1 = &level1;
+
+ status = dcerpc_spoolss_OpenPrinterEx(b, mem_ctx,
+ printername,
+ NULL,
+ devmode_ctr,
+ access_desired,
+ userlevel_ctr,
+ handle,
+ &werror);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ if (!W_ERROR_IS_OK(werror)) {
+ return werror;
+ }
+
+ return WERR_OK;
+}
+
+/**********************************************************************
+ convenience wrapper around rpccli_spoolss_GetPrinterDriver
+**********************************************************************/
+
+WERROR rpccli_spoolss_getprinterdriver(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ const char *architecture,
+ uint32_t level,
+ uint32_t offered,
+ union spoolss_DriverInfo *info)
+{
+ NTSTATUS status;
+ WERROR werror;
+ uint32_t needed;
+ DATA_BLOB buffer;
+ struct dcerpc_binding_handle *b = cli->binding_handle;
+
+ if (offered > 0) {
+ buffer = data_blob_talloc_zero(mem_ctx, offered);
+ W_ERROR_HAVE_NO_MEMORY(buffer.data);
+ }
+
+ status = dcerpc_spoolss_GetPrinterDriver(b, mem_ctx,
+ handle,
+ architecture,
+ level,
+ (offered > 0) ? &buffer : NULL,
+ offered,
+ info,
+ &needed,
+ &werror);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+ if (W_ERROR_EQUAL(werror, WERR_INSUFFICIENT_BUFFER)) {
+ offered = needed;
+ buffer = data_blob_talloc_zero(mem_ctx, needed);
+ W_ERROR_HAVE_NO_MEMORY(buffer.data);
+
+ status = dcerpc_spoolss_GetPrinterDriver(b, mem_ctx,
+ handle,
+ architecture,
+ level,
+ &buffer,
+ offered,
+ info,
+ &needed,
+ &werror);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ return werror;
+}
+
+/**********************************************************************
+ convenience wrapper around rpccli_spoolss_GetPrinterDriver2
+**********************************************************************/
+
+WERROR rpccli_spoolss_getprinterdriver2(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ const char *architecture,
+ uint32_t level,
+ uint32_t offered,
+ uint32_t client_major_version,
+ uint32_t client_minor_version,
+ union spoolss_DriverInfo *info,
+ uint32_t *server_major_version,
+ uint32_t *server_minor_version)
+{
+ NTSTATUS status;
+ WERROR werror;
+ uint32_t needed;
+ DATA_BLOB buffer;
+ struct dcerpc_binding_handle *b = cli->binding_handle;
+
+ if (offered > 0) {
+ buffer = data_blob_talloc_zero(mem_ctx, offered);
+ W_ERROR_HAVE_NO_MEMORY(buffer.data);
+ }
+
+ status = dcerpc_spoolss_GetPrinterDriver2(b, mem_ctx,
+ handle,
+ architecture,
+ level,
+ (offered > 0) ? &buffer : NULL,
+ offered,
+ client_major_version,
+ client_minor_version,
+ info,
+ &needed,
+ server_major_version,
+ server_minor_version,
+ &werror);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ if (W_ERROR_EQUAL(werror, WERR_INSUFFICIENT_BUFFER)) {
+ offered = needed;
+ buffer = data_blob_talloc_zero(mem_ctx, needed);
+ W_ERROR_HAVE_NO_MEMORY(buffer.data);
+
+ status = dcerpc_spoolss_GetPrinterDriver2(b, mem_ctx,
+ handle,
+ architecture,
+ level,
+ &buffer,
+ offered,
+ client_major_version,
+ client_minor_version,
+ info,
+ &needed,
+ server_major_version,
+ server_minor_version,
+ &werror);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ return werror;
+}
+
+/**********************************************************************
+ convenience wrapper around rpccli_spoolss_AddPrinterEx
+**********************************************************************/
+
+WERROR rpccli_spoolss_addprinterex(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ struct spoolss_SetPrinterInfoCtr *info_ctr)
+{
+ WERROR result;
+ NTSTATUS status;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+ struct spoolss_UserLevelCtr userlevel_ctr;
+ struct spoolss_UserLevel1 level1;
+ struct policy_handle handle;
+ struct dcerpc_binding_handle *b = cli->binding_handle;
+ struct cli_credentials *creds = gensec_get_credentials(cli->auth->auth_ctx);
+
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+
+ result = spoolss_init_spoolss_UserLevel1(mem_ctx,
+ cli_credentials_get_username(creds),
+ &level1);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ userlevel_ctr.level = 1;
+ userlevel_ctr.user_info.level1 = &level1;
+
+ status = dcerpc_spoolss_AddPrinterEx(b, mem_ctx,
+ cli->srv_name_slash,
+ info_ctr,
+ &devmode_ctr,
+ &secdesc_ctr,
+ &userlevel_ctr,
+ &handle,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ return result;
+}
+
+/**********************************************************************
+ convenience wrapper around rpccli_spoolss_GetPrinter
+**********************************************************************/
+
+WERROR rpccli_spoolss_getprinter(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ uint32_t level,
+ uint32_t offered,
+ union spoolss_PrinterInfo *info)
+{
+ NTSTATUS status;
+ WERROR werror;
+ DATA_BLOB buffer;
+ uint32_t needed;
+ struct dcerpc_binding_handle *b = cli->binding_handle;
+
+ if (offered > 0) {
+ buffer = data_blob_talloc_zero(mem_ctx, offered);
+ W_ERROR_HAVE_NO_MEMORY(buffer.data);
+ }
+
+ status = dcerpc_spoolss_GetPrinter(b, mem_ctx,
+ handle,
+ level,
+ (offered > 0) ? &buffer : NULL,
+ offered,
+ info,
+ &needed,
+ &werror);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ if (W_ERROR_EQUAL(werror, WERR_INSUFFICIENT_BUFFER)) {
+
+ offered = needed;
+ buffer = data_blob_talloc_zero(mem_ctx, offered);
+ W_ERROR_HAVE_NO_MEMORY(buffer.data);
+
+ status = dcerpc_spoolss_GetPrinter(b, mem_ctx,
+ handle,
+ level,
+ &buffer,
+ offered,
+ info,
+ &needed,
+ &werror);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ return werror;
+}
+
+/**********************************************************************
+ convenience wrapper around rpccli_spoolss_GetJob
+**********************************************************************/
+
+WERROR rpccli_spoolss_getjob(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ uint32_t job_id,
+ uint32_t level,
+ uint32_t offered,
+ union spoolss_JobInfo *info)
+{
+ NTSTATUS status;
+ WERROR werror;
+ uint32_t needed;
+ DATA_BLOB buffer;
+ struct dcerpc_binding_handle *b = cli->binding_handle;
+
+ if (offered > 0) {
+ buffer = data_blob_talloc_zero(mem_ctx, offered);
+ W_ERROR_HAVE_NO_MEMORY(buffer.data);
+ }
+
+ status = dcerpc_spoolss_GetJob(b, mem_ctx,
+ handle,
+ job_id,
+ level,
+ (offered > 0) ? &buffer : NULL,
+ offered,
+ info,
+ &needed,
+ &werror);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ if (W_ERROR_EQUAL(werror, WERR_INSUFFICIENT_BUFFER)) {
+ offered = needed;
+ buffer = data_blob_talloc_zero(mem_ctx, needed);
+ W_ERROR_HAVE_NO_MEMORY(buffer.data);
+
+ status = dcerpc_spoolss_GetJob(b, mem_ctx,
+ handle,
+ job_id,
+ level,
+ &buffer,
+ offered,
+ info,
+ &needed,
+ &werror);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ return werror;
+}
+
+/**********************************************************************
+ convenience wrapper around rpccli_spoolss_EnumForms
+**********************************************************************/
+
+WERROR rpccli_spoolss_enumforms(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ uint32_t level,
+ uint32_t offered,
+ uint32_t *count,
+ union spoolss_FormInfo **info)
+{
+ NTSTATUS status;
+ WERROR werror;
+ uint32_t needed;
+ DATA_BLOB buffer;
+ struct dcerpc_binding_handle *b = cli->binding_handle;
+
+ if (offered > 0) {
+ buffer = data_blob_talloc_zero(mem_ctx, offered);
+ W_ERROR_HAVE_NO_MEMORY(buffer.data);
+ }
+
+ status = dcerpc_spoolss_EnumForms(b, mem_ctx,
+ handle,
+ level,
+ (offered > 0) ? &buffer : NULL,
+ offered,
+ count,
+ info,
+ &needed,
+ &werror);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ if (W_ERROR_EQUAL(werror, WERR_INSUFFICIENT_BUFFER)) {
+ offered = needed;
+ buffer = data_blob_talloc_zero(mem_ctx, needed);
+ W_ERROR_HAVE_NO_MEMORY(buffer.data);
+
+ status = dcerpc_spoolss_EnumForms(b, mem_ctx,
+ handle,
+ level,
+ (offered > 0) ? &buffer : NULL,
+ offered,
+ count,
+ info,
+ &needed,
+ &werror);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ return werror;
+}
+
+/**********************************************************************
+ convenience wrapper around rpccli_spoolss_EnumPrintProcessors
+**********************************************************************/
+
+WERROR rpccli_spoolss_enumprintprocessors(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ const char *servername,
+ const char *environment,
+ uint32_t level,
+ uint32_t offered,
+ uint32_t *count,
+ union spoolss_PrintProcessorInfo **info)
+{
+ NTSTATUS status;
+ WERROR werror;
+ uint32_t needed;
+ DATA_BLOB buffer;
+ struct dcerpc_binding_handle *b = cli->binding_handle;
+
+ if (offered > 0) {
+ buffer = data_blob_talloc_zero(mem_ctx, offered);
+ W_ERROR_HAVE_NO_MEMORY(buffer.data);
+ }
+
+ status = dcerpc_spoolss_EnumPrintProcessors(b, mem_ctx,
+ servername,
+ environment,
+ level,
+ (offered > 0) ? &buffer : NULL,
+ offered,
+ count,
+ info,
+ &needed,
+ &werror);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ if (W_ERROR_EQUAL(werror, WERR_INSUFFICIENT_BUFFER)) {
+ offered = needed;
+ buffer = data_blob_talloc_zero(mem_ctx, needed);
+ W_ERROR_HAVE_NO_MEMORY(buffer.data);
+
+ status = dcerpc_spoolss_EnumPrintProcessors(b, mem_ctx,
+ servername,
+ environment,
+ level,
+ (offered > 0) ? &buffer : NULL,
+ offered,
+ count,
+ info,
+ &needed,
+ &werror);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ return werror;
+}
+
+/**********************************************************************
+ convenience wrapper around rpccli_spoolss_EnumPrintProcessorDataTypes
+**********************************************************************/
+
+WERROR rpccli_spoolss_enumprintprocessordatatypes(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ const char *servername,
+ const char *print_processor_name,
+ uint32_t level,
+ uint32_t offered,
+ uint32_t *count,
+ union spoolss_PrintProcDataTypesInfo **info)
+{
+ NTSTATUS status;
+ WERROR werror;
+ uint32_t needed;
+ DATA_BLOB buffer;
+ struct dcerpc_binding_handle *b = cli->binding_handle;
+
+ if (offered > 0) {
+ buffer = data_blob_talloc_zero(mem_ctx, offered);
+ W_ERROR_HAVE_NO_MEMORY(buffer.data);
+ }
+
+ status = dcerpc_spoolss_EnumPrintProcessorDataTypes(b, mem_ctx,
+ servername,
+ print_processor_name,
+ level,
+ (offered > 0) ? &buffer : NULL,
+ offered,
+ count,
+ info,
+ &needed,
+ &werror);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ if (W_ERROR_EQUAL(werror, WERR_INSUFFICIENT_BUFFER)) {
+ offered = needed;
+ buffer = data_blob_talloc_zero(mem_ctx, needed);
+ W_ERROR_HAVE_NO_MEMORY(buffer.data);
+
+ status = dcerpc_spoolss_EnumPrintProcessorDataTypes(b, mem_ctx,
+ servername,
+ print_processor_name,
+ level,
+ (offered > 0) ? &buffer : NULL,
+ offered,
+ count,
+ info,
+ &needed,
+ &werror);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ return werror;
+}
+
+/**********************************************************************
+ convenience wrapper around rpccli_spoolss_EnumPorts
+**********************************************************************/
+
+WERROR rpccli_spoolss_enumports(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ const char *servername,
+ uint32_t level,
+ uint32_t offered,
+ uint32_t *count,
+ union spoolss_PortInfo **info)
+{
+ NTSTATUS status;
+ WERROR werror;
+ uint32_t needed;
+ DATA_BLOB buffer;
+ struct dcerpc_binding_handle *b = cli->binding_handle;
+
+ if (offered > 0) {
+ buffer = data_blob_talloc_zero(mem_ctx, offered);
+ W_ERROR_HAVE_NO_MEMORY(buffer.data);
+ }
+
+ status = dcerpc_spoolss_EnumPorts(b, mem_ctx,
+ servername,
+ level,
+ (offered > 0) ? &buffer : NULL,
+ offered,
+ count,
+ info,
+ &needed,
+ &werror);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ if (W_ERROR_EQUAL(werror, WERR_INSUFFICIENT_BUFFER)) {
+ offered = needed;
+ buffer = data_blob_talloc_zero(mem_ctx, needed);
+ W_ERROR_HAVE_NO_MEMORY(buffer.data);
+
+ status = dcerpc_spoolss_EnumPorts(b, mem_ctx,
+ servername,
+ level,
+ (offered > 0) ? &buffer : NULL,
+ offered,
+ count,
+ info,
+ &needed,
+ &werror);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ return werror;
+}
+
+/**********************************************************************
+ convenience wrapper around rpccli_spoolss_EnumMonitors
+**********************************************************************/
+
+WERROR rpccli_spoolss_enummonitors(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ const char *servername,
+ uint32_t level,
+ uint32_t offered,
+ uint32_t *count,
+ union spoolss_MonitorInfo **info)
+{
+ NTSTATUS status;
+ WERROR werror;
+ uint32_t needed;
+ DATA_BLOB buffer;
+ struct dcerpc_binding_handle *b = cli->binding_handle;
+
+ if (offered > 0) {
+ buffer = data_blob_talloc_zero(mem_ctx, offered);
+ W_ERROR_HAVE_NO_MEMORY(buffer.data);
+ }
+
+ status = dcerpc_spoolss_EnumMonitors(b, mem_ctx,
+ servername,
+ level,
+ (offered > 0) ? &buffer : NULL,
+ offered,
+ count,
+ info,
+ &needed,
+ &werror);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ if (W_ERROR_EQUAL(werror, WERR_INSUFFICIENT_BUFFER)) {
+ offered = needed;
+ buffer = data_blob_talloc_zero(mem_ctx, needed);
+ W_ERROR_HAVE_NO_MEMORY(buffer.data);
+
+ status = dcerpc_spoolss_EnumMonitors(b, mem_ctx,
+ servername,
+ level,
+ (offered > 0) ? &buffer : NULL,
+ offered,
+ count,
+ info,
+ &needed,
+ &werror);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ return werror;
+}
+
+/**********************************************************************
+ convenience wrapper around rpccli_spoolss_EnumJobs
+**********************************************************************/
+
+WERROR rpccli_spoolss_enumjobs(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ uint32_t firstjob,
+ uint32_t numjobs,
+ uint32_t level,
+ uint32_t offered,
+ uint32_t *count,
+ union spoolss_JobInfo **info)
+{
+ NTSTATUS status;
+ WERROR werror;
+ uint32_t needed;
+ DATA_BLOB buffer;
+ struct dcerpc_binding_handle *b = cli->binding_handle;
+
+ if (offered > 0) {
+ buffer = data_blob_talloc_zero(mem_ctx, offered);
+ W_ERROR_HAVE_NO_MEMORY(buffer.data);
+ }
+
+ status = dcerpc_spoolss_EnumJobs(b, mem_ctx,
+ handle,
+ firstjob,
+ numjobs,
+ level,
+ (offered > 0) ? &buffer : NULL,
+ offered,
+ count,
+ info,
+ &needed,
+ &werror);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ if (W_ERROR_EQUAL(werror, WERR_INSUFFICIENT_BUFFER)) {
+ offered = needed;
+ buffer = data_blob_talloc_zero(mem_ctx, needed);
+ W_ERROR_HAVE_NO_MEMORY(buffer.data);
+
+ status = dcerpc_spoolss_EnumJobs(b, mem_ctx,
+ handle,
+ firstjob,
+ numjobs,
+ level,
+ (offered > 0) ? &buffer : NULL,
+ offered,
+ count,
+ info,
+ &needed,
+ &werror);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ return werror;
+}
+
+/**********************************************************************
+ convenience wrapper around rpccli_spoolss_EnumPrinterDrivers
+**********************************************************************/
+
+WERROR rpccli_spoolss_enumprinterdrivers(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ const char *server,
+ const char *environment,
+ uint32_t level,
+ uint32_t offered,
+ uint32_t *count,
+ union spoolss_DriverInfo **info)
+{
+ NTSTATUS status;
+ WERROR werror;
+ uint32_t needed;
+ DATA_BLOB buffer;
+ struct dcerpc_binding_handle *b = cli->binding_handle;
+
+ if (offered > 0) {
+ buffer = data_blob_talloc_zero(mem_ctx, offered);
+ W_ERROR_HAVE_NO_MEMORY(buffer.data);
+ }
+
+ status = dcerpc_spoolss_EnumPrinterDrivers(b, mem_ctx,
+ server,
+ environment,
+ level,
+ (offered > 0) ? &buffer : NULL,
+ offered,
+ count,
+ info,
+ &needed,
+ &werror);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ if (W_ERROR_EQUAL(werror, WERR_INSUFFICIENT_BUFFER)) {
+ offered = needed;
+ buffer = data_blob_talloc_zero(mem_ctx, needed);
+ W_ERROR_HAVE_NO_MEMORY(buffer.data);
+
+ status = dcerpc_spoolss_EnumPrinterDrivers(b, mem_ctx,
+ server,
+ environment,
+ level,
+ (offered > 0) ? &buffer : NULL,
+ offered,
+ count,
+ info,
+ &needed,
+ &werror);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ return werror;
+}
+
+/**********************************************************************
+ convenience wrapper around rpccli_spoolss_EnumPrinters
+**********************************************************************/
+
+WERROR rpccli_spoolss_enumprinters(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ uint32_t flags,
+ const char *server,
+ uint32_t level,
+ uint32_t offered,
+ uint32_t *count,
+ union spoolss_PrinterInfo **info)
+{
+ NTSTATUS status;
+ WERROR werror;
+ uint32_t needed;
+ DATA_BLOB buffer;
+ struct dcerpc_binding_handle *b = cli->binding_handle;
+
+ if (offered > 0) {
+ buffer = data_blob_talloc_zero(mem_ctx, offered);
+ W_ERROR_HAVE_NO_MEMORY(buffer.data);
+ }
+
+ status = dcerpc_spoolss_EnumPrinters(b, mem_ctx,
+ flags,
+ server,
+ level,
+ (offered > 0) ? &buffer : NULL,
+ offered,
+ count,
+ info,
+ &needed,
+ &werror);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ if (W_ERROR_EQUAL(werror, WERR_INSUFFICIENT_BUFFER)) {
+ offered = needed;
+ buffer = data_blob_talloc_zero(mem_ctx, needed);
+ W_ERROR_HAVE_NO_MEMORY(buffer.data);
+
+ status = dcerpc_spoolss_EnumPrinters(b, mem_ctx,
+ flags,
+ server,
+ level,
+ (offered > 0) ? &buffer : NULL,
+ offered,
+ count,
+ info,
+ &needed,
+ &werror);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ return werror;
+}
+
+/**********************************************************************
+ convenience wrapper around rpccli_spoolss_GetPrinterData
+**********************************************************************/
+
+WERROR rpccli_spoolss_getprinterdata(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ const char *value_name,
+ uint32_t offered,
+ enum winreg_Type *type,
+ uint32_t *needed_p,
+ uint8_t **data_p)
+{
+ NTSTATUS status;
+ WERROR werror;
+ uint32_t needed;
+ uint8_t *data;
+ struct dcerpc_binding_handle *b = cli->binding_handle;
+
+ data = talloc_zero_array(mem_ctx, uint8_t, offered);
+ W_ERROR_HAVE_NO_MEMORY(data);
+
+ status = dcerpc_spoolss_GetPrinterData(b, mem_ctx,
+ handle,
+ value_name,
+ type,
+ data,
+ offered,
+ &needed,
+ &werror);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ if (W_ERROR_EQUAL(werror, WERR_MORE_DATA)) {
+ offered = needed;
+ data = talloc_zero_array(mem_ctx, uint8_t, offered);
+ W_ERROR_HAVE_NO_MEMORY(data);
+
+ status = dcerpc_spoolss_GetPrinterData(b, mem_ctx,
+ handle,
+ value_name,
+ type,
+ data,
+ offered,
+ &needed,
+ &werror);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ *data_p = data;
+ *needed_p = needed;
+
+ return werror;
+}
+
+/**********************************************************************
+ convenience wrapper around rpccli_spoolss_EnumPrinterKey
+**********************************************************************/
+
+WERROR rpccli_spoolss_enumprinterkey(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ const char *key_name,
+ const char ***key_buffer,
+ uint32_t offered)
+{
+ NTSTATUS status;
+ WERROR werror;
+ uint32_t needed;
+ union spoolss_KeyNames _key_buffer;
+ uint32_t _ndr_size;
+ struct dcerpc_binding_handle *b = cli->binding_handle;
+
+ status = dcerpc_spoolss_EnumPrinterKey(b, mem_ctx,
+ handle,
+ key_name,
+ &_ndr_size,
+ &_key_buffer,
+ offered,
+ &needed,
+ &werror);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ if (W_ERROR_EQUAL(werror, WERR_MORE_DATA)) {
+ offered = needed;
+ status = dcerpc_spoolss_EnumPrinterKey(b, mem_ctx,
+ handle,
+ key_name,
+ &_ndr_size,
+ &_key_buffer,
+ offered,
+ &needed,
+ &werror);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ *key_buffer = _key_buffer.string_array;
+
+ return werror;
+}
+
+/**********************************************************************
+ convenience wrapper around rpccli_spoolss_EnumPrinterDataEx
+**********************************************************************/
+
+WERROR rpccli_spoolss_enumprinterdataex(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ const char *key_name,
+ uint32_t offered,
+ uint32_t *count,
+ struct spoolss_PrinterEnumValues **info)
+{
+ NTSTATUS status;
+ WERROR werror;
+ uint32_t needed;
+ struct dcerpc_binding_handle *b = cli->binding_handle;
+
+ status = dcerpc_spoolss_EnumPrinterDataEx(b, mem_ctx,
+ handle,
+ key_name,
+ offered,
+ count,
+ info,
+ &needed,
+ &werror);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ if (W_ERROR_EQUAL(werror, WERR_MORE_DATA)) {
+ offered = needed;
+
+ status = dcerpc_spoolss_EnumPrinterDataEx(b, mem_ctx,
+ handle,
+ key_name,
+ offered,
+ count,
+ info,
+ &needed,
+ &werror);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ return werror;
+}
diff --git a/source3/rpc_client/cli_spoolss.h b/source3/rpc_client/cli_spoolss.h
new file mode 100644
index 0000000..9883874
--- /dev/null
+++ b/source3/rpc_client/cli_spoolss.h
@@ -0,0 +1,153 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Gerald Carter 2001-2005,
+ Copyright (C) Tim Potter 2000-2002,
+ Copyright (C) Andrew Tridgell 1994-2000,
+ Copyright (C) Jean-Francois Micouleau 1999-2000.
+ Copyright (C) Jeremy Allison 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 _RPC_CLIENT_CLI_SPOOLSS_H_
+#define _RPC_CLIENT_CLI_SPOOLSS_H_
+
+/* The following definitions come from rpc_client/cli_spoolss.c */
+
+WERROR rpccli_spoolss_openprinter_ex(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ const char *printername,
+ uint32_t access_desired,
+ struct policy_handle *handle);
+WERROR rpccli_spoolss_getprinterdriver(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ const char *architecture,
+ uint32_t level,
+ uint32_t offered,
+ union spoolss_DriverInfo *info);
+WERROR rpccli_spoolss_getprinterdriver2(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ const char *architecture,
+ uint32_t level,
+ uint32_t offered,
+ uint32_t client_major_version,
+ uint32_t client_minor_version,
+ union spoolss_DriverInfo *info,
+ uint32_t *server_major_version,
+ uint32_t *server_minor_version);
+WERROR rpccli_spoolss_addprinterex(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ struct spoolss_SetPrinterInfoCtr *info_ctr);
+WERROR rpccli_spoolss_getprinter(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ uint32_t level,
+ uint32_t offered,
+ union spoolss_PrinterInfo *info);
+WERROR rpccli_spoolss_getjob(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ uint32_t job_id,
+ uint32_t level,
+ uint32_t offered,
+ union spoolss_JobInfo *info);
+WERROR rpccli_spoolss_enumforms(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ uint32_t level,
+ uint32_t offered,
+ uint32_t *count,
+ union spoolss_FormInfo **info);
+WERROR rpccli_spoolss_enumprintprocessors(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ const char *servername,
+ const char *environment,
+ uint32_t level,
+ uint32_t offered,
+ uint32_t *count,
+ union spoolss_PrintProcessorInfo **info);
+WERROR rpccli_spoolss_enumprintprocessordatatypes(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ const char *servername,
+ const char *print_processor_name,
+ uint32_t level,
+ uint32_t offered,
+ uint32_t *count,
+ union spoolss_PrintProcDataTypesInfo **info);
+WERROR rpccli_spoolss_enumports(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ const char *servername,
+ uint32_t level,
+ uint32_t offered,
+ uint32_t *count,
+ union spoolss_PortInfo **info);
+WERROR rpccli_spoolss_enummonitors(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ const char *servername,
+ uint32_t level,
+ uint32_t offered,
+ uint32_t *count,
+ union spoolss_MonitorInfo **info);
+WERROR rpccli_spoolss_enumjobs(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ uint32_t firstjob,
+ uint32_t numjobs,
+ uint32_t level,
+ uint32_t offered,
+ uint32_t *count,
+ union spoolss_JobInfo **info);
+WERROR rpccli_spoolss_enumprinterdrivers(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ const char *server,
+ const char *environment,
+ uint32_t level,
+ uint32_t offered,
+ uint32_t *count,
+ union spoolss_DriverInfo **info);
+WERROR rpccli_spoolss_enumprinters(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ uint32_t flags,
+ const char *server,
+ uint32_t level,
+ uint32_t offered,
+ uint32_t *count,
+ union spoolss_PrinterInfo **info);
+WERROR rpccli_spoolss_getprinterdata(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ const char *value_name,
+ uint32_t offered,
+ enum winreg_Type *type,
+ uint32_t *needed_p,
+ uint8_t **data_p);
+WERROR rpccli_spoolss_enumprinterkey(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ const char *key_name,
+ const char ***key_buffer,
+ uint32_t offered);
+WERROR rpccli_spoolss_enumprinterdataex(struct rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ const char *key_name,
+ uint32_t offered,
+ uint32_t *count,
+ struct spoolss_PrinterEnumValues **info);
+
+#endif /* _RPC_CLIENT_CLI_SPOOLSS_H_ */
diff --git a/source3/rpc_client/cli_winreg.c b/source3/rpc_client/cli_winreg.c
new file mode 100644
index 0000000..a360d1e
--- /dev/null
+++ b/source3/rpc_client/cli_winreg.c
@@ -0,0 +1,975 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * WINREG client routines
+ *
+ * Copyright (c) 2011 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "../librpc/gen_ndr/ndr_winreg_c.h"
+#include "../librpc/gen_ndr/ndr_security.h"
+#include "rpc_client/cli_winreg.h"
+#include "../libcli/registry/util_reg.h"
+
+NTSTATUS dcerpc_winreg_query_dword(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_handle,
+ const char *value,
+ uint32_t *data,
+ WERROR *pwerr)
+{
+ struct winreg_String wvalue;
+ enum winreg_Type type = REG_NONE;
+ uint32_t value_len = 0;
+ uint32_t data_size = 0;
+ NTSTATUS status;
+ DATA_BLOB blob;
+
+ wvalue.name = value;
+
+ status = dcerpc_winreg_QueryValue(h,
+ mem_ctx,
+ key_handle,
+ &wvalue,
+ &type,
+ NULL,
+ &data_size,
+ &value_len,
+ pwerr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!W_ERROR_IS_OK(*pwerr)) {
+ return status;
+ }
+
+ if (type != REG_DWORD) {
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ if (data_size != 4) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ blob = data_blob_talloc_zero(mem_ctx, data_size);
+ if (blob.data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ value_len = 0;
+
+ status = dcerpc_winreg_QueryValue(h,
+ mem_ctx,
+ key_handle,
+ &wvalue,
+ &type,
+ blob.data,
+ &data_size,
+ &value_len,
+ pwerr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!W_ERROR_IS_OK(*pwerr)) {
+ return status;
+ }
+
+ if (data) {
+ *data = IVAL(blob.data, 0);
+ }
+
+ return status;
+}
+
+NTSTATUS dcerpc_winreg_query_binary(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_handle,
+ const char *value,
+ DATA_BLOB *data,
+ WERROR *pwerr)
+{
+ struct winreg_String wvalue;
+ enum winreg_Type type = REG_NONE;
+ uint32_t value_len = 0;
+ uint32_t data_size = 0;
+ NTSTATUS status;
+ DATA_BLOB blob;
+
+ ZERO_STRUCT(wvalue);
+ wvalue.name = value;
+
+ status = dcerpc_winreg_QueryValue(h,
+ mem_ctx,
+ key_handle,
+ &wvalue,
+ &type,
+ NULL,
+ &data_size,
+ &value_len,
+ pwerr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!W_ERROR_IS_OK(*pwerr)) {
+ return status;
+ }
+
+ if (type != REG_BINARY) {
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ blob = data_blob_talloc_zero(mem_ctx, data_size);
+ if (blob.data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ value_len = 0;
+
+ status = dcerpc_winreg_QueryValue(h,
+ mem_ctx,
+ key_handle,
+ &wvalue,
+ &type,
+ blob.data,
+ &data_size,
+ &value_len,
+ pwerr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!W_ERROR_IS_OK(*pwerr)) {
+ return status;
+ }
+
+ if (data) {
+ data->data = blob.data;
+ data->length = blob.length;
+ }
+
+ return status;
+}
+
+NTSTATUS dcerpc_winreg_query_multi_sz(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_handle,
+ const char *value,
+ const char ***data,
+ WERROR *pwerr)
+{
+ struct winreg_String wvalue;
+ enum winreg_Type type = REG_NONE;
+ uint32_t value_len = 0;
+ uint32_t data_size = 0;
+ NTSTATUS status;
+ DATA_BLOB blob;
+
+ wvalue.name = value;
+
+ status = dcerpc_winreg_QueryValue(h,
+ mem_ctx,
+ key_handle,
+ &wvalue,
+ &type,
+ NULL,
+ &data_size,
+ &value_len,
+ pwerr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!W_ERROR_IS_OK(*pwerr)) {
+ return status;
+ }
+
+ if (type != REG_MULTI_SZ) {
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ blob = data_blob_talloc_zero(mem_ctx, data_size);
+ if (blob.data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ value_len = 0;
+
+ status = dcerpc_winreg_QueryValue(h,
+ mem_ctx,
+ key_handle,
+ &wvalue,
+ &type,
+ blob.data,
+ &data_size,
+ &value_len,
+ pwerr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!W_ERROR_IS_OK(*pwerr)) {
+ return status;
+ }
+
+ if (data) {
+ bool ok;
+
+ ok = pull_reg_multi_sz(mem_ctx, &blob, data);
+ if (!ok) {
+ status = NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ return status;
+}
+
+NTSTATUS dcerpc_winreg_query_sz(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_handle,
+ const char *value,
+ const char **data,
+ WERROR *pwerr)
+{
+ struct winreg_String wvalue;
+ enum winreg_Type type = REG_NONE;
+ uint32_t value_len = 0;
+ uint32_t data_size = 0;
+ NTSTATUS status;
+ DATA_BLOB blob;
+
+ wvalue.name = value;
+
+ status = dcerpc_winreg_QueryValue(h,
+ mem_ctx,
+ key_handle,
+ &wvalue,
+ &type,
+ NULL,
+ &data_size,
+ &value_len,
+ pwerr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!W_ERROR_IS_OK(*pwerr)) {
+ return status;
+ }
+
+ if (type != REG_SZ) {
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ blob = data_blob_talloc_zero(mem_ctx, data_size);
+ if (blob.data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ value_len = 0;
+
+ status = dcerpc_winreg_QueryValue(h,
+ mem_ctx,
+ key_handle,
+ &wvalue,
+ &type,
+ blob.data,
+ &data_size,
+ &value_len,
+ pwerr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!W_ERROR_IS_OK(*pwerr)) {
+ return status;
+ }
+
+ if (data) {
+ bool ok;
+
+ ok = pull_reg_sz(mem_ctx, &blob, data);
+ if (!ok) {
+ status = NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ return status;
+}
+
+NTSTATUS dcerpc_winreg_query_sd(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_handle,
+ const char *value,
+ struct security_descriptor **data,
+ WERROR *pwerr)
+{
+ NTSTATUS status;
+ DATA_BLOB blob;
+
+ status = dcerpc_winreg_query_binary(mem_ctx,
+ h,
+ key_handle,
+ value,
+ &blob,
+ pwerr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!W_ERROR_IS_OK(*pwerr)) {
+ return status;
+ }
+
+ if (data) {
+ struct security_descriptor *sd;
+ enum ndr_err_code ndr_err;
+
+ sd = talloc_zero(mem_ctx, struct security_descriptor);
+ if (sd == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ndr_err = ndr_pull_struct_blob(&blob,
+ sd,
+ sd,
+ (ndr_pull_flags_fn_t) ndr_pull_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(2, ("dcerpc_winreg_query_sd: Failed to marshall "
+ "security descriptor\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *data = sd;
+ }
+
+ return status;
+}
+
+NTSTATUS dcerpc_winreg_set_dword(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_handle,
+ const char *value,
+ uint32_t data,
+ WERROR *pwerr)
+{
+ struct winreg_String wvalue;
+ DATA_BLOB blob;
+ NTSTATUS status;
+
+ ZERO_STRUCT(wvalue);
+ wvalue.name = value;
+ blob = data_blob_talloc_zero(mem_ctx, 4);
+ SIVAL(blob.data, 0, data);
+
+ status = dcerpc_winreg_SetValue(h,
+ mem_ctx,
+ key_handle,
+ wvalue,
+ REG_DWORD,
+ blob.data,
+ blob.length,
+ pwerr);
+
+ return status;
+}
+
+NTSTATUS dcerpc_winreg_set_sz(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_handle,
+ const char *value,
+ const char *data,
+ WERROR *pwerr)
+{
+ struct winreg_String wvalue = { 0, };
+ DATA_BLOB blob;
+ NTSTATUS status;
+
+ wvalue.name = value;
+ if (data == NULL) {
+ blob = data_blob_string_const("");
+ } else {
+ if (!push_reg_sz(mem_ctx, &blob, data)) {
+ DEBUG(2, ("dcerpc_winreg_set_sz: Could not marshall "
+ "string %s for %s\n",
+ data, wvalue.name));
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ status = dcerpc_winreg_SetValue(h,
+ mem_ctx,
+ key_handle,
+ wvalue,
+ REG_SZ,
+ blob.data,
+ blob.length,
+ pwerr);
+
+ return status;
+}
+
+NTSTATUS dcerpc_winreg_set_expand_sz(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_handle,
+ const char *value,
+ const char *data,
+ WERROR *pwerr)
+{
+ struct winreg_String wvalue = { 0, };
+ DATA_BLOB blob;
+ NTSTATUS status;
+
+ wvalue.name = value;
+ if (data == NULL) {
+ blob = data_blob_string_const("");
+ } else {
+ if (!push_reg_sz(mem_ctx, &blob, data)) {
+ DEBUG(2, ("dcerpc_winreg_set_expand_sz: Could not marshall "
+ "string %s for %s\n",
+ data, wvalue.name));
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ status = dcerpc_winreg_SetValue(h,
+ mem_ctx,
+ key_handle,
+ wvalue,
+ REG_EXPAND_SZ,
+ blob.data,
+ blob.length,
+ pwerr);
+
+ return status;
+}
+
+NTSTATUS dcerpc_winreg_set_multi_sz(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_handle,
+ const char *value,
+ const char **data,
+ WERROR *pwerr)
+{
+ struct winreg_String wvalue = { 0, };
+ DATA_BLOB blob;
+ NTSTATUS status;
+
+ wvalue.name = value;
+ if (!push_reg_multi_sz(mem_ctx, &blob, data)) {
+ DEBUG(2, ("dcerpc_winreg_set_multi_sz: Could not marshall "
+ "string multi sz for %s\n",
+ wvalue.name));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dcerpc_winreg_SetValue(h,
+ mem_ctx,
+ key_handle,
+ wvalue,
+ REG_MULTI_SZ,
+ blob.data,
+ blob.length,
+ pwerr);
+
+ return status;
+}
+
+NTSTATUS dcerpc_winreg_set_binary(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_handle,
+ const char *value,
+ DATA_BLOB *data,
+ WERROR *pwerr)
+{
+ struct winreg_String wvalue = { 0, };
+ NTSTATUS status;
+
+ wvalue.name = value;
+
+ status = dcerpc_winreg_SetValue(h,
+ mem_ctx,
+ key_handle,
+ wvalue,
+ REG_BINARY,
+ data->data,
+ data->length,
+ pwerr);
+
+ return status;
+}
+
+NTSTATUS dcerpc_winreg_set_sd(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_handle,
+ const char *value,
+ const struct security_descriptor *data,
+ WERROR *pwerr)
+{
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+
+ ndr_err = ndr_push_struct_blob(&blob,
+ mem_ctx,
+ data,
+ (ndr_push_flags_fn_t) ndr_push_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(2, ("dcerpc_winreg_set_sd: Failed to marshall security "
+ "descriptor\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return dcerpc_winreg_set_binary(mem_ctx,
+ h,
+ key_handle,
+ value,
+ &blob,
+ pwerr);
+}
+
+NTSTATUS dcerpc_winreg_add_multi_sz(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_handle,
+ const char *value,
+ const char *data,
+ WERROR *pwerr)
+{
+ const char **a = NULL;
+ const char **p;
+ uint32_t i;
+ NTSTATUS status;
+
+ status = dcerpc_winreg_query_multi_sz(mem_ctx,
+ h,
+ key_handle,
+ value,
+ &a,
+ pwerr);
+
+ /* count the elements */
+ for (p = a, i = 0; p && *p; p++, i++);
+
+ p = talloc_realloc(mem_ctx, a, const char *, i + 2);
+ if (p == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ p[i] = data;
+ p[i + 1] = NULL;
+
+ status = dcerpc_winreg_set_multi_sz(mem_ctx,
+ h,
+ key_handle,
+ value,
+ p,
+ pwerr);
+
+ return status;
+}
+
+NTSTATUS dcerpc_winreg_enum_keys(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_hnd,
+ uint32_t *pnum_subkeys,
+ const char ***psubkeys,
+ WERROR *pwerr)
+{
+ const char **subkeys;
+ uint32_t num_subkeys, max_subkeylen, max_classlen;
+ uint32_t num_values, max_valnamelen, max_valbufsize;
+ uint32_t i;
+ NTTIME last_changed_time;
+ uint32_t secdescsize;
+ struct winreg_String classname;
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCT(classname);
+
+ status = dcerpc_winreg_QueryInfoKey(h,
+ tmp_ctx,
+ key_hnd,
+ &classname,
+ &num_subkeys,
+ &max_subkeylen,
+ &max_classlen,
+ &num_values,
+ &max_valnamelen,
+ &max_valbufsize,
+ &secdescsize,
+ &last_changed_time,
+ pwerr);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto error;
+ }
+ if (!W_ERROR_IS_OK(*pwerr)) {
+ goto error;
+ }
+
+ subkeys = talloc_zero_array(tmp_ctx, const char *, num_subkeys + 2);
+ if (subkeys == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto error;
+ }
+
+ if (num_subkeys == 0) {
+ subkeys[0] = talloc_strdup(subkeys, "");
+ if (subkeys[0] == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto error;
+ }
+ *pnum_subkeys = 0;
+ if (psubkeys) {
+ *psubkeys = talloc_move(mem_ctx, &subkeys);
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_OK;
+ }
+
+ for (i = 0; i < num_subkeys; i++) {
+ char c = '\0';
+ char n = '\0';
+ char *name = NULL;
+ struct winreg_StringBuf class_buf;
+ struct winreg_StringBuf name_buf;
+ NTTIME modtime;
+
+ class_buf.name = &c;
+ class_buf.size = max_classlen + 2;
+ class_buf.length = 0;
+
+ name_buf.name = &n;
+ name_buf.size = max_subkeylen + 2;
+ name_buf.length = 0;
+
+ ZERO_STRUCT(modtime);
+
+ status = dcerpc_winreg_EnumKey(h,
+ tmp_ctx,
+ key_hnd,
+ i,
+ &name_buf,
+ &class_buf,
+ &modtime,
+ pwerr);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5, ("dcerpc_winreg_enum_keys: Could not enumerate keys: %s\n",
+ nt_errstr(status)));
+ goto error;
+ }
+
+ if (W_ERROR_EQUAL(*pwerr, WERR_NO_MORE_ITEMS)) {
+ *pwerr = WERR_OK;
+ break;
+ }
+ if (!W_ERROR_IS_OK(*pwerr)) {
+ DEBUG(5, ("dcerpc_winreg_enum_keys: Could not enumerate keys: %s\n",
+ win_errstr(*pwerr)));
+ goto error;
+ }
+
+ if (name_buf.name == NULL) {
+ *pwerr = WERR_INVALID_PARAMETER;
+ goto error;
+ }
+
+ name = talloc_strdup(subkeys, name_buf.name);
+ if (name == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto error;
+ }
+
+ subkeys[i] = name;
+ }
+
+ *pnum_subkeys = num_subkeys;
+ if (psubkeys) {
+ *psubkeys = talloc_move(mem_ctx, &subkeys);
+ }
+
+ error:
+ TALLOC_FREE(tmp_ctx);
+
+ return status;
+}
+
+NTSTATUS dcerpc_winreg_enumvals(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_hnd,
+ uint32_t *pnum_values,
+ const char ***pnames,
+ enum winreg_Type **_type,
+ DATA_BLOB **pdata,
+ WERROR *pwerr)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ uint32_t num_subkeys = 0, max_subkeylen = 0, max_classlen = 0;
+ uint32_t num_values = 0, max_valnamelen = 0, max_valbufsize = 0;
+ uint32_t secdescsize = 0;
+ uint32_t i;
+ NTTIME last_changed_time = 0;
+ struct winreg_String classname = { .name = NULL };
+
+ const char **enum_names = NULL;
+ enum winreg_Type *enum_types = NULL;
+ DATA_BLOB *enum_data_blobs = NULL;
+
+
+ WERROR result = WERR_OK;
+ NTSTATUS status;
+
+ status = dcerpc_winreg_QueryInfoKey(h,
+ tmp_ctx,
+ key_hnd,
+ &classname,
+ &num_subkeys,
+ &max_subkeylen,
+ &max_classlen,
+ &num_values,
+ &max_valnamelen,
+ &max_valbufsize,
+ &secdescsize,
+ &last_changed_time,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("dcerpc_winreg_enumvals: Could not query info: %s\n",
+ nt_errstr(status)));
+ goto error;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("dcerpc_winreg_enumvals: Could not query info: %s\n",
+ win_errstr(result)));
+ *pwerr = result;
+ goto error;
+ }
+
+ if (num_values == 0) {
+ *pnum_values = 0;
+ TALLOC_FREE(tmp_ctx);
+ *pwerr = WERR_OK;
+ return status;
+ }
+
+ enum_names = talloc_zero_array(tmp_ctx, const char *, num_values);
+
+ if (enum_names == NULL) {
+ *pwerr = WERR_NOT_ENOUGH_MEMORY;
+ goto error;
+ }
+
+ enum_types = talloc_zero_array(tmp_ctx, enum winreg_Type, num_values);
+
+ if (enum_types == NULL) {
+ *pwerr = WERR_NOT_ENOUGH_MEMORY;
+ goto error;
+ }
+
+ enum_data_blobs = talloc_zero_array(tmp_ctx, DATA_BLOB, num_values);
+
+ if (enum_data_blobs == NULL) {
+ *pwerr = WERR_NOT_ENOUGH_MEMORY;
+ goto error;
+ }
+
+ for (i = 0; i < num_values; i++) {
+ const char *name;
+ struct winreg_ValNameBuf name_buf;
+ enum winreg_Type type = REG_NONE;
+ uint8_t *data;
+ uint32_t data_size;
+ uint32_t length;
+ char n = '\0';
+
+
+ name_buf.name = &n;
+ name_buf.size = max_valnamelen + 2;
+ name_buf.length = 0;
+
+ data_size = max_valbufsize;
+ data = NULL;
+ if (data_size) {
+ data = (uint8_t *) TALLOC(tmp_ctx, data_size);
+ }
+ length = 0;
+
+ status = dcerpc_winreg_EnumValue(h,
+ tmp_ctx,
+ key_hnd,
+ i,
+ &name_buf,
+ &type,
+ data,
+ data_size ? &data_size : NULL,
+ &length,
+ &result);
+ if (W_ERROR_EQUAL(result, WERR_NO_MORE_ITEMS) ) {
+ result = WERR_OK;
+ status = NT_STATUS_OK;
+ break;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("dcerpc_winreg_enumvals: Could not enumerate values: %s\n",
+ nt_errstr(status)));
+ goto error;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("dcerpc_winreg_enumvals: Could not enumerate values: %s\n",
+ win_errstr(result)));
+ *pwerr = result;
+ goto error;
+ }
+
+ if (name_buf.name == NULL) {
+ result = WERR_INVALID_PARAMETER;
+ *pwerr = result;
+ goto error;
+ }
+
+ name = talloc_strdup(enum_names, name_buf.name);
+ if (name == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ *pwerr = result;
+ goto error;
+ }
+ /* place name, type and datablob in the enum return params */
+
+ enum_data_blobs[i] = data_blob_talloc(enum_data_blobs, data, length);
+ enum_names[i] = name;
+ enum_types[i] = type;
+
+ }
+ /* move to the main mem context */
+ *pnum_values = num_values;
+ if (pnames) {
+ *pnames = talloc_move(mem_ctx, &enum_names);
+ }
+ /* can this fail in any way? */
+ if (_type) {
+ *_type = talloc_move(mem_ctx, &enum_types);
+ }
+
+ if (pdata){
+ *pdata = talloc_move(mem_ctx, &enum_data_blobs);
+ }
+
+
+ result = WERR_OK;
+
+ error:
+ TALLOC_FREE(tmp_ctx);
+ *pwerr = result;
+
+ return status;
+}
+
+NTSTATUS dcerpc_winreg_delete_subkeys_recursive(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *hive_handle,
+ uint32_t access_mask,
+ const char *key,
+ WERROR *pwerr)
+{
+ const char **subkeys = NULL;
+ uint32_t num_subkeys = 0;
+ struct policy_handle key_hnd;
+ struct winreg_String wkey = { 0, };
+ WERROR result = WERR_OK;
+ NTSTATUS status;
+ uint32_t i;
+
+ ZERO_STRUCT(key_hnd);
+ wkey.name = key;
+
+ DEBUG(2, ("dcerpc_winreg_delete_subkeys_recursive: delete key %s\n", key));
+ /* open the key */
+ status = dcerpc_winreg_OpenKey(h,
+ mem_ctx,
+ hive_handle,
+ wkey,
+ 0,
+ access_mask,
+ &key_hnd,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("dcerpc_winreg_delete_subkeys_recursive: Could not open key %s: %s\n",
+ wkey.name, nt_errstr(status)));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("dcerpc_winreg_delete_subkeys_recursive: Could not open key %s: %s\n",
+ wkey.name, win_errstr(result)));
+ *pwerr = result;
+ goto done;
+ }
+
+ status = dcerpc_winreg_enum_keys(mem_ctx,
+ h,
+ &key_hnd,
+ &num_subkeys,
+ &subkeys,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ for (i = 0; i < num_subkeys; i++) {
+ /* create key + subkey */
+ char *subkey = talloc_asprintf(mem_ctx, "%s\\%s", key, subkeys[i]);
+ if (subkey == NULL) {
+ goto done;
+ }
+
+ DEBUG(2, ("dcerpc_winreg_delete_subkeys_recursive: delete subkey %s\n", subkey));
+ status = dcerpc_winreg_delete_subkeys_recursive(mem_ctx,
+ h,
+ hive_handle,
+ access_mask,
+ subkey,
+ &result);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ if (is_valid_policy_hnd(&key_hnd)) {
+ WERROR ignore;
+ dcerpc_winreg_CloseKey(h, mem_ctx, &key_hnd, &ignore);
+ }
+
+ wkey.name = key;
+
+ status = dcerpc_winreg_DeleteKey(h,
+ mem_ctx,
+ hive_handle,
+ wkey,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ *pwerr = result;
+ goto done;
+ }
+
+done:
+ if (is_valid_policy_hnd(&key_hnd)) {
+ WERROR ignore;
+
+ dcerpc_winreg_CloseKey(h, mem_ctx, &key_hnd, &ignore);
+ }
+
+ *pwerr = result;
+ return status;
+}
+
+/* vim: set ts=8 sw=8 noet cindent syntax=c.doxygen: */
diff --git a/source3/rpc_client/cli_winreg.h b/source3/rpc_client/cli_winreg.h
new file mode 100644
index 0000000..c8dc3f6
--- /dev/null
+++ b/source3/rpc_client/cli_winreg.h
@@ -0,0 +1,449 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * WINREG client routines
+ *
+ * Copyright (c) 2011 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CLI_WINREG_H
+#define CLI_WINREG_H
+
+/**
+ * @brief Query a key for the specified dword value.
+ *
+ * Get the data that is associated with the named value of a specified registry
+ * open key. This function ensures that the key is a dword and converts it
+ * correctly.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] h The binding handle for the rpc connection.
+ *
+ * @param[in] key_handle A handle to a key that MUST have been opened
+ * previously.
+ *
+ * @param[in] value The name of the value to query.
+ *
+ * @param[out] data A pointer to store the data of the value.
+ *
+ * @param[out] pwerr A pointer to a WERROR to store result of the query.
+ *
+ * @return NT_STATUS_OK on success or a corresponding error if
+ * there was a problem on the connection.
+ */
+NTSTATUS dcerpc_winreg_query_dword(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_handle,
+ const char *value,
+ uint32_t *data,
+ WERROR *pwerr);
+
+/**
+ * @brief Query a key for the specified binary value.
+ *
+ * Get the data that is associated with the named value of a specified registry
+ * open key. This function ensures that the key is a binary value.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] h The binding handle for the rpc connection.
+ *
+ * @param[in] key_handle A handle to a key that MUST have been opened
+ * previously.
+ *
+ * @param[in] value The name of the value to query.
+ *
+ * @param[out] data A pointer to store the data of the value.
+ *
+ * @param[out] pwerr A pointer to a WERROR to store result of the query.
+ *
+ * @return NT_STATUS_OK on success or a corresponding error if
+ * there was a problem on the connection.
+ */
+NTSTATUS dcerpc_winreg_query_binary(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_handle,
+ const char *value,
+ DATA_BLOB *data,
+ WERROR *pwerr);
+
+/**
+ * @brief Query a key for the specified multi sz value.
+ *
+ * Get the data that is associated with the named value of a specified registry
+ * open key. This function ensures that the key is a multi sz value.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] h The binding handle for the rpc connection.
+ *
+ * @param[in] key_handle A handle to a key that MUST have been opened
+ * previously.
+ *
+ * @param[in] value The name of the value to query.
+ *
+ * @param[out] data A pointer to store the data of the value.
+ *
+ * @param[out] pwerr A pointer to a WERROR to store result of the query.
+ *
+ * @return NT_STATUS_OK on success or a corresponding error if
+ * there was a problem on the connection.
+ */
+NTSTATUS dcerpc_winreg_query_multi_sz(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_handle,
+ const char *value,
+ const char ***data,
+ WERROR *pwerr);
+
+/**
+ * @brief Query a key for the specified sz value.
+ *
+ * Get the data that is associated with the named value of a specified registry
+ * open key. This function ensures that the key is a multi sz value.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] h The binding handle for the rpc connection.
+ *
+ * @param[in] key_handle A handle to a key that MUST have been opened
+ * previously.
+ *
+ * @param[in] value The name of the value to query.
+ *
+ * @param[out] data A pointer to store the data of the value.
+ *
+ * @param[out] pwerr A pointer to a WERROR to store result of the query.
+ *
+ * @return NT_STATUS_OK on success or a corresponding error if
+ * there was a problem on the connection.
+ */
+NTSTATUS dcerpc_winreg_query_sz(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_handle,
+ const char *value,
+ const char **data,
+ WERROR *pwerr);
+
+/**
+ * @brief Query a key for the specified security descriptor.
+ *
+ * Get the data that is associated with the named value of a specified registry
+ * open key. This function ensures that the key is a security descriptor.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] h The binding handle for the rpc connection.
+ *
+ * @param[in] key_handle A handle to a key that MUST have been opened
+ * previously.
+ *
+ * @param[in] value The name of the value to query.
+ *
+ * @param[out] data A pointer to store the data of the value.
+ *
+ * @param[out] pwerr A pointer to a WERROR to store result of the query.
+ *
+ * @return NT_STATUS_OK on success or a corresponding error if
+ * there was a problem on the connection.
+ */
+NTSTATUS dcerpc_winreg_query_sd(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_handle,
+ const char *value,
+ struct security_descriptor **data,
+ WERROR *pwerr);
+
+/**
+ * @brief Set a value with the specified dword data.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] h The binding handle for the rpc connection.
+ *
+ * @param[in] key_handle A handle to a key that MUST have been opened
+ * previously.
+ *
+ * @param[in] value The name of the value to set.
+ *
+ * @param[in] data The data to store in the value.
+ *
+ * @param[out] pwerr A pointer to a WERROR to store result of the query.
+ *
+ * @return NT_STATUS_OK on success or a corresponding error if
+ * there was a problem on the connection.
+ */
+NTSTATUS dcerpc_winreg_set_dword(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_handle,
+ const char *value,
+ uint32_t data,
+ WERROR *pwerr);
+
+/**
+ * @brief Set a value with the specified sz data.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] h The binding handle for the rpc connection.
+ *
+ * @param[in] key_handle A handle to a key that MUST have been opened
+ * previously.
+ *
+ * @param[in] value The name of the value to set.
+ *
+ * @param[in] data The data to store in the value.
+ *
+ * @param[out] pwerr A pointer to a WERROR to store result of the query.
+ *
+ * @return NT_STATUS_OK on success or a corresponding error if
+ * there was a problem on the connection.
+ */
+NTSTATUS dcerpc_winreg_set_sz(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_handle,
+ const char *value,
+ const char *data,
+ WERROR *pwerr);
+
+/**
+ * @brief Set a value with the specified expand sz data.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] h The binding handle for the rpc connection.
+ *
+ * @param[in] key_handle A handle to a key that MUST have been opened
+ * previously.
+ *
+ * @param[in] value The name of the value to set.
+ *
+ * @param[in] data The data to store in the value.
+ *
+ * @param[out] pwerr A pointer to a WERROR to store result of the query.
+ *
+ * @return NT_STATUS_OK on success or a corresponding error if
+ * there was a problem on the connection.
+ */
+NTSTATUS dcerpc_winreg_set_expand_sz(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_handle,
+ const char *value,
+ const char *data,
+ WERROR *pwerr);
+
+/**
+ * @brief Set a value with the specified multi sz data.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] h The binding handle for the rpc connection.
+ *
+ * @param[in] key_handle A handle to a key that MUST have been opened
+ * previously.
+ *
+ * @param[in] value The name of the value to set.
+ *
+ * @param[in] data The data to store in the value.
+ *
+ * @param[out] pwerr A pointer to a WERROR to store result of the query.
+ *
+ * @return NT_STATUS_OK on success or a corresponding error if
+ * there was a problem on the connection.
+ */
+NTSTATUS dcerpc_winreg_set_multi_sz(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_handle,
+ const char *value,
+ const char **data,
+ WERROR *pwerr);
+
+/**
+ * @brief Set a value with the specified binary data.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] h The binding handle for the rpc connection.
+ *
+ * @param[in] key_handle A handle to a key that MUST have been opened
+ * previously.
+ *
+ * @param[in] value The name of the value to set.
+ *
+ * @param[in] data The data to store in the value.
+ *
+ * @param[out] pwerr A pointer to a WERROR to store result of the query.
+ *
+ * @return NT_STATUS_OK on success or a corresponding error if
+ * there was a problem on the connection.
+ */
+NTSTATUS dcerpc_winreg_set_binary(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_handle,
+ const char *value,
+ DATA_BLOB *data,
+ WERROR *pwerr);
+
+/**
+ * @brief Set a value with the specified security descriptor.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] h The binding handle for the rpc connection.
+ *
+ * @param[in] key_handle A handle to a key that MUST have been opened
+ * previously.
+ *
+ * @param[in] value The name of the value to set.
+ *
+ * @param[in] data The security descriptor to store in the value.
+ *
+ * @param[out] pwerr A pointer to a WERROR to store result of the query.
+ *
+ * @return NT_STATUS_OK on success or a corresponding error if
+ * there was a problem on the connection.
+ */
+NTSTATUS dcerpc_winreg_set_sd(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_handle,
+ const char *value,
+ const struct security_descriptor *data,
+ WERROR *pwerr);
+
+/**
+ * @brief Add a value to the multi sz data.
+ *
+ * This reads the multi sz data from the given value and adds the data to the
+ * multi sz. Then it saves it to the registry.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] h The binding handle for the rpc connection.
+ *
+ * @param[in] key_handle A handle to a key that MUST have been opened
+ * previously.
+ *
+ * @param[in] value The name of the value to set.
+ *
+ * @param[in] data The data to add.
+ *
+ * @param[out] pwerr A pointer to a WERROR to store result of the query.
+ *
+ * @return NT_STATUS_OK on success or a corresponding error if
+ * there was a problem on the connection.
+ */
+NTSTATUS dcerpc_winreg_add_multi_sz(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_handle,
+ const char *value,
+ const char *data,
+ WERROR *pwerr);
+
+/**
+ * @brief Enumerate on the given keyhandle to get the subkey names.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] h The binding handle for the rpc connection.
+ *
+ * @param[in] key_handle A handle to a key that MUST have been opened
+ * previously.
+ *
+ * @param[out] pnum_subkeys A pointer to store the number of subkeys.
+ *
+ * @param[out] psubkeys A pointer to store the names of the subkeys.
+ *
+ * @param[out] pwerr A pointer to a WERROR to store result of the query.
+ *
+ * @return NT_STATUS_OK on success or a corresponding error if
+ * there was a problem on the connection.
+ */
+NTSTATUS dcerpc_winreg_enum_keys(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_hnd,
+ uint32_t *pnum_subkeys,
+ const char ***psubkeys,
+ WERROR *pwerr);
+/**
+ * @internal
+ *
+ * @brief Enumerate values of an opened key handle and retrieve the data.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] winreg_handle The binding handle for the rpc connection.
+ *
+ * @param[in] key_hnd The opened key handle.
+ *
+ * @param[out] pnum_values A pointer to store the number of values we found.
+ *
+ * @param[out] pnames A pointer to store all the names of the values we found.
+ *
+ * @param[out] _type A pointer to store all the types corresponding with the
+ * values found.
+ * @param[out] pdata A pointer to store the data corresponding to the values.
+ *
+ * @param[out] pwerr A pointer to the WERROR. WERR_OK on success
+ * WERR_OK on success, the corresponding DOS error
+ * code if something's gone wrong.
+ *
+ * @return NT_STATUS_OK on success or a corresponding error if
+ * there was a problem on the connection.
+ */
+
+NTSTATUS dcerpc_winreg_enumvals(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *key_hnd,
+ uint32_t *pnum_values,
+ const char ***pnames,
+ enum winreg_Type **_type,
+ DATA_BLOB **pdata,
+ WERROR *pwerr);
+
+/**
+ * @internal
+ *
+ * @brief A function to delete a key and its subkeys recursively.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] winreg_handle The binding handle for the rpc connection.
+ *
+ * @param[in] hive_handle A opened hive handle to the key.
+ *
+ * @param[in] access_mask The access mask to access the key.
+ *
+ * @param[in] key The key to delete
+ *
+ * @param[out] WERR_OK on success, the corresponding DOS error
+ * code if something gone wrong.
+ *
+ * @return NT_STATUS_OK on success or a corresponding error if
+ * there was a problem on the connection.
+ */
+
+NTSTATUS dcerpc_winreg_delete_subkeys_recursive(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *hive_handle,
+ uint32_t access_mask,
+ const char *key,
+ WERROR *pwerr);
+
+
+#endif /* CLI_WINREG_H */
+
+/* vim: set ts=8 sw=8 noet cindent syntax=c.doxygen: */
diff --git a/source3/rpc_client/cli_winreg_int.c b/source3/rpc_client/cli_winreg_int.c
new file mode 100644
index 0000000..51cd363
--- /dev/null
+++ b/source3/rpc_client/cli_winreg_int.c
@@ -0,0 +1,323 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * WINREG internal client routines
+ *
+ * Copyright (c) 2011 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "include/registry.h"
+#include "librpc/gen_ndr/ndr_winreg_c.h"
+#include "rpc_client/cli_winreg_int.h"
+#include "rpc_server/rpc_ncacn_np.h"
+#include "../lib/tsocket/tsocket.h"
+
+/**
+ * Split path into hive name and subkeyname
+ * normalizations performed:
+ * - if the path contains no '\\' characters,
+ * assume that the legacy format of using '/'
+ * as a separator is used and convert '/' to '\\'
+ * - strip trailing '\\' chars
+ */
+static WERROR _split_hive_key(TALLOC_CTX *mem_ctx,
+ const char *path,
+ char **hivename,
+ char **subkeyname)
+{
+ char *p;
+ const char *tmp_subkeyname;
+
+ if ((path == NULL) || (hivename == NULL) || (subkeyname == NULL)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (strlen(path) == 0) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (strchr(path, '\\') == NULL) {
+ *hivename = talloc_string_sub(mem_ctx, path, "/", "\\");
+ } else {
+ *hivename = talloc_strdup(mem_ctx, path);
+ }
+
+ if (*hivename == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* strip trailing '\\' chars */
+ p = strrchr(*hivename, '\\');
+ while ((p != NULL) && (p[1] == '\0')) {
+ *p = '\0';
+ p = strrchr(*hivename, '\\');
+ }
+
+ p = strchr(*hivename, '\\');
+
+ if ((p == NULL) || (*p == '\0')) {
+ /* just the hive - no subkey given */
+ tmp_subkeyname = "";
+ } else {
+ *p = '\0';
+ tmp_subkeyname = p+1;
+ }
+ *subkeyname = talloc_strdup(mem_ctx, tmp_subkeyname);
+ if (*subkeyname == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ return WERR_OK;
+}
+
+static NTSTATUS _winreg_int_openkey(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ struct dcerpc_binding_handle **h,
+ uint32_t reg_type,
+ const char *key,
+ bool create_key,
+ uint32_t access_mask,
+ struct policy_handle *hive_handle,
+ struct policy_handle *key_handle,
+ WERROR *pwerr)
+{
+ struct tsocket_address *local;
+ struct dcerpc_binding_handle *binding_handle;
+ struct winreg_String wkey, wkeyclass;
+ NTSTATUS status;
+ WERROR result = WERR_OK;
+ int rc;
+
+ rc = tsocket_address_inet_from_strings(mem_ctx,
+ "ip",
+ "127.0.0.1",
+ 0,
+ &local);
+ if (rc < 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = rpcint_binding_handle(mem_ctx,
+ &ndr_table_winreg,
+ local,
+ NULL,
+ session_info,
+ msg_ctx,
+ &binding_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("Could not connect to winreg pipe: %s\n",
+ nt_errstr(status));
+ return status;
+ }
+
+ switch (reg_type) {
+ case HKEY_LOCAL_MACHINE:
+ status = dcerpc_winreg_OpenHKLM(binding_handle,
+ mem_ctx,
+ NULL,
+ access_mask,
+ hive_handle,
+ &result);
+ break;
+ case HKEY_CLASSES_ROOT:
+ status = dcerpc_winreg_OpenHKCR(binding_handle,
+ mem_ctx,
+ NULL,
+ access_mask,
+ hive_handle,
+ &result);
+ break;
+ case HKEY_USERS:
+ status = dcerpc_winreg_OpenHKU(binding_handle,
+ mem_ctx,
+ NULL,
+ access_mask,
+ hive_handle,
+ &result);
+ break;
+ case HKEY_CURRENT_USER:
+ status = dcerpc_winreg_OpenHKCU(binding_handle,
+ mem_ctx,
+ NULL,
+ access_mask,
+ hive_handle,
+ &result);
+ break;
+ case HKEY_PERFORMANCE_DATA:
+ status = dcerpc_winreg_OpenHKPD(binding_handle,
+ mem_ctx,
+ NULL,
+ access_mask,
+ hive_handle,
+ &result);
+ break;
+ default:
+ result = WERR_INVALID_PARAMETER;
+ status = NT_STATUS_OK;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(binding_handle);
+ return status;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(binding_handle);
+ *pwerr = result;
+ return status;
+ }
+
+ ZERO_STRUCT(wkey);
+ wkey.name = key;
+
+ if (create_key) {
+ enum winreg_CreateAction action = REG_ACTION_NONE;
+
+ ZERO_STRUCT(wkeyclass);
+ wkeyclass.name = "";
+
+ status = dcerpc_winreg_CreateKey(binding_handle,
+ mem_ctx,
+ hive_handle,
+ wkey,
+ wkeyclass,
+ 0,
+ access_mask,
+ NULL,
+ key_handle,
+ &action,
+ &result);
+ switch (action) {
+ case REG_ACTION_NONE:
+ DEBUG(8, ("dcerpc_winreg_int_openkey: createkey"
+ " did nothing -- huh?\n"));
+ break;
+ case REG_CREATED_NEW_KEY:
+ DEBUG(8, ("dcerpc_winreg_int_openkey: createkey"
+ " created %s\n", key));
+ break;
+ case REG_OPENED_EXISTING_KEY:
+ DEBUG(8, ("dcerpc_winreg_int_openkey: createkey"
+ " opened existing %s\n", key));
+ break;
+ }
+ } else {
+ status = dcerpc_winreg_OpenKey(binding_handle,
+ mem_ctx,
+ hive_handle,
+ wkey,
+ 0,
+ access_mask,
+ key_handle,
+ &result);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(binding_handle);
+ return status;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(binding_handle);
+ *pwerr = result;
+ return status;
+ }
+
+ *h = binding_handle;
+
+ return status;
+}
+
+NTSTATUS dcerpc_winreg_int_openkey(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *server_info,
+ struct messaging_context *msg_ctx,
+ struct dcerpc_binding_handle **h,
+ const char *key,
+ bool create_key,
+ uint32_t access_mask,
+ struct policy_handle *hive_handle,
+ struct policy_handle *key_handle,
+ WERROR *pwerr)
+{
+ char *hivename = NULL;
+ char *subkey = NULL;
+ uint32_t reg_type;
+ WERROR result;
+
+ result = _split_hive_key(mem_ctx, key, &hivename, &subkey);
+ if (!W_ERROR_IS_OK(result)) {
+ *pwerr = result;
+ return NT_STATUS_OK;
+ }
+
+ if (strequal(hivename, "HKLM") ||
+ strequal(hivename, "HKEY_LOCAL_MACHINE")) {
+ reg_type = HKEY_LOCAL_MACHINE;
+ } else if (strequal(hivename, "HKCR") ||
+ strequal(hivename, "HKEY_CLASSES_ROOT")) {
+ reg_type = HKEY_CLASSES_ROOT;
+ } else if (strequal(hivename, "HKU") ||
+ strequal(hivename, "HKEY_USERS")) {
+ reg_type = HKEY_USERS;
+ } else if (strequal(hivename, "HKCU") ||
+ strequal(hivename, "HKEY_CURRENT_USER")) {
+ reg_type = HKEY_CURRENT_USER;
+ } else if (strequal(hivename, "HKPD") ||
+ strequal(hivename, "HKEY_PERFORMANCE_DATA")) {
+ reg_type = HKEY_PERFORMANCE_DATA;
+ } else {
+ DEBUG(10,("dcerpc_winreg_int_openkey: unrecognised hive key %s\n",
+ key));
+ *pwerr = WERR_INVALID_PARAMETER;
+ return NT_STATUS_OK;
+ }
+
+ return _winreg_int_openkey(mem_ctx,
+ server_info,
+ msg_ctx,
+ h,
+ reg_type,
+ key,
+ create_key,
+ access_mask,
+ hive_handle,
+ key_handle,
+ pwerr);
+}
+
+NTSTATUS dcerpc_winreg_int_hklm_openkey(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *server_info,
+ struct messaging_context *msg_ctx,
+ struct dcerpc_binding_handle **h,
+ const char *key,
+ bool create_key,
+ uint32_t access_mask,
+ struct policy_handle *hive_handle,
+ struct policy_handle *key_handle,
+ WERROR *pwerr)
+{
+ return _winreg_int_openkey(mem_ctx,
+ server_info,
+ msg_ctx,
+ h,
+ HKEY_LOCAL_MACHINE,
+ key,
+ create_key,
+ access_mask,
+ hive_handle,
+ key_handle,
+ pwerr);
+}
+
+/* vim: set ts=8 sw=8 noet cindent syntax=c.doxygen: */
diff --git a/source3/rpc_client/cli_winreg_int.h b/source3/rpc_client/cli_winreg_int.h
new file mode 100644
index 0000000..9584088
--- /dev/null
+++ b/source3/rpc_client/cli_winreg_int.h
@@ -0,0 +1,103 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * WINREG internal client routines
+ *
+ * Copyright (c) 2011 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CLI_WINREG_INT_H
+#define CLI_WINREG_INT_H
+
+struct auth_session_info;
+struct dcerpc_binding_handle;
+
+/**
+ * @brief Connect to the internal winreg server and open the given key.
+ *
+ * The function will create the needed subkeys if they don't exist.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] session_info The supplied server info.
+ *
+ * @param[in] key The key to open. This needs to start with the name
+ * of the hive like HKLM.
+ *
+ * @param[in] create_key Set to true if the key should be created if it
+ * doesn't exist.
+ *
+ * @param[in] access_mask The access mask to open the key.
+ *
+ * @param[out] binding_handle A pointer for the winreg dcerpc binding handle.
+ *
+ * @param[out] hive_handle A policy handle for the opened hive.
+ *
+ * @param[out] key_handle A policy handle for the opened key.
+ *
+ * @return WERR_OK on success, the corresponding DOS error
+ * code if something gone wrong.
+ */
+NTSTATUS dcerpc_winreg_int_openkey(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *server_info,
+ struct messaging_context *msg_ctx,
+ struct dcerpc_binding_handle **h,
+ const char *key,
+ bool create_key,
+ uint32_t access_mask,
+ struct policy_handle *hive_handle,
+ struct policy_handle *key_handle,
+ WERROR *pwerr);
+
+/**
+ * @brief Connect to the internal winreg server and open the given key.
+ *
+ * The function will create the needed subkeys if they don't exist.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] server_info The supplied server info.
+ *
+ * @param[in] key The key to open.
+ *
+ * @param[in] create_key Set to true if the key should be created if it
+ * doesn't exist.
+ *
+ * @param[in] access_mask The access mask to open the key.
+ *
+ * @param[out] binding_handle A pointer for the winreg dcerpc binding handle.
+ *
+ * @param[out] hive_handle A policy handle for the opened hive.
+ *
+ * @param[out] key_handle A policy handle for the opened key.
+ *
+ * @return WERR_OK on success, the corresponding DOS error
+ * code if something gone wrong.
+ */
+NTSTATUS dcerpc_winreg_int_hklm_openkey(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ struct dcerpc_binding_handle **h,
+ const char *key,
+ bool create_key,
+ uint32_t access_mask,
+ struct policy_handle *hive_handle,
+ struct policy_handle *key_handle,
+ WERROR *pwerr);
+
+#endif /* CLI_WINREG_INT_H */
+
+/* vim: set ts=8 sw=8 noet cindent syntax=c.doxygen: */
diff --git a/source3/rpc_client/cli_winreg_spoolss.c b/source3/rpc_client/cli_winreg_spoolss.c
new file mode 100644
index 0000000..ca46ae5
--- /dev/null
+++ b/source3/rpc_client/cli_winreg_spoolss.c
@@ -0,0 +1,4729 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * SPOOLSS RPC Pipe server / winreg client routines
+ *
+ * Copyright (c) 2010 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "nt_printing.h"
+#include "../librpc/gen_ndr/ndr_spoolss.h"
+#include "../librpc/gen_ndr/ndr_winreg_c.h"
+#include "../librpc/gen_ndr/ndr_security.h"
+#include "secrets.h"
+#include "../libcli/security/security.h"
+#include "rpc_client/cli_winreg.h"
+#include "../libcli/registry/util_reg.h"
+#include "rpc_client/cli_winreg_spoolss.h"
+#include "printing/nt_printing_os2.h"
+#include "rpc_client/init_spoolss.h"
+
+#define TOP_LEVEL_PRINT_KEY "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Print"
+#define TOP_LEVEL_PRINT_PRINTERS_KEY TOP_LEVEL_PRINT_KEY "\\Printers"
+#define TOP_LEVEL_PRINT_PACKAGEINSTALLATION_KEY TOP_LEVEL_PRINT_KEY "\\PackageInstallation"
+#define TOP_LEVEL_CONTROL_KEY "SYSTEM\\CurrentControlSet\\Control\\Print"
+#define TOP_LEVEL_CONTROL_FORMS_KEY TOP_LEVEL_CONTROL_KEY "\\Forms"
+
+#define CHECK_ERROR(result) \
+ if (W_ERROR_IS_OK(result)) continue; \
+ if (W_ERROR_EQUAL(result, WERR_NOT_FOUND)) result = WERR_OK; \
+ if (!W_ERROR_IS_OK(result)) break
+
+/* FLAGS, NAME, with, height, left, top, right, bottom */
+static const struct spoolss_FormInfo1 builtin_forms1[] = {
+ { SPOOLSS_FORM_BUILTIN, "Letter", {0x34b5c,0x44368}, {0x0,0x0,0x34b5c,0x44368} },
+ { SPOOLSS_FORM_BUILTIN, "Letter Small", {0x34b5c,0x44368}, {0x0,0x0,0x34b5c,0x44368} },
+ { SPOOLSS_FORM_BUILTIN, "Tabloid", {0x44368,0x696b8}, {0x0,0x0,0x44368,0x696b8} },
+ { SPOOLSS_FORM_BUILTIN, "Ledger", {0x696b8,0x44368}, {0x0,0x0,0x696b8,0x44368} },
+ { SPOOLSS_FORM_BUILTIN, "Legal", {0x34b5c,0x56d10}, {0x0,0x0,0x34b5c,0x56d10} },
+ { SPOOLSS_FORM_BUILTIN, "Statement", {0x221b4,0x34b5c}, {0x0,0x0,0x221b4,0x34b5c} },
+ { SPOOLSS_FORM_BUILTIN, "Executive", {0x2cf56,0x411cc}, {0x0,0x0,0x2cf56,0x411cc} },
+ { SPOOLSS_FORM_BUILTIN, "A3", {0x48828,0x668a0}, {0x0,0x0,0x48828,0x668a0} },
+ { SPOOLSS_FORM_BUILTIN, "A4", {0x33450,0x48828}, {0x0,0x0,0x33450,0x48828} },
+ { SPOOLSS_FORM_BUILTIN, "A4 Small", {0x33450,0x48828}, {0x0,0x0,0x33450,0x48828} },
+ { SPOOLSS_FORM_BUILTIN, "A5", {0x24220,0x33450}, {0x0,0x0,0x24220,0x33450} },
+ { SPOOLSS_FORM_BUILTIN, "B4 (JIS)", {0x3ebe8,0x58de0}, {0x0,0x0,0x3ebe8,0x58de0} },
+ { SPOOLSS_FORM_BUILTIN, "B5 (JIS)", {0x2c6f0,0x3ebe8}, {0x0,0x0,0x2c6f0,0x3ebe8} },
+ { SPOOLSS_FORM_BUILTIN, "Folio", {0x34b5c,0x509d8}, {0x0,0x0,0x34b5c,0x509d8} },
+ { SPOOLSS_FORM_BUILTIN, "Quarto", {0x347d8,0x43238}, {0x0,0x0,0x347d8,0x43238} },
+ { SPOOLSS_FORM_BUILTIN, "10x14", {0x3e030,0x56d10}, {0x0,0x0,0x3e030,0x56d10} },
+ { SPOOLSS_FORM_BUILTIN, "11x17", {0x44368,0x696b8}, {0x0,0x0,0x44368,0x696b8} },
+ { SPOOLSS_FORM_BUILTIN, "Note", {0x34b5c,0x44368}, {0x0,0x0,0x34b5c,0x44368} },
+ { SPOOLSS_FORM_BUILTIN, "Envelope #9", {0x18079,0x37091}, {0x0,0x0,0x18079,0x37091} },
+ { SPOOLSS_FORM_BUILTIN, "Envelope #10", {0x19947,0x3ae94}, {0x0,0x0,0x19947,0x3ae94} },
+ { SPOOLSS_FORM_BUILTIN, "Envelope #11", {0x1be7c,0x40565}, {0x0,0x0,0x1be7c,0x40565} },
+ { SPOOLSS_FORM_BUILTIN, "Envelope #12", {0x1d74a,0x44368}, {0x0,0x0,0x1d74a,0x44368} },
+ { SPOOLSS_FORM_BUILTIN, "Envelope #14", {0x1f018,0x47504}, {0x0,0x0,0x1f018,0x47504} },
+ { SPOOLSS_FORM_BUILTIN, "C size sheet", {0x696b8,0x886d0}, {0x0,0x0,0x696b8,0x886d0} },
+ { SPOOLSS_FORM_BUILTIN, "D size sheet", {0x886d0,0xd2d70}, {0x0,0x0,0x886d0,0xd2d70} },
+ { SPOOLSS_FORM_BUILTIN, "E size sheet", {0xd2d70,0x110da0},{0x0,0x0,0xd2d70,0x110da0} },
+ { SPOOLSS_FORM_BUILTIN, "Envelope DL", {0x1adb0,0x35b60}, {0x0,0x0,0x1adb0,0x35b60} },
+ { SPOOLSS_FORM_BUILTIN, "Envelope C5", {0x278d0,0x37e88}, {0x0,0x0,0x278d0,0x37e88} },
+ { SPOOLSS_FORM_BUILTIN, "Envelope C3", {0x4f1a0,0x6fd10}, {0x0,0x0,0x4f1a0,0x6fd10} },
+ { SPOOLSS_FORM_BUILTIN, "Envelope C4", {0x37e88,0x4f1a0}, {0x0,0x0,0x37e88,0x4f1a0} },
+ { SPOOLSS_FORM_BUILTIN, "Envelope C6", {0x1bd50,0x278d0}, {0x0,0x0,0x1bd50,0x278d0} },
+ { SPOOLSS_FORM_BUILTIN, "Envelope C65", {0x1bd50,0x37e88}, {0x0,0x0,0x1bd50,0x37e88} },
+ { SPOOLSS_FORM_BUILTIN, "Envelope B4", {0x3d090,0x562e8}, {0x0,0x0,0x3d090,0x562e8} },
+ { SPOOLSS_FORM_BUILTIN, "Envelope B5", {0x2af80,0x3d090}, {0x0,0x0,0x2af80,0x3d090} },
+ { SPOOLSS_FORM_BUILTIN, "Envelope B6", {0x2af80,0x1e848}, {0x0,0x0,0x2af80,0x1e848} },
+ { SPOOLSS_FORM_BUILTIN, "Envelope", {0x1adb0,0x38270}, {0x0,0x0,0x1adb0,0x38270} },
+ { SPOOLSS_FORM_BUILTIN, "Envelope Monarch", {0x18079,0x2e824}, {0x0,0x0,0x18079,0x2e824} },
+ { SPOOLSS_FORM_BUILTIN, "6 3/4 Envelope", {0x167ab,0x284ec}, {0x0,0x0,0x167ab,0x284ec} },
+ { SPOOLSS_FORM_BUILTIN, "US Std Fanfold", {0x5c3e1,0x44368}, {0x0,0x0,0x5c3e1,0x44368} },
+ { SPOOLSS_FORM_BUILTIN, "German Std Fanfold", {0x34b5c,0x4a6a0}, {0x0,0x0,0x34b5c,0x4a6a0} },
+ { SPOOLSS_FORM_BUILTIN, "German Legal Fanfold", {0x34b5c,0x509d8}, {0x0,0x0,0x34b5c,0x509d8} },
+ { SPOOLSS_FORM_BUILTIN, "B4 (ISO)", {0x3d090,0x562e8}, {0x0,0x0,0x3d090,0x562e8} },
+ { SPOOLSS_FORM_BUILTIN, "Japanese Postcard", {0x186a0,0x24220}, {0x0,0x0,0x186a0,0x24220} },
+ { SPOOLSS_FORM_BUILTIN, "9x11", {0x37cf8,0x44368}, {0x0,0x0,0x37cf8,0x44368} },
+ { SPOOLSS_FORM_BUILTIN, "10x11", {0x3e030,0x44368}, {0x0,0x0,0x3e030,0x44368} },
+ { SPOOLSS_FORM_BUILTIN, "15x11", {0x5d048,0x44368}, {0x0,0x0,0x5d048,0x44368} },
+ { SPOOLSS_FORM_BUILTIN, "Envelope Invite", {0x35b60,0x35b60}, {0x0,0x0,0x35b60,0x35b60} },
+ { SPOOLSS_FORM_BUILTIN, "Reserved48", {0x1,0x1}, {0x0,0x0,0x1,0x1} },
+ { SPOOLSS_FORM_BUILTIN, "Reserved49", {0x1,0x1}, {0x0,0x0,0x1,0x1} },
+ { SPOOLSS_FORM_BUILTIN, "Letter Extra", {0x3ae94,0x4a6a0}, {0x0,0x0,0x3ae94,0x4a6a0} },
+ { SPOOLSS_FORM_BUILTIN, "Legal Extra", {0x3ae94,0x5d048}, {0x0,0x0,0x3ae94,0x5d048} },
+ { SPOOLSS_FORM_BUILTIN, "Tabloid Extra", {0x4a6a0,0x6f9f0}, {0x0,0x0,0x4a6a0,0x6f9f0} },
+ { SPOOLSS_FORM_BUILTIN, "A4 Extra", {0x397c2,0x4eb16}, {0x0,0x0,0x397c2,0x4eb16} },
+ { SPOOLSS_FORM_BUILTIN, "Letter Transverse", {0x34b5c,0x44368}, {0x0,0x0,0x34b5c,0x44368} },
+ { SPOOLSS_FORM_BUILTIN, "A4 Transverse", {0x33450,0x48828}, {0x0,0x0,0x33450,0x48828} },
+ { SPOOLSS_FORM_BUILTIN, "Letter Extra Transverse", {0x3ae94,0x4a6a0}, {0x0,0x0,0x3ae94,0x4a6a0} },
+ { SPOOLSS_FORM_BUILTIN, "Super A", {0x376b8,0x56ea0}, {0x0,0x0,0x376b8,0x56ea0} },
+ { SPOOLSS_FORM_BUILTIN, "Super B", {0x4a768,0x76e58}, {0x0,0x0,0x4a768,0x76e58} },
+ { SPOOLSS_FORM_BUILTIN, "Letter Plus", {0x34b5c,0x4eb16}, {0x0,0x0,0x34b5c,0x4eb16} },
+ { SPOOLSS_FORM_BUILTIN, "A4 Plus", {0x33450,0x50910}, {0x0,0x0,0x33450,0x50910} },
+ { SPOOLSS_FORM_BUILTIN, "A5 Transverse", {0x24220,0x33450}, {0x0,0x0,0x24220,0x33450} },
+ { SPOOLSS_FORM_BUILTIN, "B5 (JIS) Transverse", {0x2c6f0,0x3ebe8}, {0x0,0x0,0x2c6f0,0x3ebe8} },
+ { SPOOLSS_FORM_BUILTIN, "A3 Extra", {0x4e9d0,0x6ca48}, {0x0,0x0,0x4e9d0,0x6ca48} },
+ { SPOOLSS_FORM_BUILTIN, "A5 Extra", {0x2a7b0,0x395f8}, {0x0,0x0,0x2a7b0,0x395f8} },
+ { SPOOLSS_FORM_BUILTIN, "B5 (ISO) Extra", {0x31128,0x43620}, {0x0,0x0,0x31128,0x43620} },
+ { SPOOLSS_FORM_BUILTIN, "A2", {0x668a0,0x91050}, {0x0,0x0,0x668a0,0x91050} },
+ { SPOOLSS_FORM_BUILTIN, "A3 Transverse", {0x48828,0x668a0}, {0x0,0x0,0x48828,0x668a0} },
+ { SPOOLSS_FORM_BUILTIN, "A3 Extra Transverse", {0x4e9d0,0x6ca48}, {0x0,0x0,0x4e9d0,0x6ca48} },
+ { SPOOLSS_FORM_BUILTIN, "Japanese Double Postcard", {0x30d40,0x24220}, {0x0,0x0,0x30d40,0x24220} },
+ { SPOOLSS_FORM_BUILTIN, "A6", {0x19a28,0x24220}, {0x0,0x0,0x19a28,0x24220} },
+ { SPOOLSS_FORM_BUILTIN, "Japan Envelope Kaku #2 Rotated", {0x510e0,0x3a980}, {0x0,0x0,0x510e0,0x3a980} },
+ { SPOOLSS_FORM_BUILTIN, "Japan Envelope Kaku #3 Rotated", {0x43a08,0x34bc0}, {0x0,0x0,0x43a08,0x34bc0} },
+ { SPOOLSS_FORM_BUILTIN, "Japan Envelope Chou #3 Rotated", {0x395f8,0x1d4c0}, {0x0,0x0,0x395f8,0x1d4c0} },
+ { SPOOLSS_FORM_BUILTIN, "Japan Envelope Chou #4 Rotated", {0x320c8,0x15f90}, {0x0,0x0,0x320c8,0x15f90} },
+ { SPOOLSS_FORM_BUILTIN, "Letter Rotated", {0x44368,0x34b5c}, {0x0,0x0,0x44368,0x34b5c} },
+ { SPOOLSS_FORM_BUILTIN, "A3 Rotated", {0x668a0,0x48828}, {0x0,0x0,0x668a0,0x48828} },
+ { SPOOLSS_FORM_BUILTIN, "A4 Rotated", {0x48828,0x33450}, {0x0,0x0,0x48828,0x33450} },
+ { SPOOLSS_FORM_BUILTIN, "A5 Rotated", {0x33450,0x24220}, {0x0,0x0,0x33450,0x24220} },
+ { SPOOLSS_FORM_BUILTIN, "B4 (JIS) Rotated", {0x58de0,0x3ebe8}, {0x0,0x0,0x58de0,0x3ebe8} },
+ { SPOOLSS_FORM_BUILTIN, "B5 (JIS) Rotated", {0x3ebe8,0x2c6f0}, {0x0,0x0,0x3ebe8,0x2c6f0} },
+ { SPOOLSS_FORM_BUILTIN, "Japanese Postcard Rotated", {0x24220,0x186a0}, {0x0,0x0,0x24220,0x186a0} },
+ { SPOOLSS_FORM_BUILTIN, "Double Japan Postcard Rotated", {0x24220,0x30d40}, {0x0,0x0,0x24220,0x30d40} },
+ { SPOOLSS_FORM_BUILTIN, "A6 Rotated", {0x24220,0x19a28}, {0x0,0x0,0x24220,0x19a28} },
+ { SPOOLSS_FORM_BUILTIN, "Japanese Envelope Kaku #2", {0x3a980,0x510e0}, {0x0,0x0,0x3a980,0x510e0} },
+ { SPOOLSS_FORM_BUILTIN, "Japanese Envelope Kaku #3", {0x34bc0,0x43a08}, {0x0,0x0,0x34bc0,0x43a08} },
+ { SPOOLSS_FORM_BUILTIN, "Japanese Envelope Chou #3", {0x1d4c0,0x395f8}, {0x0,0x0,0x1d4c0,0x395f8} },
+ { SPOOLSS_FORM_BUILTIN, "Japanese Envelope Chou #4", {0x15f90,0x320c8}, {0x0,0x0,0x15f90,0x320c8} },
+ { SPOOLSS_FORM_BUILTIN, "B6 (JIS)", {0x1f400,0x2c6f0}, {0x0,0x0,0x1f400,0x2c6f0} },
+ { SPOOLSS_FORM_BUILTIN, "B6 (JIS) Rotated", {0x2c6f0,0x1f400}, {0x0,0x0,0x2c6f0,0x1f400} },
+ { SPOOLSS_FORM_BUILTIN, "12x11", {0x4a724,0x443e1}, {0x0,0x0,0x4a724,0x443e1} },
+ { SPOOLSS_FORM_BUILTIN, "Japan Envelope You #4", {0x19a28,0x395f8}, {0x0,0x0,0x19a28,0x395f8} },
+ { SPOOLSS_FORM_BUILTIN, "Japan Envelope You #4 Rotated", {0x395f8,0x19a28}, {0x0,0x0,0x395f8,0x19a28} },
+ { SPOOLSS_FORM_BUILTIN, "PRC 16K", {0x2de60,0x3f7a0}, {0x0,0x0,0x2de60,0x3f7a0} },
+ { SPOOLSS_FORM_BUILTIN, "PRC 32K", {0x1fbd0,0x2cec0}, {0x0,0x0,0x1fbd0,0x2cec0} },
+ { SPOOLSS_FORM_BUILTIN, "PRC 32K(Big)", {0x222e0,0x318f8}, {0x0,0x0,0x222e0,0x318f8} },
+ { SPOOLSS_FORM_BUILTIN, "PRC Envelope #1", {0x18e70,0x28488}, {0x0,0x0,0x18e70,0x28488} },
+ { SPOOLSS_FORM_BUILTIN, "PRC Envelope #2", {0x18e70,0x2af80}, {0x0,0x0,0x18e70,0x2af80} },
+ { SPOOLSS_FORM_BUILTIN, "PRC Envelope #3", {0x1e848,0x2af80}, {0x0,0x0,0x1e848,0x2af80} },
+ { SPOOLSS_FORM_BUILTIN, "PRC Envelope #4", {0x1adb0,0x32c80}, {0x0,0x0,0x1adb0,0x32c80} },
+ { SPOOLSS_FORM_BUILTIN, "PRC Envelope #5", {0x1adb0,0x35b60}, {0x0,0x0,0x1adb0,0x35b60} },
+ { SPOOLSS_FORM_BUILTIN, "PRC Envelope #6", {0x1d4c0,0x38270}, {0x0,0x0,0x1d4c0,0x38270} },
+ { SPOOLSS_FORM_BUILTIN, "PRC Envelope #7", {0x27100,0x38270}, {0x0,0x0,0x27100,0x38270} },
+ { SPOOLSS_FORM_BUILTIN, "PRC Envelope #8", {0x1d4c0,0x4b708}, {0x0,0x0,0x1d4c0,0x4b708} },
+ { SPOOLSS_FORM_BUILTIN, "PRC Envelope #9", {0x37e88,0x4f1a0}, {0x0,0x0,0x37e88,0x4f1a0} },
+ { SPOOLSS_FORM_BUILTIN, "PRC Envelope #10", {0x4f1a0,0x6fd10}, {0x0,0x0,0x4f1a0,0x6fd10} },
+ { SPOOLSS_FORM_BUILTIN, "PRC 16K Rotated", {0x3f7a0,0x2de60}, {0x0,0x0,0x3f7a0,0x2de60} },
+ { SPOOLSS_FORM_BUILTIN, "PRC 32K Rotated", {0x2cec0,0x1fbd0}, {0x0,0x0,0x2cec0,0x1fbd0} },
+ { SPOOLSS_FORM_BUILTIN, "PRC 32K(Big) Rotated", {0x318f8,0x222e0}, {0x0,0x0,0x318f8,0x222e0} },
+ { SPOOLSS_FORM_BUILTIN, "PRC Envelope #1 Rotated", {0x28488,0x18e70}, {0x0,0x0,0x28488,0x18e70} },
+ { SPOOLSS_FORM_BUILTIN, "PRC Envelope #2 Rotated", {0x2af80,0x18e70}, {0x0,0x0,0x2af80,0x18e70} },
+ { SPOOLSS_FORM_BUILTIN, "PRC Envelope #3 Rotated", {0x2af80,0x1e848}, {0x0,0x0,0x2af80,0x1e848} },
+ { SPOOLSS_FORM_BUILTIN, "PRC Envelope #4 Rotated", {0x32c80,0x1adb0}, {0x0,0x0,0x32c80,0x1adb0} },
+ { SPOOLSS_FORM_BUILTIN, "PRC Envelope #5 Rotated", {0x35b60,0x1adb0}, {0x0,0x0,0x35b60,0x1adb0} },
+ { SPOOLSS_FORM_BUILTIN, "PRC Envelope #6 Rotated", {0x38270,0x1d4c0}, {0x0,0x0,0x38270,0x1d4c0} },
+ { SPOOLSS_FORM_BUILTIN, "PRC Envelope #7 Rotated", {0x38270,0x27100}, {0x0,0x0,0x38270,0x27100} },
+ { SPOOLSS_FORM_BUILTIN, "PRC Envelope #8 Rotated", {0x4b708,0x1d4c0}, {0x0,0x0,0x4b708,0x1d4c0} },
+ { SPOOLSS_FORM_BUILTIN, "PRC Envelope #9 Rotated", {0x4f1a0,0x37e88}, {0x0,0x0,0x4f1a0,0x37e88} },
+ { SPOOLSS_FORM_BUILTIN, "PRC Envelope #10 Rotated", {0x6fd10,0x4f1a0}, {0x0,0x0,0x6fd10,0x4f1a0} }
+};
+
+/********************************************************************
+ static helper functions
+********************************************************************/
+
+/****************************************************************************
+ Update the changeid time.
+****************************************************************************/
+/**
+ * @internal
+ *
+ * @brief Update the ChangeID time of a printer.
+ *
+ * This is SO NASTY as some drivers need this to change, others need it
+ * static. This value will change every second, and I must hope that this
+ * is enough..... DON'T CHANGE THIS CODE WITHOUT A TEST MATRIX THE SIZE OF
+ * UTAH ! JRA.
+ *
+ * @return The ChangeID.
+ */
+static uint32_t winreg_printer_rev_changeid(void)
+{
+ struct timeval tv;
+
+ get_process_uptime(&tv);
+
+#if 1 /* JERRY */
+ /* Return changeid as msec since spooler restart */
+ return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+#else
+ /*
+ * This setting seems to work well but is too untested
+ * to replace the above calculation. Left in for experimentation
+ * of the reader --jerry (Tue Mar 12 09:15:05 CST 2002)
+ */
+ return tv.tv_sec * 10 + tv.tv_usec / 100000;
+#endif
+}
+
+static WERROR winreg_printer_openkey(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *binding_handle,
+ const char *path,
+ const char *key,
+ bool create_key,
+ uint32_t access_mask,
+ struct policy_handle *hive_handle,
+ struct policy_handle *key_handle)
+{
+ struct winreg_String wkey, wkeyclass;
+ char *keyname;
+ NTSTATUS status;
+ WERROR result = WERR_OK;
+
+ status = dcerpc_winreg_OpenHKLM(binding_handle,
+ mem_ctx,
+ NULL,
+ access_mask,
+ hive_handle,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("winreg_printer_openkey: Could not open HKLM hive: %s\n",
+ nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_printer_openkey: Could not open HKLM hive: %s\n",
+ win_errstr(result)));
+ return result;
+ }
+
+ if (key && *key) {
+ keyname = talloc_asprintf(mem_ctx, "%s\\%s", path, key);
+ } else {
+ keyname = talloc_strdup(mem_ctx, path);
+ }
+ if (keyname == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ ZERO_STRUCT(wkey);
+ wkey.name = keyname;
+
+ if (create_key) {
+ enum winreg_CreateAction action = REG_ACTION_NONE;
+
+ ZERO_STRUCT(wkeyclass);
+ wkeyclass.name = "";
+
+ status = dcerpc_winreg_CreateKey(binding_handle,
+ mem_ctx,
+ hive_handle,
+ wkey,
+ wkeyclass,
+ 0,
+ access_mask,
+ NULL,
+ key_handle,
+ &action,
+ &result);
+ switch (action) {
+ case REG_ACTION_NONE:
+ DEBUG(8, ("winreg_printer_openkey:createkey did nothing -- huh?\n"));
+ break;
+ case REG_CREATED_NEW_KEY:
+ DEBUG(8, ("winreg_printer_openkey: createkey created %s\n", keyname));
+ break;
+ case REG_OPENED_EXISTING_KEY:
+ DEBUG(8, ("winreg_printer_openkey: createkey opened existing %s\n", keyname));
+ break;
+ }
+ } else {
+ status = dcerpc_winreg_OpenKey(binding_handle,
+ mem_ctx,
+ hive_handle,
+ wkey,
+ 0,
+ access_mask,
+ key_handle,
+ &result);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ WERROR ignore;
+
+ if (is_valid_policy_hnd(hive_handle)) {
+ dcerpc_winreg_CloseKey(binding_handle,
+ mem_ctx,
+ hive_handle,
+ &ignore);
+ }
+ ZERO_STRUCTP(hive_handle);
+
+ return result;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR winreg_printer_open_core_driver(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *binding_handle,
+ const char *architecture,
+ const char *key,
+ uint32_t access_mask,
+ struct policy_handle *hive_handle,
+ struct policy_handle *key_handle)
+{
+ struct winreg_String wkey, wkeyclass;
+ NTSTATUS status;
+ WERROR result = WERR_OK;
+ WERROR ignore;
+ enum winreg_CreateAction action = REG_ACTION_NONE;
+ const char *path;
+
+ status = dcerpc_winreg_OpenHKLM(binding_handle,
+ mem_ctx,
+ NULL,
+ access_mask,
+ hive_handle,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("winreg_printer_open_core_driver: Could not open HKLM hive: %s\n",
+ nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0,("winreg_printer_open_core_driver: Could not open HKLM hive: %s\n",
+ win_errstr(result)));
+ return result;
+ }
+
+ ZERO_STRUCT(wkey);
+ wkey.name = TOP_LEVEL_PRINT_PACKAGEINSTALLATION_KEY;
+
+ ZERO_STRUCT(wkeyclass);
+ wkeyclass.name = "";
+
+ status = dcerpc_winreg_CreateKey(binding_handle,
+ mem_ctx,
+ hive_handle,
+ wkey,
+ wkeyclass,
+ 0,
+ access_mask,
+ NULL,
+ key_handle,
+ &action,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ dcerpc_winreg_CloseKey(binding_handle, mem_ctx, key_handle, &ignore);
+
+ path = talloc_asprintf(mem_ctx, "%s\\%s",
+ TOP_LEVEL_PRINT_PACKAGEINSTALLATION_KEY,
+ architecture);
+ if (path == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ wkey.name = path;
+
+ status = dcerpc_winreg_CreateKey(binding_handle,
+ mem_ctx,
+ hive_handle,
+ wkey,
+ wkeyclass,
+ 0,
+ access_mask,
+ NULL,
+ key_handle,
+ &action,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ dcerpc_winreg_CloseKey(binding_handle, mem_ctx, key_handle, &ignore);
+
+ path = talloc_asprintf(mem_ctx, "%s\\%s\\CorePrinterDrivers",
+ TOP_LEVEL_PRINT_PACKAGEINSTALLATION_KEY,
+ architecture);
+ if (path == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ wkey.name = path;
+
+ status = dcerpc_winreg_CreateKey(binding_handle,
+ mem_ctx,
+ hive_handle,
+ wkey,
+ wkeyclass,
+ 0,
+ access_mask,
+ NULL,
+ key_handle,
+ &action,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ dcerpc_winreg_CloseKey(binding_handle, mem_ctx, key_handle, &ignore);
+
+ path = talloc_asprintf(mem_ctx, "%s\\%s\\CorePrinterDrivers\\%s",
+ TOP_LEVEL_PRINT_PACKAGEINSTALLATION_KEY,
+ architecture,
+ key);
+ if (path == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ wkey.name = path;
+
+ status = dcerpc_winreg_CreateKey(binding_handle,
+ mem_ctx,
+ hive_handle,
+ wkey,
+ wkeyclass,
+ 0,
+ access_mask,
+ NULL,
+ key_handle,
+ &action,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ done:
+ if (is_valid_policy_hnd(hive_handle)) {
+ dcerpc_winreg_CloseKey(binding_handle,
+ mem_ctx,
+ hive_handle,
+ &ignore);
+ }
+ ZERO_STRUCTP(hive_handle);
+
+ return result;
+}
+
+/**
+ * @brief Create the registry keyname for the given printer.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] printer The name of the printer to get the registry key.
+ *
+ * @return The registry key or NULL on error.
+ */
+static char *winreg_printer_data_keyname(TALLOC_CTX *mem_ctx, const char *printer) {
+ return talloc_asprintf(mem_ctx, "%s\\%s", TOP_LEVEL_PRINT_PRINTERS_KEY, printer);
+}
+
+static WERROR winreg_printer_opendriver(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *drivername,
+ const char *architecture,
+ uint32_t version,
+ uint32_t access_mask,
+ bool create,
+ struct policy_handle *hive_hnd,
+ struct policy_handle *key_hnd)
+{
+ WERROR result;
+ char *key_name;
+
+ key_name = talloc_asprintf(mem_ctx, "%s\\Environments\\%s\\Drivers\\Version-%u",
+ TOP_LEVEL_CONTROL_KEY,
+ architecture, version);
+ if (!key_name) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_openkey(mem_ctx,
+ winreg_handle,
+ key_name,
+ drivername,
+ create,
+ access_mask,
+ hive_hnd,
+ key_hnd);
+ return result;
+}
+
+static WERROR winreg_enumval_to_dword(TALLOC_CTX *mem_ctx,
+ struct spoolss_PrinterEnumValues *v,
+ const char *valuename, uint32_t *dw)
+{
+ /* just return if it is not the one we are looking for */
+ if (strcmp(valuename, v->value_name) != 0) {
+ return WERR_NOT_FOUND;
+ }
+
+ if (v->type != REG_DWORD) {
+ return WERR_INVALID_DATATYPE;
+ }
+
+ if (v->data_length != 4) {
+ *dw = 0;
+ return WERR_OK;
+ }
+
+ *dw = IVAL(v->data->data, 0);
+ return WERR_OK;
+}
+
+static WERROR winreg_enumval_to_sz(TALLOC_CTX *mem_ctx,
+ struct spoolss_PrinterEnumValues *v,
+ const char *valuename, const char **_str)
+{
+ /* just return if it is not the one we are looking for */
+ if (strcmp(valuename, v->value_name) != 0) {
+ return WERR_NOT_FOUND;
+ }
+
+ if (v->type != REG_SZ) {
+ return WERR_INVALID_DATATYPE;
+ }
+
+ if (v->data_length == 0) {
+ *_str = talloc_strdup(mem_ctx, "");
+ if (*_str == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ return WERR_OK;
+ }
+
+ if (!pull_reg_sz(mem_ctx, v->data, _str)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR winreg_enumval_to_multi_sz(TALLOC_CTX *mem_ctx,
+ struct spoolss_PrinterEnumValues *v,
+ const char *valuename,
+ const char ***array)
+{
+ /* just return if it is not the one we are looking for */
+ if (strcmp(valuename, v->value_name) != 0) {
+ return WERR_NOT_FOUND;
+ }
+
+ if (v->type != REG_MULTI_SZ) {
+ return WERR_INVALID_DATATYPE;
+ }
+
+ if (v->data_length == 0) {
+ *array = talloc_array(mem_ctx, const char *, 1);
+ if (*array == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ *array[0] = NULL;
+ return WERR_OK;
+ }
+
+ if (!pull_reg_multi_sz(mem_ctx, v->data, array)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR winreg_printer_write_date(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ struct policy_handle *key_handle,
+ const char *value,
+ NTTIME data)
+{
+ struct winreg_String wvalue = { 0, };
+ DATA_BLOB blob;
+ WERROR result = WERR_OK;
+ NTSTATUS status;
+ const char *str;
+ struct tm *tm;
+ time_t t;
+
+ if (data == 0) {
+ str = talloc_strdup(mem_ctx, "01/01/1601");
+ } else {
+ t = nt_time_to_unix(data);
+ tm = localtime(&t);
+ if (tm == NULL) {
+ return map_werror_from_unix(errno);
+ }
+ str = talloc_asprintf(mem_ctx, "%02d/%02d/%04d",
+ tm->tm_mon + 1, tm->tm_mday, tm->tm_year + 1900);
+ }
+ if (!str) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ wvalue.name = value;
+ if (!push_reg_sz(mem_ctx, &blob, str)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ status = dcerpc_winreg_SetValue(winreg_handle,
+ mem_ctx,
+ key_handle,
+ wvalue,
+ REG_SZ,
+ blob.data,
+ blob.length,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_printer_write_date: Could not set value %s: %s\n",
+ wvalue.name, win_errstr(result)));
+ }
+
+ return result;
+}
+
+static WERROR winreg_printer_date_to_NTTIME(const char *str, NTTIME *data)
+{
+ bool ok;
+
+ ok = spoolss_timestr_to_NTTIME(str, data);
+ if (!ok) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR winreg_printer_write_ver(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ struct policy_handle *key_handle,
+ const char *value,
+ uint64_t data)
+{
+ struct winreg_String wvalue = { 0, };
+ DATA_BLOB blob;
+ WERROR result = WERR_OK;
+ NTSTATUS status;
+ char *str;
+
+ /*
+ * this needs to be something like: 6.1.7600.16385
+ */
+ str = talloc_asprintf(mem_ctx, "%u.%u.%u.%u",
+ (unsigned)((data >> 48) & 0xFFFF),
+ (unsigned)((data >> 32) & 0xFFFF),
+ (unsigned)((data >> 16) & 0xFFFF),
+ (unsigned)(data & 0xFFFF));
+ if (!str) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ wvalue.name = value;
+ if (!push_reg_sz(mem_ctx, &blob, str)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ status = dcerpc_winreg_SetValue(winreg_handle,
+ mem_ctx,
+ key_handle,
+ wvalue,
+ REG_SZ,
+ blob.data,
+ blob.length,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_printer_write_date: Could not set value %s: %s\n",
+ wvalue.name, win_errstr(result)));
+ }
+
+ return result;
+}
+
+static WERROR winreg_printer_ver_to_qword(const char *str, uint64_t *data)
+{
+ bool ok;
+
+ ok = spoolss_driver_version_to_qword(str, data);
+ if (!ok) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ Public winreg function for spoolss
+********************************************************************/
+
+WERROR winreg_create_printer(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *sharename)
+{
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ struct spoolss_SetPrinterInfo2 *info2;
+ struct security_descriptor *secdesc;
+ struct winreg_String wkey, wkeyclass;
+ const char *path;
+ const char *subkeys[] = { SPOOL_DSDRIVER_KEY, SPOOL_DSSPOOLER_KEY, SPOOL_PRINTERDATA_KEY };
+ uint32_t i, count = ARRAY_SIZE(subkeys);
+ uint32_t info2_mask = 0;
+ WERROR result = WERR_OK;
+ WERROR ignore;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ path = winreg_printer_data_keyname(tmp_ctx, sharename);
+ if (path == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+ result = winreg_printer_openkey(tmp_ctx,
+ winreg_handle,
+ path,
+ "",
+ false,
+ access_mask,
+ &hive_hnd,
+ &key_hnd);
+ if (W_ERROR_IS_OK(result)) {
+ DEBUG(2, ("winreg_create_printer: Skipping, %s already exists\n", path));
+ goto done;
+ } else if (W_ERROR_EQUAL(result, WERR_FILE_NOT_FOUND)) {
+ DEBUG(2, ("winreg_create_printer: Creating default values in %s\n", path));
+ } else if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_create_printer: Could not open key %s: %s\n",
+ path, win_errstr(result)));
+ goto done;
+ }
+
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+
+ /* Create the main key */
+ result = winreg_printer_openkey(tmp_ctx,
+ winreg_handle,
+ path,
+ "",
+ true,
+ access_mask,
+ &hive_hnd,
+ &key_hnd);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_create_printer_keys: Could not create key %s: %s\n",
+ path, win_errstr(result)));
+ goto done;
+ }
+
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &result);
+ }
+
+ /* Create subkeys */
+ for (i = 0; i < count; i++) {
+ NTSTATUS status;
+ enum winreg_CreateAction action = REG_ACTION_NONE;
+
+ ZERO_STRUCT(key_hnd);
+ ZERO_STRUCT(wkey);
+
+ wkey.name = talloc_asprintf(tmp_ctx, "%s\\%s", path, subkeys[i]);
+ if (wkey.name == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ ZERO_STRUCT(wkeyclass);
+ wkeyclass.name = "";
+
+ status = dcerpc_winreg_CreateKey(winreg_handle,
+ tmp_ctx,
+ &hive_hnd,
+ wkey,
+ wkeyclass,
+ 0,
+ access_mask,
+ NULL,
+ &key_hnd,
+ &action,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_create_printer_keys: Could not create key %s: %s\n",
+ wkey.name, win_errstr(result)));
+ goto done;
+ }
+
+ if (strequal(subkeys[i], SPOOL_DSSPOOLER_KEY)) {
+ const char *dnssuffix;
+ const char *longname;
+ const char *uncname;
+
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ SPOOL_REG_PRINTERNAME,
+ sharename,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ SPOOL_REG_PRINTSHARENAME,
+ sharename,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ SPOOL_REG_SHORTSERVERNAME,
+ lp_netbios_name(),
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ /* We make the assumption that the netbios name
+ * is the same as the DNS name since the former
+ * will be what we used to join the domain
+ */
+ dnssuffix = get_mydnsdomname(tmp_ctx);
+ if (dnssuffix != NULL && dnssuffix[0] != '\0') {
+ longname = talloc_asprintf(tmp_ctx, "%s.%s", lp_netbios_name(), dnssuffix);
+ } else {
+ longname = talloc_strdup(tmp_ctx, lp_netbios_name());
+ }
+ if (longname == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ SPOOL_REG_SERVERNAME,
+ longname,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ uncname = talloc_asprintf(tmp_ctx, "\\\\%s\\%s",
+ longname, sharename);
+ if (uncname == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ SPOOL_REG_UNCNAME,
+ uncname,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_dword(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ SPOOL_REG_VERSIONNUMBER,
+ 4,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_dword(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ SPOOL_REG_PRINTSTARTTIME,
+ 0,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_dword(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ SPOOL_REG_PRINTENDTIME,
+ 0,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_dword(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ SPOOL_REG_PRIORITY,
+ 1,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_dword(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ SPOOL_REG_PRINTKEEPPRINTEDJOBS,
+ 0,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &result);
+ }
+ }
+ info2 = talloc_zero(tmp_ctx, struct spoolss_SetPrinterInfo2);
+ if (info2 == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ info2->printername = sharename;
+ if (info2->printername == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+ info2_mask |= SPOOLSS_PRINTER_INFO_PRINTERNAME;
+
+ info2->sharename = sharename;
+ info2_mask |= SPOOLSS_PRINTER_INFO_SHARENAME;
+
+ info2->portname = SAMBA_PRINTER_PORT_NAME;
+ info2_mask |= SPOOLSS_PRINTER_INFO_PORTNAME;
+
+ info2->printprocessor = "winprint";
+ info2_mask |= SPOOLSS_PRINTER_INFO_PRINTPROCESSOR;
+
+ info2->datatype = "RAW";
+ info2_mask |= SPOOLSS_PRINTER_INFO_DATATYPE;
+
+ info2->comment = "";
+ info2_mask |= SPOOLSS_PRINTER_INFO_COMMENT;
+
+ info2->attributes = PRINTER_ATTRIBUTE_SAMBA;
+ info2_mask |= SPOOLSS_PRINTER_INFO_ATTRIBUTES;
+
+ info2->starttime = 0; /* Minutes since 12:00am GMT */
+ info2_mask |= SPOOLSS_PRINTER_INFO_STARTTIME;
+
+ info2->untiltime = 0; /* Minutes since 12:00am GMT */
+ info2_mask |= SPOOLSS_PRINTER_INFO_UNTILTIME;
+
+ info2->priority = 1;
+ info2_mask |= SPOOLSS_PRINTER_INFO_PRIORITY;
+
+ info2->defaultpriority = 1;
+ info2_mask |= SPOOLSS_PRINTER_INFO_DEFAULTPRIORITY;
+
+ result = spoolss_create_default_secdesc(tmp_ctx, &secdesc);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+ info2_mask |= SPOOLSS_PRINTER_INFO_SECDESC;
+
+ /*
+ * Don't write a default Device Mode to the registry! The Device Mode is
+ * only written to disk with a SetPrinter level 2 or 8.
+ */
+
+ result = winreg_update_printer(tmp_ctx,
+ winreg_handle,
+ sharename,
+ info2_mask,
+ info2,
+ NULL,
+ secdesc);
+
+done:
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_update_printer(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *sharename,
+ uint32_t info2_mask,
+ struct spoolss_SetPrinterInfo2 *info2,
+ struct spoolss_DeviceMode *devmode,
+ struct security_descriptor *secdesc)
+{
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ int snum = lp_servicenumber(sharename);
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ char *path;
+ WERROR result = WERR_OK;
+ WERROR ignore;
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ path = winreg_printer_data_keyname(tmp_ctx, sharename);
+ if (path == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+ result = winreg_printer_openkey(tmp_ctx,
+ winreg_handle,
+ path,
+ "",
+ true,
+ access_mask,
+ &hive_hnd,
+ &key_hnd);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_update_printer: Could not open key %s: %s\n",
+ path, win_errstr(result)));
+ goto done;
+ }
+
+ if (info2_mask & SPOOLSS_PRINTER_INFO_ATTRIBUTES) {
+ status = dcerpc_winreg_set_dword(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Attributes",
+ info2->attributes,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+#if 0
+ if (info2_mask & SPOOLSS_PRINTER_INFO_AVERAGEPPM) {
+ status = dcerpc_winreg_set_dword(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "AveragePpm",
+ info2->attributes,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+ }
+#endif
+
+ if (info2_mask & SPOOLSS_PRINTER_INFO_COMMENT) {
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Description",
+ info2->comment,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ if (info2_mask & SPOOLSS_PRINTER_INFO_DATATYPE) {
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Datatype",
+ info2->datatype,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ if (info2_mask & SPOOLSS_PRINTER_INFO_DEFAULTPRIORITY) {
+ status = dcerpc_winreg_set_dword(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Default Priority",
+ info2->defaultpriority,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ if (info2_mask & SPOOLSS_PRINTER_INFO_DEVMODE) {
+ /*
+ * Some client drivers freak out if there is a NULL devmode
+ * (probably the driver is not checking before accessing
+ * the devmode pointer) --jerry
+ */
+ if (devmode == NULL && lp_default_devmode(snum) && info2 != NULL) {
+ result = spoolss_create_default_devmode(tmp_ctx,
+ info2->printername,
+ &devmode);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ if (devmode->size != (ndr_size_spoolss_DeviceMode(devmode, 0) - devmode->__driverextra_length)) {
+ result = WERR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, devmode,
+ (ndr_push_flags_fn_t) ndr_push_spoolss_DeviceMode);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(0, ("winreg_update_printer: Failed to marshall device mode\n"));
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_binary(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Default DevMode",
+ &blob,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ if (info2_mask & SPOOLSS_PRINTER_INFO_DRIVERNAME) {
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Printer Driver",
+ info2->drivername,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ if (info2_mask & SPOOLSS_PRINTER_INFO_LOCATION) {
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Location",
+ info2->location,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ if (info2_mask & SPOOLSS_PRINTER_INFO_PARAMETERS) {
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Parameters",
+ info2->parameters,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ if (info2_mask & SPOOLSS_PRINTER_INFO_PORTNAME) {
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Port",
+ info2->portname,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ if (info2_mask & SPOOLSS_PRINTER_INFO_PRINTERNAME) {
+ /*
+ * in addprinter: no servername and the printer is the name
+ * in setprinter: servername is \\server
+ * and printer is \\server\\printer
+ *
+ * Samba manages only local printers.
+ * we currently don't support things like i
+ * path=\\other_server\printer
+ *
+ * We only store the printername, not \\server\printername
+ */
+ const char *p = strrchr(info2->printername, '\\');
+ if (p == NULL) {
+ p = info2->printername;
+ } else {
+ p++;
+ }
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Name",
+ p,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ if (info2_mask & SPOOLSS_PRINTER_INFO_PRINTPROCESSOR) {
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Print Processor",
+ info2->printprocessor,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ if (info2_mask & SPOOLSS_PRINTER_INFO_PRIORITY) {
+ status = dcerpc_winreg_set_dword(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Priority",
+ info2->priority,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ if (info2_mask & SPOOLSS_PRINTER_INFO_SECDESC) {
+ /*
+ * We need a security descriptor, if it isn't specified by
+ * AddPrinter{Ex} then create a default descriptor.
+ */
+ if (secdesc == NULL) {
+ result = spoolss_create_default_secdesc(tmp_ctx, &secdesc);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+ }
+ result = winreg_set_printer_secdesc(tmp_ctx,
+ winreg_handle,
+ sharename,
+ secdesc);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ if (info2_mask & SPOOLSS_PRINTER_INFO_SEPFILE) {
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Separator File",
+ info2->sepfile,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ if (info2_mask & SPOOLSS_PRINTER_INFO_SHARENAME) {
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Share Name",
+ info2->sharename,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ if (info2_mask & SPOOLSS_PRINTER_INFO_STARTTIME) {
+ status = dcerpc_winreg_set_dword(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "StartTime",
+ info2->starttime,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ if (info2_mask & SPOOLSS_PRINTER_INFO_STATUS) {
+ status = dcerpc_winreg_set_dword(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Status",
+ info2->status,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ if (info2_mask & SPOOLSS_PRINTER_INFO_UNTILTIME) {
+ status = dcerpc_winreg_set_dword(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "UntilTime",
+ info2->untiltime,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ status = dcerpc_winreg_set_dword(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "ChangeID",
+ winreg_printer_rev_changeid(),
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ result = WERR_OK;
+done:
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_get_printer(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *printer,
+ struct spoolss_PrinterInfo2 **pinfo2)
+{
+ struct spoolss_PrinterInfo2 *info2;
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd = { .handle_type = 0 };
+ struct policy_handle key_hnd = { .handle_type = 0 };
+ struct spoolss_PrinterEnumValues enum_value;
+ struct spoolss_PrinterEnumValues *v = NULL;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ int snum = lp_servicenumber(printer);
+ uint32_t num_values = 0;
+ uint32_t i;
+ char *path;
+ NTSTATUS status;
+ WERROR result = WERR_OK;
+ WERROR ignore;
+ const char **enum_names = NULL;
+ enum winreg_Type *enum_types = NULL;
+ DATA_BLOB *enum_data_blobs = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+
+ path = winreg_printer_data_keyname(tmp_ctx, printer);
+ if (path == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ result = winreg_printer_openkey(tmp_ctx,
+ winreg_handle,
+ path,
+ "",
+ false,
+ access_mask,
+ &hive_hnd,
+ &key_hnd);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(2, ("winreg_get_printer: Could not open key %s: %s\n",
+ path, win_errstr(result)));
+ goto done;
+ }
+
+ status = dcerpc_winreg_enumvals(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ &num_values,
+ &enum_names,
+ &enum_types,
+ &enum_data_blobs,
+ &result);
+ if (!NT_STATUS_IS_OK(status)){
+ result = ntstatus_to_werror(status);
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_get_printer: Could not enumerate values in %s: %s\n",
+ path, win_errstr(result)));
+ goto done;
+ }
+
+ result = WERR_NOT_ENOUGH_MEMORY;
+
+ info2 = talloc_zero(tmp_ctx, struct spoolss_PrinterInfo2);
+ if (info2 == NULL) {
+ goto done;
+ }
+
+ info2->servername = talloc_strdup(info2, "");
+ if (info2->servername == NULL) {
+ goto done;
+ }
+ info2->printername = talloc_strdup(info2, "");
+ if (info2->printername == NULL) {
+ goto done;
+ }
+ info2->sharename = talloc_strdup(info2, "");
+ if (info2->sharename == NULL) {
+ goto done;
+ }
+ info2->portname = talloc_strdup(info2, "");
+ if (info2->portname == NULL) {
+ goto done;
+ }
+ info2->drivername = talloc_strdup(info2, "");
+ if (info2->drivername == NULL) {
+ goto done;
+ }
+ info2->comment = talloc_strdup(info2, "");
+ if (info2->comment == NULL) {
+ goto done;
+ }
+ info2->location = talloc_strdup(info2, "");
+ if (info2->location == NULL) {
+ goto done;
+ }
+ info2->sepfile = talloc_strdup(info2, "");
+ if (info2->sepfile == NULL) {
+ goto done;
+ }
+ info2->printprocessor = talloc_strdup(info2, "");
+ if (info2->printprocessor == NULL) {
+ goto done;
+ }
+ info2->datatype = talloc_strdup(info2, "");
+ if (info2->datatype == NULL) {
+ goto done;
+ }
+ info2->parameters = talloc_strdup(info2, "");
+ if (info2->parameters == NULL) {
+ goto done;
+ }
+
+ for (i = 0; i < num_values; i++) {
+ enum_value.value_name = enum_names[i];
+ enum_value.value_name_len = 2*strlen_m_term(enum_names[i]);
+ enum_value.type = enum_types[i];
+ enum_value.data_length = enum_data_blobs[i].length;
+ enum_value.data = NULL;
+ if (enum_value.data_length != 0){
+ enum_value.data = &enum_data_blobs[i];
+ }
+ v = &enum_value;
+
+ result = winreg_enumval_to_sz(info2,
+ v,
+ "Name",
+ &info2->printername);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(info2,
+ v,
+ "Share Name",
+ &info2->sharename);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(info2,
+ v,
+ "Port",
+ &info2->portname);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(info2,
+ v,
+ "Description",
+ &info2->comment);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(info2,
+ v,
+ "Location",
+ &info2->location);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(info2,
+ v,
+ "Separator File",
+ &info2->sepfile);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(info2,
+ v,
+ "Print Processor",
+ &info2->printprocessor);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(info2,
+ v,
+ "Datatype",
+ &info2->datatype);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(info2,
+ v,
+ "Parameters",
+ &info2->parameters);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(info2,
+ v,
+ "Printer Driver",
+ &info2->drivername);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_dword(info2,
+ v,
+ "Attributes",
+ &info2->attributes);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_dword(info2,
+ v,
+ "Priority",
+ &info2->priority);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_dword(info2,
+ v,
+ "Default Priority",
+ &info2->defaultpriority);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_dword(info2,
+ v,
+ "StartTime",
+ &info2->starttime);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_dword(info2,
+ v,
+ "UntilTime",
+ &info2->untiltime);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_dword(info2,
+ v,
+ "Status",
+ &info2->status);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_dword(info2,
+ v,
+ "StartTime",
+ &info2->starttime);
+ CHECK_ERROR(result);
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_get_printer: winreg_enumval_to_TYPE() failed "
+ "for %s: %s\n",
+ v->value_name,
+ win_errstr(result)));
+ goto done;
+ }
+
+ /* Construct the Device Mode */
+ status = dcerpc_winreg_query_binary(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Default DevMode",
+ &blob,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (W_ERROR_IS_OK(result)) {
+ info2->devmode = talloc_zero(info2, struct spoolss_DeviceMode);
+ if (info2->devmode == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+ ndr_err = ndr_pull_struct_blob(&blob,
+ info2->devmode,
+ info2->devmode,
+ (ndr_pull_flags_fn_t) ndr_pull_spoolss_DeviceMode);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(0, ("winreg_get_printer: Failed to unmarshall device mode\n"));
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+ }
+
+ if (info2->devmode == NULL && lp_default_devmode(snum)) {
+ result = spoolss_create_default_devmode(info2,
+ info2->printername,
+ &info2->devmode);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ if (info2->devmode) {
+ info2->devmode->size = ndr_size_spoolss_DeviceMode(info2->devmode, 0) - info2->devmode->driverextra_data.length;
+ }
+
+ result = winreg_get_printer_secdesc(info2,
+ winreg_handle,
+ printer,
+ &info2->secdesc);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Fix for OS/2 drivers. */
+ if (get_remote_arch() == RA_OS2) {
+ spoolss_map_to_os2_driver(info2, &info2->drivername);
+ }
+
+ if (pinfo2) {
+ *pinfo2 = talloc_move(mem_ctx, &info2);
+ }
+
+ result = WERR_OK;
+done:
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
+
+static WERROR winreg_get_secdesc(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *path,
+ const char *attribute,
+ struct spoolss_security_descriptor **psecdesc)
+{
+ struct spoolss_security_descriptor *secdesc;
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ TALLOC_CTX *tmp_ctx;
+ NTSTATUS status;
+ WERROR result;
+ WERROR ignore;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+ result = winreg_printer_openkey(tmp_ctx,
+ winreg_handle,
+ path,
+ "",
+ false,
+ access_mask,
+ &hive_hnd,
+ &key_hnd);
+ if (!W_ERROR_IS_OK(result)) {
+ if (W_ERROR_EQUAL(result, WERR_FILE_NOT_FOUND)) {
+ goto create_default;
+ }
+ goto done;
+ }
+
+ status = dcerpc_winreg_query_sd(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ attribute,
+ &secdesc,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ if (W_ERROR_EQUAL(result, WERR_FILE_NOT_FOUND)) {
+
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle,
+ tmp_ctx,
+ &key_hnd,
+ &ignore);
+ }
+
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle,
+ tmp_ctx,
+ &hive_hnd,
+ &ignore);
+ }
+ goto create_default;
+ }
+ goto done;
+ }
+
+ if (psecdesc) {
+ *psecdesc = talloc_move(mem_ctx, &secdesc);
+ }
+
+ result = WERR_OK;
+ goto done;
+
+create_default:
+ result = winreg_printer_openkey(tmp_ctx,
+ winreg_handle,
+ path,
+ "",
+ true,
+ access_mask,
+ &hive_hnd,
+ &key_hnd);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ result = spoolss_create_default_secdesc(tmp_ctx, &secdesc);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ /* If security descriptor is owned by S-1-1-0 and winbindd is up,
+ this security descriptor has been created when winbindd was
+ down. Take ownership of security descriptor. */
+ if (dom_sid_equal(secdesc->owner_sid, &global_sid_World)) {
+ struct dom_sid owner_sid;
+
+ /* Change sd owner to workgroup administrator */
+
+ if (secrets_fetch_domain_sid(lp_workgroup(), &owner_sid)) {
+ struct spoolss_security_descriptor *new_secdesc;
+ size_t size;
+
+ /* Create new sd */
+ sid_append_rid(&owner_sid, DOMAIN_RID_ADMINISTRATOR);
+
+ new_secdesc = make_sec_desc(tmp_ctx,
+ secdesc->revision,
+ secdesc->type,
+ &owner_sid,
+ secdesc->group_sid,
+ secdesc->sacl,
+ secdesc->dacl,
+ &size);
+
+ if (new_secdesc == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ /* Swap with other one */
+ secdesc = new_secdesc;
+ }
+ }
+
+ status = dcerpc_winreg_set_sd(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ attribute,
+ secdesc,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ if (psecdesc) {
+ *psecdesc = talloc_move(mem_ctx, &secdesc);
+ }
+
+ result = WERR_OK;
+done:
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_get_printer_secdesc(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *sharename,
+ struct spoolss_security_descriptor **psecdesc)
+{
+ WERROR result;
+ char *path;
+
+ path = winreg_printer_data_keyname(mem_ctx, sharename);
+ if (path == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_get_secdesc(mem_ctx, winreg_handle,
+ path,
+ "Security",
+ psecdesc);
+ talloc_free(path);
+
+ return result;
+}
+
+WERROR winreg_get_printserver_secdesc(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ struct spoolss_security_descriptor **psecdesc)
+{
+ return winreg_get_secdesc(mem_ctx, winreg_handle,
+ TOP_LEVEL_CONTROL_KEY,
+ "ServerSecurityDescriptor",
+ psecdesc);
+}
+
+static WERROR winreg_set_secdesc(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *path,
+ const char *attribute,
+ const struct spoolss_security_descriptor *secdesc)
+{
+ const struct spoolss_security_descriptor *new_secdesc = secdesc;
+ struct spoolss_security_descriptor *old_secdesc;
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ TALLOC_CTX *tmp_ctx;
+ NTSTATUS status;
+ WERROR result;
+ WERROR ignore;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /*
+ * The old owner and group sids of the security descriptor are not
+ * present when new ACEs are added or removed by changing printer
+ * permissions through NT. If they are NULL in the new security
+ * descriptor then copy them over from the old one.
+ */
+ if (!secdesc->owner_sid || !secdesc->group_sid) {
+ struct dom_sid *owner_sid, *group_sid;
+ struct security_acl *dacl, *sacl;
+ size_t size;
+
+ result = winreg_get_secdesc(tmp_ctx,
+ winreg_handle,
+ path,
+ attribute,
+ &old_secdesc);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ /* Pick out correct owner and group sids */
+ owner_sid = secdesc->owner_sid ?
+ secdesc->owner_sid :
+ old_secdesc->owner_sid;
+
+ group_sid = secdesc->group_sid ?
+ secdesc->group_sid :
+ old_secdesc->group_sid;
+
+ dacl = secdesc->dacl ?
+ secdesc->dacl :
+ old_secdesc->dacl;
+
+ sacl = secdesc->sacl ?
+ secdesc->sacl :
+ old_secdesc->sacl;
+
+ /* Make a deep copy of the security descriptor */
+ new_secdesc = make_sec_desc(tmp_ctx,
+ secdesc->revision,
+ secdesc->type,
+ owner_sid,
+ group_sid,
+ sacl,
+ dacl,
+ &size);
+ if (new_secdesc == NULL) {
+ talloc_free(tmp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+ result = winreg_printer_openkey(tmp_ctx,
+ winreg_handle,
+ path,
+ "",
+ false,
+ access_mask,
+ &hive_hnd,
+ &key_hnd);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_sd(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ attribute,
+ new_secdesc,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+
+done:
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_set_printer_secdesc(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *sharename,
+ const struct spoolss_security_descriptor *secdesc)
+{
+ char *path;
+ WERROR result;
+
+ path = winreg_printer_data_keyname(mem_ctx, sharename);
+ if (path == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_set_secdesc(mem_ctx, winreg_handle,
+ path,
+ "Security", secdesc);
+ talloc_free(path);
+
+ return result;
+}
+
+WERROR winreg_set_printserver_secdesc(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const struct spoolss_security_descriptor *secdesc)
+{
+ return winreg_set_secdesc(mem_ctx, winreg_handle,
+ TOP_LEVEL_CONTROL_KEY,
+ "ServerSecurityDescriptor",
+ secdesc);
+}
+
+/* Set printer data over the winreg pipe. */
+WERROR winreg_set_printer_dataex(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *printer,
+ const char *key,
+ const char *value,
+ enum winreg_Type type,
+ uint8_t *data,
+ uint32_t data_size)
+{
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ struct winreg_String wvalue = { 0, };
+ char *path;
+ WERROR result = WERR_OK;
+ WERROR ignore;
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ path = winreg_printer_data_keyname(tmp_ctx, printer);
+ if (path == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+ DEBUG(8, ("winreg_set_printer_dataex: Open printer key %s, value %s, access_mask: 0x%05x for [%s]\n",
+ key, value, access_mask, printer));
+ result = winreg_printer_openkey(tmp_ctx,
+ winreg_handle,
+ path,
+ key,
+ true,
+ access_mask,
+ &hive_hnd,
+ &key_hnd);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_set_printer_dataex: Could not open key %s: %s\n",
+ key, win_errstr(result)));
+ goto done;
+ }
+
+ wvalue.name = value;
+ status = dcerpc_winreg_SetValue(winreg_handle,
+ tmp_ctx,
+ &key_hnd,
+ wvalue,
+ type,
+ data,
+ data_size,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("winreg_set_printer_dataex: Could not set value %s: %s\n",
+ value, nt_errstr(status)));
+ result = ntstatus_to_werror(status);
+ }
+
+done:
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
+
+/* Get printer data over a winreg pipe. */
+WERROR winreg_get_printer_dataex(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *printer,
+ const char *key,
+ const char *value,
+ enum winreg_Type *type,
+ uint8_t **data,
+ uint32_t *data_size)
+{
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ struct winreg_String wvalue;
+ enum winreg_Type type_in = REG_NONE;
+ char *path;
+ uint8_t *data_in = NULL;
+ uint32_t data_in_size = 0;
+ uint32_t value_len = 0;
+ WERROR result = WERR_OK;
+ WERROR ignore;
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ path = winreg_printer_data_keyname(tmp_ctx, printer);
+ if (path == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+ result = winreg_printer_openkey(tmp_ctx,
+ winreg_handle,
+ path,
+ key,
+ false,
+ access_mask,
+ &hive_hnd,
+ &key_hnd);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(2, ("winreg_get_printer_dataex: Could not open key %s: %s\n",
+ key, win_errstr(result)));
+ goto done;
+ }
+
+ wvalue.name = value;
+
+ /*
+ * call QueryValue once with data == NULL to get the
+ * needed memory size to be allocated, then allocate
+ * data buffer and call again.
+ */
+ status = dcerpc_winreg_QueryValue(winreg_handle,
+ tmp_ctx,
+ &key_hnd,
+ &wvalue,
+ &type_in,
+ NULL,
+ &data_in_size,
+ &value_len,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("winreg_get_printer_dataex: Could not query value %s: %s\n",
+ value, nt_errstr(status)));
+ result = ntstatus_to_werror(status);
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(2, ("winreg_get_printer_dataex: Could not query value %s: %s\n",
+ value, win_errstr(result)));
+ goto done;
+ }
+
+ data_in = (uint8_t *) TALLOC(tmp_ctx, data_in_size);
+ if (data_in == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+ value_len = 0;
+
+ status = dcerpc_winreg_QueryValue(winreg_handle,
+ tmp_ctx,
+ &key_hnd,
+ &wvalue,
+ &type_in,
+ data_in,
+ &data_in_size,
+ &value_len,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("winreg_get_printer_dataex: Could not query value %s: %s\n",
+ value, nt_errstr(status)));
+ result = ntstatus_to_werror(status);
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(2, ("winreg_get_printer_dataex: Could not query value %s: %s\n",
+ value, win_errstr(result)));
+ goto done;
+ }
+
+ *type = type_in;
+ *data_size = data_in_size;
+ if (data_in_size) {
+ *data = talloc_move(mem_ctx, &data_in);
+ }
+
+ result = WERR_OK;
+done:
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
+
+/* Enumerate on the values of a given key and provide the data. */
+WERROR winreg_enum_printer_dataex(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *printer,
+ const char *key,
+ uint32_t *pnum_values,
+ struct spoolss_PrinterEnumValues **penum_values)
+{
+ uint32_t i;
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+
+ struct spoolss_PrinterEnumValues *enum_values = NULL;
+ uint32_t num_values = 0;
+ char *path;
+ WERROR result = WERR_OK;
+ WERROR ignore;
+ NTSTATUS status;
+ const char **enum_names = NULL;
+ enum winreg_Type *enum_types = NULL;
+ DATA_BLOB *enum_data_blobs = NULL;
+
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ path = winreg_printer_data_keyname(tmp_ctx, printer);
+ if (path == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_openkey(tmp_ctx,
+ winreg_handle,
+ path,
+ key,
+ false,
+ access_mask,
+ &hive_hnd,
+ &key_hnd);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(2, ("winreg_enum_printer_dataex: Could not open key %s: %s\n",
+ key, win_errstr(result)));
+ goto done;
+ }
+
+ status = dcerpc_winreg_enumvals(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ &num_values,
+ &enum_names,
+ &enum_types,
+ &enum_data_blobs,
+ &result);
+ if (!NT_STATUS_IS_OK(status)){
+ result = ntstatus_to_werror(status);
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_enum_printer_dataex: Could not enumerate values in %s: %s\n",
+ key, win_errstr(result)));
+ goto done;
+ }
+
+ enum_values = talloc_array(tmp_ctx, struct spoolss_PrinterEnumValues, num_values);
+ if (enum_values == NULL){
+ result = WERR_NOT_ENOUGH_MEMORY;
+ DEBUG(0, ("winreg_enum_printer_dataex: Could not enumerate values in %s: %s\n",
+ key, win_errstr(result)));
+ goto done;
+ }
+
+ for (i = 0; i < num_values; i++){
+ enum_values[i].value_name = enum_names[i];
+ enum_values[i].value_name_len = strlen_m_term(enum_names[i]) * 2;
+ enum_values[i].type = enum_types[i];
+ enum_values[i].data_length = enum_data_blobs[i].length;
+ enum_values[i].data = NULL;
+
+ if (enum_values[i].data_length != 0){
+ enum_values[i].data = &enum_data_blobs[i];
+ }
+ }
+
+ talloc_steal(enum_values, enum_names);
+ talloc_steal(enum_values, enum_data_blobs);
+
+ *pnum_values = num_values;
+ if (penum_values) {
+ *penum_values = talloc_move(mem_ctx, &enum_values);
+ }
+
+ result = WERR_OK;
+done:
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
+
+/* Delete printer data over a winreg pipe. */
+WERROR winreg_delete_printer_dataex(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *printer,
+ const char *key,
+ const char *value)
+{
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ struct winreg_String wvalue = { 0, };
+ char *path;
+ WERROR result = WERR_OK;
+ WERROR ignore;
+ NTSTATUS status;
+
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ path = winreg_printer_data_keyname(tmp_ctx, printer);
+ if (path == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+ result = winreg_printer_openkey(tmp_ctx,
+ winreg_handle,
+ path,
+ key,
+ false,
+ access_mask,
+ &hive_hnd,
+ &key_hnd);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_delete_printer_dataex: Could not open key %s: %s\n",
+ key, win_errstr(result)));
+ goto done;
+ }
+
+ wvalue.name = value;
+ status = dcerpc_winreg_DeleteValue(winreg_handle,
+ tmp_ctx,
+ &key_hnd,
+ wvalue,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("winreg_delete_printer_dataex: Could not delete value %s: %s\n",
+ value, nt_errstr(status)));
+ result = ntstatus_to_werror(status);
+ }
+
+done:
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
+
+/* Enumerate on the subkeys of a given key and provide the data. */
+WERROR winreg_enum_printer_key(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *printer,
+ const char *key,
+ uint32_t *pnum_subkeys,
+ const char ***psubkeys)
+{
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ char *path;
+ const char **subkeys = NULL;
+ uint32_t num_subkeys = -1;
+
+ WERROR result = WERR_OK;
+ WERROR ignore;
+ NTSTATUS status;
+
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ path = winreg_printer_data_keyname(tmp_ctx, printer);
+ if (path == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+ result = winreg_printer_openkey(tmp_ctx,
+ winreg_handle,
+ path,
+ key,
+ false,
+ access_mask,
+ &hive_hnd,
+ &key_hnd);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(2, ("winreg_enum_printer_key: Could not open key %s: %s\n",
+ key, win_errstr(result)));
+ goto done;
+ }
+
+ status = dcerpc_winreg_enum_keys(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ &num_subkeys,
+ &subkeys,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_enum_printer_key: Could not enumerate subkeys in %s: %s\n",
+ key, win_errstr(result)));
+ goto done;
+ }
+
+ *pnum_subkeys = num_subkeys;
+ if (psubkeys) {
+ *psubkeys = talloc_move(mem_ctx, &subkeys);
+ }
+
+ result = WERR_OK;
+done:
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
+
+/* Delete a key with subkeys of a given printer. */
+WERROR winreg_delete_printer_key(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *printer,
+ const char *key)
+{
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ char *keyname;
+ char *path;
+ WERROR result;
+ WERROR ignore;
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ path = winreg_printer_data_keyname(tmp_ctx, printer);
+ if (path == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_openkey(tmp_ctx,
+ winreg_handle,
+ path,
+ key,
+ false,
+ access_mask,
+ &hive_hnd,
+ &key_hnd);
+ if (!W_ERROR_IS_OK(result)) {
+ /* key doesn't exist */
+ if (W_ERROR_EQUAL(result, WERR_FILE_NOT_FOUND)) {
+ result = WERR_OK;
+ goto done;
+ }
+
+ DEBUG(0, ("winreg_delete_printer_key: Could not open key %s: %s\n",
+ key, win_errstr(result)));
+ goto done;
+ }
+
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &result);
+ }
+
+ if (key == NULL || key[0] == '\0') {
+ keyname = path;
+ } else {
+ keyname = talloc_asprintf(tmp_ctx,
+ "%s\\%s",
+ path,
+ key);
+ if (keyname == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+ }
+
+ status = dcerpc_winreg_delete_subkeys_recursive(tmp_ctx,
+ winreg_handle,
+ &hive_hnd,
+ access_mask,
+ keyname,
+ &result);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("winreg_delete_printer_key: Could not delete key %s: %s\n",
+ key, nt_errstr(status)));
+ result = ntstatus_to_werror(status);
+ goto done;
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_delete_printer_key: Could not delete key %s: %s\n",
+ key, win_errstr(result)));
+ goto done;
+ }
+
+done:
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_printer_update_changeid(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *printer)
+{
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ char *path;
+ NTSTATUS status;
+ WERROR result;
+ WERROR ignore;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ path = winreg_printer_data_keyname(tmp_ctx, printer);
+ if (path == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+ result = winreg_printer_openkey(tmp_ctx,
+ winreg_handle,
+ path,
+ "",
+ false,
+ access_mask,
+ &hive_hnd,
+ &key_hnd);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_printer_update_changeid: Could not open key %s: %s\n",
+ path, win_errstr(result)));
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_dword(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "ChangeID",
+ winreg_printer_rev_changeid(),
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ result = WERR_OK;
+done:
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_printer_get_changeid(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *printer,
+ uint32_t *pchangeid)
+{
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ uint32_t changeid = 0;
+ char *path;
+ NTSTATUS status;
+ WERROR result;
+ WERROR ignore;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ path = winreg_printer_data_keyname(tmp_ctx, printer);
+ if (path == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+ result = winreg_printer_openkey(tmp_ctx,
+ winreg_handle,
+ path,
+ "",
+ false,
+ access_mask,
+ &hive_hnd,
+ &key_hnd);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(2, ("winreg_printer_get_changeid: Could not open key %s: %s\n",
+ path, win_errstr(result)));
+ goto done;
+ }
+
+ DEBUG(10, ("winreg_printer_get_changeid: get changeid from %s\n", path));
+
+ status = dcerpc_winreg_query_dword(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "ChangeID",
+ &changeid,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ if (pchangeid) {
+ *pchangeid = changeid;
+ }
+
+ result = WERR_OK;
+done:
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
+
+/*
+ * The special behaviour of the spoolss forms is documented at the website:
+ *
+ * Managing Win32 Printserver Forms
+ * http://unixwiz.net/techtips/winspooler-forms.html
+ */
+
+WERROR winreg_printer_addform1(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ struct spoolss_AddFormInfo1 *form)
+{
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ struct winreg_String wvalue = { 0, };
+ DATA_BLOB blob;
+ uint32_t num_info = 0;
+ union spoolss_FormInfo *info = NULL;
+ uint32_t i;
+ WERROR result;
+ WERROR ignore;
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+ result = winreg_printer_openkey(tmp_ctx,
+ winreg_handle,
+ TOP_LEVEL_CONTROL_FORMS_KEY,
+ "",
+ true,
+ access_mask,
+ &hive_hnd,
+ &key_hnd);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_printer_addform1: Could not open key %s: %s\n",
+ TOP_LEVEL_CONTROL_FORMS_KEY, win_errstr(result)));
+ goto done;
+ }
+
+ result = winreg_printer_enumforms1(tmp_ctx, winreg_handle,
+ &num_info, &info);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_printer_addform: Could not enum keys %s: %s\n",
+ TOP_LEVEL_CONTROL_FORMS_KEY, win_errstr(result)));
+ goto done;
+ }
+
+ /* If form name already exists or is builtin return ALREADY_EXISTS */
+ for (i = 0; i < num_info; i++) {
+ if (strequal(info[i].info1.form_name, form->form_name)) {
+ result = WERR_FILE_EXISTS;
+ goto done;
+ }
+ }
+
+ wvalue.name = form->form_name;
+
+ blob = data_blob_talloc(tmp_ctx, NULL, 32);
+ SIVAL(blob.data, 0, form->size.width);
+ SIVAL(blob.data, 4, form->size.height);
+ SIVAL(blob.data, 8, form->area.left);
+ SIVAL(blob.data, 12, form->area.top);
+ SIVAL(blob.data, 16, form->area.right);
+ SIVAL(blob.data, 20, form->area.bottom);
+ SIVAL(blob.data, 24, num_info + 1); /* FIXME */
+ SIVAL(blob.data, 28, form->flags);
+
+ status = dcerpc_winreg_SetValue(winreg_handle,
+ tmp_ctx,
+ &key_hnd,
+ wvalue,
+ REG_BINARY,
+ blob.data,
+ blob.length,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("winreg_printer_addform1: Could not set value %s: %s\n",
+ wvalue.name, nt_errstr(status)));
+ result = ntstatus_to_werror(status);
+ }
+
+done:
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+
+ TALLOC_FREE(info);
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_printer_enumforms1(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ uint32_t *pnum_info,
+ union spoolss_FormInfo **pinfo)
+{
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ union spoolss_FormInfo *info;
+ struct spoolss_PrinterEnumValues *enum_values = NULL;
+ uint32_t num_values = 0;
+ uint32_t num_builtin = ARRAY_SIZE(builtin_forms1);
+ uint32_t i;
+ WERROR result;
+ WERROR ignore;
+ NTSTATUS status;
+ const char **enum_names = NULL;
+ enum winreg_Type *enum_types = NULL;
+ DATA_BLOB *enum_data_blobs = NULL;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+ result = winreg_printer_openkey(tmp_ctx,
+ winreg_handle,
+ TOP_LEVEL_CONTROL_FORMS_KEY,
+ "",
+ true,
+ access_mask,
+ &hive_hnd,
+ &key_hnd);
+ if (!W_ERROR_IS_OK(result)) {
+ /* key doesn't exist */
+ if (W_ERROR_EQUAL(result, WERR_FILE_NOT_FOUND)) {
+ result = WERR_OK;
+ goto done;
+ }
+
+ DEBUG(0, ("winreg_printer_enumforms1: Could not open key %s: %s\n",
+ TOP_LEVEL_CONTROL_FORMS_KEY, win_errstr(result)));
+ goto done;
+ }
+
+ status = dcerpc_winreg_enumvals(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ &num_values,
+ &enum_names,
+ &enum_types,
+ &enum_data_blobs,
+ &result);
+ if (!NT_STATUS_IS_OK(status)){
+ result = ntstatus_to_werror(status);
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_printer_enumforms1: Could not enumerate values in %s: %s\n",
+ TOP_LEVEL_CONTROL_FORMS_KEY, win_errstr(result)));
+ goto done;
+ }
+
+ enum_values = talloc_zero_array(tmp_ctx,
+ struct spoolss_PrinterEnumValues,
+ num_values);
+ if (enum_values == NULL){
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ for (i = 0; i < num_values; i++){
+ enum_values[i].value_name = enum_names[i];
+ enum_values[i].value_name_len = strlen_m_term(enum_names[i]) * 2;
+ enum_values[i].type = enum_types[i];
+ enum_values[i].data_length = enum_data_blobs[i].length;
+ enum_values[i].data = NULL;
+ if (enum_values[i].data_length != 0){
+ enum_values[i].data = &enum_data_blobs[i];
+ }
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_printer_enumforms1: Could not enumerate values in %s: %s\n",
+ TOP_LEVEL_CONTROL_FORMS_KEY, win_errstr(result)));
+ goto done;
+ }
+
+ info = talloc_array(tmp_ctx, union spoolss_FormInfo, num_builtin + num_values);
+ if (info == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ /* Enumerate BUILTIN forms */
+ for (i = 0; i < num_builtin; i++) {
+ info[i].info1 = builtin_forms1[i];
+ }
+
+ /* Enumerate registry forms */
+ for (i = 0; i < num_values; i++) {
+ union spoolss_FormInfo val;
+
+ if (enum_values[i].type != REG_BINARY ||
+ enum_values[i].data_length != 32) {
+ continue;
+ }
+
+ val.info1.form_name = talloc_strdup(info, enum_values[i].value_name);
+ if (val.info1.form_name == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ val.info1.size.width = IVAL(enum_values[i].data->data, 0);
+ val.info1.size.height = IVAL(enum_values[i].data->data, 4);
+ val.info1.area.left = IVAL(enum_values[i].data->data, 8);
+ val.info1.area.top = IVAL(enum_values[i].data->data, 12);
+ val.info1.area.right = IVAL(enum_values[i].data->data, 16);
+ val.info1.area.bottom = IVAL(enum_values[i].data->data, 20);
+ /* skip form index IVAL(enum_values[i].data->data, 24)));*/
+ val.info1.flags = (enum spoolss_FormFlags) IVAL(enum_values[i].data->data, 28);
+
+ info[i + num_builtin] = val;
+ }
+
+ *pnum_info = num_builtin + num_values;
+ if (pinfo) {
+ *pinfo = talloc_move(mem_ctx, &info);
+ }
+
+done:
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+
+ TALLOC_FREE(enum_values);
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_printer_deleteform1(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *form_name)
+{
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ struct winreg_String wvalue = { 0, };
+ uint32_t num_builtin = ARRAY_SIZE(builtin_forms1);
+ uint32_t i;
+ WERROR result = WERR_OK;
+ WERROR ignore;
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx;
+
+ for (i = 0; i < num_builtin; i++) {
+ if (strequal(builtin_forms1[i].form_name, form_name)) {
+ return WERR_INVALID_PARAMETER;
+ }
+ }
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+ result = winreg_printer_openkey(tmp_ctx,
+ winreg_handle,
+ TOP_LEVEL_CONTROL_FORMS_KEY,
+ "",
+ false,
+ access_mask,
+ &hive_hnd,
+ &key_hnd);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_printer_deleteform1: Could not open key %s: %s\n",
+ TOP_LEVEL_CONTROL_FORMS_KEY, win_errstr(result)));
+ if (W_ERROR_EQUAL(result, WERR_FILE_NOT_FOUND)) {
+ result = WERR_INVALID_FORM_NAME;
+ }
+ goto done;
+ }
+
+ wvalue.name = form_name;
+ status = dcerpc_winreg_DeleteValue(winreg_handle,
+ tmp_ctx,
+ &key_hnd,
+ wvalue,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* If the value doesn't exist, return WERR_INVALID_FORM_NAME */
+ DEBUG(0, ("winreg_printer_delteform1: Could not delete value %s: %s\n",
+ wvalue.name, nt_errstr(status)));
+ result = ntstatus_to_werror(status);
+ goto done;
+ }
+
+ if (W_ERROR_EQUAL(result, WERR_FILE_NOT_FOUND)) {
+ result = WERR_INVALID_FORM_NAME;
+ }
+
+done:
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_printer_setform1(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *form_name,
+ struct spoolss_AddFormInfo1 *form)
+{
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd = { 0, };
+ struct policy_handle key_hnd = { 0, };
+ struct winreg_String wvalue = { 0, };
+ DATA_BLOB blob;
+ uint32_t num_builtin = ARRAY_SIZE(builtin_forms1);
+ uint32_t i;
+ WERROR result;
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ for (i = 0; i < num_builtin; i++) {
+ if (strequal(builtin_forms1[i].form_name, form->form_name)) {
+ result = WERR_INVALID_PARAMETER;
+ goto done;
+ }
+ }
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+ result = winreg_printer_openkey(tmp_ctx,
+ winreg_handle,
+ TOP_LEVEL_CONTROL_FORMS_KEY,
+ "",
+ true,
+ access_mask,
+ &hive_hnd,
+ &key_hnd);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_printer_setform1: Could not open key %s: %s\n",
+ TOP_LEVEL_CONTROL_FORMS_KEY, win_errstr(result)));
+ goto done;
+ }
+
+ /* If form_name != form->form_name then we renamed the form */
+ if (strequal(form_name, form->form_name)) {
+ result = winreg_printer_deleteform1(tmp_ctx, winreg_handle,
+ form_name);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_printer_setform1: Could not open key %s: %s\n",
+ TOP_LEVEL_CONTROL_FORMS_KEY, win_errstr(result)));
+ goto done;
+ }
+ }
+
+ wvalue.name = form->form_name;
+
+ blob = data_blob_talloc(tmp_ctx, NULL, 32);
+ SIVAL(blob.data, 0, form->size.width);
+ SIVAL(blob.data, 4, form->size.height);
+ SIVAL(blob.data, 8, form->area.left);
+ SIVAL(blob.data, 12, form->area.top);
+ SIVAL(blob.data, 16, form->area.right);
+ SIVAL(blob.data, 20, form->area.bottom);
+ SIVAL(blob.data, 24, 42);
+ SIVAL(blob.data, 28, form->flags);
+
+ status = dcerpc_winreg_SetValue(winreg_handle,
+ tmp_ctx,
+ &key_hnd,
+ wvalue,
+ REG_BINARY,
+ blob.data,
+ blob.length,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("winreg_printer_setform1: Could not set value %s: %s\n",
+ wvalue.name, nt_errstr(status)));
+ result = ntstatus_to_werror(status);
+ }
+
+done:
+ if (winreg_handle != NULL) {
+ WERROR ignore;
+
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_printer_getform1(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *form_name,
+ struct spoolss_FormInfo1 *r)
+{
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ struct winreg_String wvalue;
+ enum winreg_Type type_in = REG_NONE;
+ uint8_t *data_in = NULL;
+ uint32_t data_in_size = 0;
+ uint32_t value_len = 0;
+ uint32_t num_builtin = ARRAY_SIZE(builtin_forms1);
+ uint32_t i;
+ WERROR result;
+ WERROR ignore;
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx;
+
+ /* check builtin forms first */
+ for (i = 0; i < num_builtin; i++) {
+ if (strequal(builtin_forms1[i].form_name, form_name)) {
+ *r = builtin_forms1[i];
+ return WERR_OK;
+ }
+ }
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+ result = winreg_printer_openkey(tmp_ctx,
+ winreg_handle,
+ TOP_LEVEL_CONTROL_FORMS_KEY,
+ "",
+ true,
+ access_mask,
+ &hive_hnd,
+ &key_hnd);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(2, ("winreg_printer_getform1: Could not open key %s: %s\n",
+ TOP_LEVEL_CONTROL_FORMS_KEY, win_errstr(result)));
+ goto done;
+ }
+
+ wvalue.name = form_name;
+
+ /*
+ * call QueryValue once with data == NULL to get the
+ * needed memory size to be allocated, then allocate
+ * data buffer and call again.
+ */
+ status = dcerpc_winreg_QueryValue(winreg_handle,
+ tmp_ctx,
+ &key_hnd,
+ &wvalue,
+ &type_in,
+ NULL,
+ &data_in_size,
+ &value_len,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("winreg_printer_getform1: Could not query value %s: %s\n",
+ wvalue.name, nt_errstr(status)));
+ result = ntstatus_to_werror(status);
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ data_in = (uint8_t *) TALLOC(tmp_ctx, data_in_size);
+ if (data_in == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+ value_len = 0;
+
+ status = dcerpc_winreg_QueryValue(winreg_handle,
+ tmp_ctx,
+ &key_hnd,
+ &wvalue,
+ &type_in,
+ data_in,
+ &data_in_size,
+ &value_len,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("winreg_printer_getform1: Could not query value %s: %s\n",
+ wvalue.name, nt_errstr(status)));
+ result = ntstatus_to_werror(status);
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ r->form_name = talloc_strdup(mem_ctx, form_name);
+ if (r->form_name == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ r->size.width = IVAL(data_in, 0);
+ r->size.height = IVAL(data_in, 4);
+ r->area.left = IVAL(data_in, 8);
+ r->area.top = IVAL(data_in, 12);
+ r->area.right = IVAL(data_in, 16);
+ r->area.bottom = IVAL(data_in, 20);
+ /* skip index IVAL(data_in, 24)));*/
+ r->flags = (enum spoolss_FormFlags) IVAL(data_in, 28);
+
+ result = WERR_OK;
+done:
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_add_driver(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ struct spoolss_AddDriverInfoCtr *r,
+ const char **driver_name,
+ uint32_t *driver_version)
+{
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ struct spoolss_DriverInfo8 info8;
+ TALLOC_CTX *tmp_ctx = NULL;
+ NTSTATUS status;
+ WERROR result;
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+ ZERO_STRUCT(info8);
+
+ if (!driver_info_ctr_to_info8(r, &info8)) {
+ result = WERR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_opendriver(tmp_ctx,
+ winreg_handle,
+ info8.driver_name,
+ info8.architecture,
+ info8.version,
+ access_mask, true,
+ &hive_hnd,
+ &key_hnd);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_add_driver: "
+ "Could not open driver key (%s,%s,%d): %s\n",
+ info8.driver_name, info8.architecture,
+ info8.version, win_errstr(result)));
+ goto done;
+ }
+
+ /* TODO: "Attributes" ? */
+
+ status = dcerpc_winreg_set_dword(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Version",
+ info8.version,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Driver",
+ info8.driver_path,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Data File",
+ info8.data_file,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Configuration File",
+ info8.config_file,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Help File",
+ info8.help_file,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_multi_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Dependent Files",
+ info8.dependent_files,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Monitor",
+ info8.monitor_name,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Datatype",
+ info8.default_datatype,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_multi_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd, "Previous Names",
+ info8.previous_names,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ result = winreg_printer_write_date(tmp_ctx, winreg_handle,
+ &key_hnd, "DriverDate",
+ info8.driver_date);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ result = winreg_printer_write_ver(tmp_ctx, winreg_handle,
+ &key_hnd, "DriverVersion",
+ info8.driver_version);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Manufacturer",
+ info8.manufacturer_name,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "OEM URL",
+ info8.manufacturer_url,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "HardwareID",
+ info8.hardware_id,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Provider",
+ info8.provider,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Print Processor",
+ info8.print_processor,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "VendorSetup",
+ info8.vendor_setup,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_multi_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "Color Profiles",
+ info8.color_profiles,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "InfPath",
+ info8.inf_path,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_dword(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "PrinterDriverAttributes",
+ info8.printer_driver_attributes,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_multi_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "CoreDependencies",
+ info8.core_driver_dependencies,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ result = winreg_printer_write_date(tmp_ctx, winreg_handle,
+ &key_hnd, "MinInboxDriverVerDate",
+ info8.min_inbox_driver_ver_date);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ result = winreg_printer_write_ver(tmp_ctx, winreg_handle, &key_hnd,
+ "MinInboxDriverVerVersion",
+ info8.min_inbox_driver_ver_version);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ *driver_name = info8.driver_name;
+ *driver_version = info8.version;
+ result = WERR_OK;
+done:
+ if (winreg_handle != NULL) {
+ WERROR ignore;
+
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_get_driver(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *architecture,
+ const char *driver_name,
+ uint32_t driver_version,
+ struct spoolss_DriverInfo8 **_info8)
+{
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ struct spoolss_DriverInfo8 i8, *info8 = NULL;
+ struct spoolss_PrinterEnumValues *enum_values = NULL;
+ struct spoolss_PrinterEnumValues *v = NULL;
+ uint32_t num_values = 0;
+ TALLOC_CTX *tmp_ctx = NULL;
+ WERROR result;
+ NTSTATUS status;
+ uint32_t i;
+ const char **enum_names = NULL;
+ enum winreg_Type *enum_types = NULL;
+ DATA_BLOB *enum_data_blobs = NULL;
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+ ZERO_STRUCT(i8);
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ if (driver_version == DRIVER_ANY_VERSION) {
+ /* look for Win2k first and then for NT4 */
+ result = winreg_printer_opendriver(tmp_ctx,
+ winreg_handle,
+ driver_name,
+ architecture,
+ 3,
+ access_mask, false,
+ &hive_hnd,
+ &key_hnd);
+ if (!W_ERROR_IS_OK(result)) {
+ result = winreg_printer_opendriver(tmp_ctx,
+ winreg_handle,
+ driver_name,
+ architecture,
+ 2,
+ access_mask, false,
+ &hive_hnd,
+ &key_hnd);
+ }
+ } else {
+ /* ok normal case */
+ result = winreg_printer_opendriver(tmp_ctx,
+ winreg_handle,
+ driver_name,
+ architecture,
+ driver_version,
+ access_mask, false,
+ &hive_hnd,
+ &key_hnd);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(5, ("winreg_get_driver: "
+ "Could not open driver key (%s,%s,%d): %s\n",
+ driver_name, architecture,
+ driver_version, win_errstr(result)));
+ goto done;
+ }
+
+ status = dcerpc_winreg_enumvals(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ &num_values,
+ &enum_names,
+ &enum_types,
+ &enum_data_blobs,
+ &result);
+ if (!NT_STATUS_IS_OK(status)){
+ result = ntstatus_to_werror(status);
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_get_driver: "
+ "Could not enumerate values for (%s,%s,%d): %s\n",
+ driver_name, architecture,
+ driver_version, win_errstr(result)));
+ goto done;
+ }
+
+ enum_values = talloc_zero_array(tmp_ctx,
+ struct spoolss_PrinterEnumValues,
+ num_values);
+ if (enum_values == NULL){
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ for (i = 0; i < num_values; i++){
+ enum_values[i].value_name = enum_names[i];
+ enum_values[i].value_name_len = strlen_m_term(enum_names[i]) * 2;
+ enum_values[i].type = enum_types[i];
+ enum_values[i].data_length = enum_data_blobs[i].length;
+ enum_values[i].data = NULL;
+ if (enum_values[i].data_length != 0){
+ enum_values[i].data = &enum_data_blobs[i];
+ }
+ }
+
+ info8 = talloc_zero(tmp_ctx, struct spoolss_DriverInfo8);
+ if (info8 == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ info8->driver_name = talloc_strdup(info8, driver_name);
+ if (info8->driver_name == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ info8->architecture = talloc_strdup(info8, architecture);
+ if (info8->architecture == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ result = WERR_OK;
+
+ for (i = 0; i < num_values; i++) {
+ const char *tmp_str;
+ uint32_t tmp = 0;
+
+ v = &enum_values[i];
+
+ result = winreg_enumval_to_dword(info8, v,
+ "Version",
+ &tmp);
+ if (W_ERROR_IS_OK(result)) {
+ info8->version = (enum spoolss_DriverOSVersion) tmp;
+ }
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(info8, v,
+ "Driver",
+ &info8->driver_path);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(info8, v,
+ "Data File",
+ &info8->data_file);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(info8, v,
+ "Configuration File",
+ &info8->config_file);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(info8, v,
+ "Help File",
+ &info8->help_file);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_multi_sz(info8, v,
+ "Dependent Files",
+ &info8->dependent_files);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(info8, v,
+ "Monitor",
+ &info8->monitor_name);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(info8, v,
+ "Datatype",
+ &info8->default_datatype);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_multi_sz(info8, v,
+ "Previous Names",
+ &info8->previous_names);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(info8, v,
+ "DriverDate",
+ &tmp_str);
+ if (W_ERROR_IS_OK(result)) {
+ result = winreg_printer_date_to_NTTIME(tmp_str,
+ &info8->driver_date);
+ }
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(info8, v,
+ "DriverVersion",
+ &tmp_str);
+ if (W_ERROR_IS_OK(result)) {
+ result = winreg_printer_ver_to_qword(tmp_str,
+ &info8->driver_version);
+ }
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(info8, v,
+ "Manufacturer",
+ &info8->manufacturer_name);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(info8, v,
+ "OEM URL",
+ &info8->manufacturer_url);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(info8, v,
+ "HardwareID",
+ &info8->hardware_id);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(info8, v,
+ "Provider",
+ &info8->provider);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(info8, v,
+ "Print Processor",
+ &info8->print_processor);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(info8, v,
+ "VendorSetup",
+ &info8->vendor_setup);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_multi_sz(info8, v,
+ "Color Profiles",
+ &info8->color_profiles);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(info8, v,
+ "InfPath",
+ &info8->inf_path);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_dword(info8, v,
+ "PrinterDriverAttributes",
+ &info8->printer_driver_attributes);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_multi_sz(info8, v,
+ "CoreDependencies",
+ &info8->core_driver_dependencies);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(info8, v,
+ "MinInboxDriverVerDate",
+ &tmp_str);
+ if (W_ERROR_IS_OK(result)) {
+ result = winreg_printer_date_to_NTTIME(tmp_str,
+ &info8->min_inbox_driver_ver_date);
+ }
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(info8, v,
+ "MinInboxDriverVerVersion",
+ &tmp_str);
+ if (W_ERROR_IS_OK(result)) {
+ result = winreg_printer_ver_to_qword(tmp_str,
+ &info8->min_inbox_driver_ver_version);
+ }
+ CHECK_ERROR(result);
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_enumval_to_TYPE() failed "
+ "for %s: %s\n", v->value_name,
+ win_errstr(result)));
+ goto done;
+ }
+
+ *_info8 = talloc_steal(mem_ctx, info8);
+ result = WERR_OK;
+done:
+ if (winreg_handle != NULL) {
+ WERROR ignore;
+
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_del_driver(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ struct spoolss_DriverInfo8 *info8,
+ uint32_t version)
+{
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ TALLOC_CTX *tmp_ctx;
+ char *key_name;
+ WERROR result;
+ NTSTATUS status;
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* test that the key exists */
+ result = winreg_printer_opendriver(tmp_ctx,
+ winreg_handle,
+ info8->driver_name,
+ info8->architecture,
+ version,
+ access_mask, false,
+ &hive_hnd,
+ &key_hnd);
+ if (!W_ERROR_IS_OK(result)) {
+ /* key doesn't exist */
+ if (W_ERROR_EQUAL(result, WERR_FILE_NOT_FOUND)) {
+ result = WERR_OK;
+ goto done;
+ }
+
+ DEBUG(5, ("winreg_del_driver: "
+ "Could not open driver (%s,%s,%u): %s\n",
+ info8->driver_name, info8->architecture,
+ version, win_errstr(result)));
+ goto done;
+ }
+
+
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &result);
+ }
+
+ key_name = talloc_asprintf(tmp_ctx,
+ "%s\\Environments\\%s\\Drivers\\Version-%u\\%s",
+ TOP_LEVEL_CONTROL_KEY,
+ info8->architecture, version,
+ info8->driver_name);
+ if (key_name == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ status = dcerpc_winreg_delete_subkeys_recursive(tmp_ctx,
+ winreg_handle,
+ &hive_hnd,
+ access_mask,
+ key_name,
+ &result);
+
+ if (!NT_STATUS_IS_OK(status)){
+ DEBUG(0, ("winreg_del_driver: "
+ "Could not open driver (%s,%s,%u): %s\n",
+ info8->driver_name, info8->architecture,
+ version, nt_errstr(status)));
+ goto done;
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_del_driver: "
+ "Could not open driver (%s,%s,%u): %s\n",
+ info8->driver_name, info8->architecture,
+ version, win_errstr(result)));
+ goto done;
+ }
+
+ result = WERR_OK;
+done:
+ if (winreg_handle != NULL) {
+ WERROR ignore;
+
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_get_driver_list(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *architecture,
+ uint32_t version,
+ uint32_t *num_drivers,
+ const char ***drivers_p)
+{
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ const char **drivers;
+ TALLOC_CTX *tmp_ctx;
+ WERROR result;
+ NTSTATUS status;
+
+ *num_drivers = 0;
+ *drivers_p = NULL;
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* use NULL for the driver name so we open the key that is
+ * parent of all drivers for this architecture and version */
+ result = winreg_printer_opendriver(tmp_ctx,
+ winreg_handle,
+ NULL,
+ architecture,
+ version,
+ access_mask, false,
+ &hive_hnd,
+ &key_hnd);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(5, ("winreg_get_driver_list: "
+ "Could not open key (%s,%u): %s\n",
+ architecture, version, win_errstr(result)));
+ result = WERR_OK;
+ goto done;
+ }
+
+ status = dcerpc_winreg_enum_keys(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ num_drivers,
+ &drivers,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_get_driver_list: "
+ "Could not enumerate drivers for (%s,%u): %s\n",
+ architecture, version, win_errstr(result)));
+ goto done;
+ }
+
+ *drivers_p = talloc_steal(mem_ctx, drivers);
+
+ result = WERR_OK;
+done:
+ if (winreg_handle != NULL) {
+ WERROR ignore;
+
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_get_core_driver(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *architecture,
+ const struct GUID *core_driver_guid,
+ struct spoolss_CorePrinterDriver **_core_printer_driver)
+{
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ struct spoolss_CorePrinterDriver *c = NULL;
+ struct spoolss_PrinterEnumValues *enum_values = NULL;
+ struct spoolss_PrinterEnumValues *v = NULL;
+ uint32_t num_values = 0;
+ TALLOC_CTX *tmp_ctx = NULL;
+ WERROR result;
+ NTSTATUS status;
+ const char *path = NULL;
+ const char *guid_str = NULL;
+ uint32_t i;
+ const char **enum_names = NULL;
+ enum winreg_Type *enum_types = NULL;
+ DATA_BLOB *enum_data_blobs = NULL;
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ path = talloc_asprintf(tmp_ctx, "%s\\%s\\CorePrinterDrivers",
+ TOP_LEVEL_PRINT_PACKAGEINSTALLATION_KEY,
+ architecture);
+ if (path == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ guid_str = GUID_string2(tmp_ctx, core_driver_guid);
+ if (guid_str == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ result = winreg_printer_openkey(tmp_ctx,
+ winreg_handle,
+ path,
+ guid_str, /* key */
+ false,
+ access_mask,
+ &hive_hnd,
+ &key_hnd);
+
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(5, ("winreg_get_core_driver: "
+ "Could not open core driver key (%s,%s): %s\n",
+ guid_str, architecture, win_errstr(result)));
+ goto done;
+ }
+
+ status = dcerpc_winreg_enumvals(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ &num_values,
+ &enum_names,
+ &enum_types,
+ &enum_data_blobs,
+ &result);
+ if (!NT_STATUS_IS_OK(status)){
+ result = ntstatus_to_werror(status);
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_get_core_driver: "
+ "Could not enumerate values for (%s,%s): %s\n",
+ guid_str, architecture, win_errstr(result)));
+ goto done;
+ }
+
+ enum_values = talloc_zero_array(tmp_ctx,
+ struct spoolss_PrinterEnumValues,
+ num_values);
+ if (enum_values == NULL){
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ for (i = 0; i < num_values; i++){
+ enum_values[i].value_name = enum_names[i];
+ enum_values[i].value_name_len = strlen_m_term(enum_names[i]) * 2;
+ enum_values[i].type = enum_types[i];
+ enum_values[i].data_length = enum_data_blobs[i].length;
+ enum_values[i].data = NULL;
+ if (enum_values[i].data_length != 0){
+ enum_values[i].data = &enum_data_blobs[i];
+ }
+ }
+
+ c = talloc_zero(tmp_ctx, struct spoolss_CorePrinterDriver);
+ if (c == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ c->core_driver_guid = *core_driver_guid;
+
+ result = WERR_OK;
+
+ for (i = 0; i < num_values; i++) {
+ const char *tmp_str;
+
+ v = &enum_values[i];
+
+ result = winreg_enumval_to_sz(c, v,
+ "InfPath",
+ &c->szPackageID);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(c, v,
+ "DriverDate",
+ &tmp_str);
+ if (W_ERROR_IS_OK(result)) {
+ result = winreg_printer_date_to_NTTIME(tmp_str,
+ &c->driver_date);
+ }
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(c, v,
+ "DriverVersion",
+ &tmp_str);
+ if (W_ERROR_IS_OK(result)) {
+ result = winreg_printer_ver_to_qword(tmp_str,
+ &c->driver_version);
+ }
+ CHECK_ERROR(result);
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_enumval_to_TYPE() failed "
+ "for %s: %s\n", v->value_name,
+ win_errstr(result)));
+ goto done;
+ }
+
+ *_core_printer_driver = talloc_steal(mem_ctx, c);
+ result = WERR_OK;
+done:
+ if (winreg_handle != NULL) {
+ WERROR ignore;
+
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_add_core_driver(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *architecture,
+ const struct spoolss_CorePrinterDriver *r)
+{
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ TALLOC_CTX *tmp_ctx = NULL;
+ NTSTATUS status;
+ WERROR result;
+ const char *guid_str;
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ guid_str = GUID_string2(tmp_ctx, &r->core_driver_guid);
+ if (guid_str == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ result = winreg_printer_open_core_driver(tmp_ctx,
+ winreg_handle,
+ architecture,
+ guid_str,
+ access_mask,
+ &hive_hnd,
+ &key_hnd);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_add_core_driver: "
+ "Could not open core driver key (%s,%s): %s\n",
+ guid_str, architecture, win_errstr(result)));
+ goto done;
+ }
+
+ result = winreg_printer_write_date(tmp_ctx, winreg_handle,
+ &key_hnd, "DriverDate",
+ r->driver_date);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ result = winreg_printer_write_ver(tmp_ctx, winreg_handle,
+ &key_hnd, "DriverVersion",
+ r->driver_version);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "InfPath",
+ r->szPackageID,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ result = WERR_OK;
+done:
+ if (winreg_handle != NULL) {
+ WERROR ignore;
+
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_add_driver_package(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *package_id,
+ const char *architecture,
+ const char *driver_store_path,
+ const char *cab_path)
+{
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ TALLOC_CTX *tmp_ctx = NULL;
+ NTSTATUS status;
+ WERROR result;
+ const char *path;
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ path = talloc_asprintf(tmp_ctx, "%s\\%s\\DriverPackages",
+ TOP_LEVEL_PRINT_PACKAGEINSTALLATION_KEY,
+ architecture);
+ if (path == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ result = winreg_printer_openkey(tmp_ctx,
+ winreg_handle,
+ path,
+ package_id, /* key */
+ true,
+ access_mask,
+ &hive_hnd,
+ &key_hnd);
+
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_add_driver_package: "
+ "Could not open driver package key (%s,%s): %s\n",
+ package_id, architecture, win_errstr(result)));
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "CabPath",
+ cab_path,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ "DriverStorePath",
+ driver_store_path,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ result = WERR_OK;
+done:
+ if (winreg_handle != NULL) {
+ WERROR ignore;
+
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_get_driver_package(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *package_id,
+ const char *architecture,
+ const char **driver_store_path,
+ const char **cab_path)
+{
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ struct spoolss_PrinterEnumValues *enum_values = NULL;
+ struct spoolss_PrinterEnumValues *v = NULL;
+ uint32_t num_values = 0;
+ TALLOC_CTX *tmp_ctx = NULL;
+ WERROR result;
+ NTSTATUS status;
+ const char *path = NULL;
+ uint32_t i;
+ const char **enum_names = NULL;
+ enum winreg_Type *enum_types = NULL;
+ DATA_BLOB *enum_data_blobs = NULL;
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ path = talloc_asprintf(tmp_ctx, "%s\\%s\\DriverPackages",
+ TOP_LEVEL_PRINT_PACKAGEINSTALLATION_KEY,
+ architecture);
+ if (path == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ result = winreg_printer_openkey(tmp_ctx,
+ winreg_handle,
+ path,
+ package_id, /* key */
+ false,
+ access_mask,
+ &hive_hnd,
+ &key_hnd);
+
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(5, ("winreg_get_driver_package: "
+ "Could not open driver package key (%s,%s): %s\n",
+ package_id, architecture, win_errstr(result)));
+ goto done;
+ }
+
+ status = dcerpc_winreg_enumvals(tmp_ctx,
+ winreg_handle,
+ &key_hnd,
+ &num_values,
+ &enum_names,
+ &enum_types,
+ &enum_data_blobs,
+ &result);
+ if (!NT_STATUS_IS_OK(status)){
+ result = ntstatus_to_werror(status);
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_get_driver_package: "
+ "Could not enumerate values for (%s,%s): %s\n",
+ package_id, architecture, win_errstr(result)));
+ goto done;
+ }
+
+ enum_values = talloc_zero_array(tmp_ctx,
+ struct spoolss_PrinterEnumValues,
+ num_values);
+ if (enum_values == NULL){
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ for (i = 0; i < num_values; i++){
+ enum_values[i].value_name = enum_names[i];
+ enum_values[i].value_name_len = strlen_m_term(enum_names[i]) * 2;
+ enum_values[i].type = enum_types[i];
+ enum_values[i].data_length = enum_data_blobs[i].length;
+ enum_values[i].data = NULL;
+ if (enum_values[i].data_length != 0){
+ enum_values[i].data = &enum_data_blobs[i];
+ }
+ }
+
+ result = WERR_OK;
+
+ for (i = 0; i < num_values; i++) {
+
+ v = &enum_values[i];
+
+ result = winreg_enumval_to_sz(mem_ctx, v,
+ "CabPath",
+ cab_path);
+ CHECK_ERROR(result);
+
+ result = winreg_enumval_to_sz(mem_ctx, v,
+ "DriverStorePath",
+ driver_store_path);
+ CHECK_ERROR(result);
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("winreg_enumval_to_TYPE() failed "
+ "for %s: %s\n", v->value_name,
+ win_errstr(result)));
+ goto done;
+ }
+
+ result = WERR_OK;
+done:
+ if (winreg_handle != NULL) {
+ WERROR ignore;
+
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_del_driver_package(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *package_id,
+ const char *architecture)
+{
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ TALLOC_CTX *tmp_ctx;
+ WERROR result;
+ NTSTATUS status;
+ const char *path;
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ path = talloc_asprintf(tmp_ctx, "%s\\%s\\DriverPackages",
+ TOP_LEVEL_PRINT_PACKAGEINSTALLATION_KEY,
+ architecture);
+ if (path == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ result = winreg_printer_openkey(tmp_ctx,
+ winreg_handle,
+ path,
+ package_id, /* key */
+ false,
+ access_mask,
+ &hive_hnd,
+ &key_hnd);
+ if (!W_ERROR_IS_OK(result)) {
+ /* key doesn't exist */
+ if (W_ERROR_EQUAL(result, WERR_FILE_NOT_FOUND)) {
+ result = WERR_OK;
+ goto done;
+ }
+
+ DEBUG(5, ("winreg_del_driver_package: "
+ "Could not open driver package key (%s,%s): %s\n",
+ package_id, architecture, win_errstr(result)));
+ goto done;
+ }
+
+
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &result);
+ }
+
+ path = talloc_asprintf(tmp_ctx, "%s\\%s\\DriverPackages\\%s",
+ TOP_LEVEL_PRINT_PACKAGEINSTALLATION_KEY,
+ architecture,
+ package_id);
+ if (path == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ status = dcerpc_winreg_delete_subkeys_recursive(tmp_ctx,
+ winreg_handle,
+ &hive_hnd,
+ access_mask,
+ path,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ DEBUG(5, ("winreg_del_driver_package: "
+ "Could not delete driver package key (%s,%s): %s\n",
+ package_id, architecture, nt_errstr(status)));
+ goto done;
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(5, ("winreg_del_driver_package: "
+ "Could not delete driver package key (%s,%s): %s\n",
+ package_id, architecture, win_errstr(result)));
+ goto done;
+ }
+
+ result = WERR_OK;
+done:
+ if (winreg_handle != NULL) {
+ WERROR ignore;
+
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(winreg_handle, tmp_ctx, &hive_hnd, &ignore);
+ }
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
diff --git a/source3/rpc_client/cli_winreg_spoolss.h b/source3/rpc_client/cli_winreg_spoolss.h
new file mode 100644
index 0000000..c80ca72
--- /dev/null
+++ b/source3/rpc_client/cli_winreg_spoolss.h
@@ -0,0 +1,720 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * SPOOLSS RPC Pipe server / winreg client routines
+ *
+ * Copyright (c) 2010 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _RPC_CLIENT_CLI_WINREG_SPOOLSS_H_
+#define _RPC_CLIENT_CLI_WINREG_SPOOLSS_H_
+
+#include "replace.h"
+#include "librpc/gen_ndr/spoolss.h"
+
+struct dcerpc_binding_handle;
+
+enum spoolss_PrinterInfo2Mask {
+ SPOOLSS_PRINTER_INFO_ATTRIBUTES = (int)(0x00000001),
+ SPOOLSS_PRINTER_INFO_AVERAGEPPM = (int)(0x00000002),
+ SPOOLSS_PRINTER_INFO_CJOBS = (int)(0x00000004),
+ SPOOLSS_PRINTER_INFO_COMMENT = (int)(0x00000008),
+ SPOOLSS_PRINTER_INFO_DATATYPE = (int)(0x00000010),
+ SPOOLSS_PRINTER_INFO_DEFAULTPRIORITY = (int)(0x00000020),
+ SPOOLSS_PRINTER_INFO_DEVMODE = (int)(0x00000040),
+ SPOOLSS_PRINTER_INFO_DRIVERNAME = (int)(0x00000080),
+ SPOOLSS_PRINTER_INFO_LOCATION = (int)(0x00000100),
+ SPOOLSS_PRINTER_INFO_NAME = (int)(0x00000200),
+ SPOOLSS_PRINTER_INFO_PARAMETERS = (int)(0x00000400),
+ SPOOLSS_PRINTER_INFO_PORTNAME = (int)(0x00000800),
+ SPOOLSS_PRINTER_INFO_PRINTERNAME = (int)(0x00001000),
+ SPOOLSS_PRINTER_INFO_PRINTPROCESSOR = (int)(0x00002000),
+ SPOOLSS_PRINTER_INFO_PRIORITY = (int)(0x00004000),
+ SPOOLSS_PRINTER_INFO_SECDESC = (int)(0x00008000),
+ SPOOLSS_PRINTER_INFO_SEPFILE = (int)(0x00010000),
+ SPOOLSS_PRINTER_INFO_SERVERNAME = (int)(0x00020000),
+ SPOOLSS_PRINTER_INFO_SHARENAME = (int)(0x00040000),
+ SPOOLSS_PRINTER_INFO_STARTTIME = (int)(0x00080000),
+ SPOOLSS_PRINTER_INFO_STATUS = (int)(0x00100000),
+ SPOOLSS_PRINTER_INFO_UNTILTIME = (int)(0x00200000)
+};
+
+#define SPOOLSS_PRINTER_INFO_ALL SPOOLSS_PRINTER_INFO_ATTRIBUTES | \
+ SPOOLSS_PRINTER_INFO_AVERAGEPPM | \
+ SPOOLSS_PRINTER_INFO_CJOBS | \
+ SPOOLSS_PRINTER_INFO_COMMENT | \
+ SPOOLSS_PRINTER_INFO_DATATYPE | \
+ SPOOLSS_PRINTER_INFO_DEFAULTPRIORITY | \
+ SPOOLSS_PRINTER_INFO_DEVMODE | \
+ SPOOLSS_PRINTER_INFO_DRIVERNAME | \
+ SPOOLSS_PRINTER_INFO_LOCATION | \
+ SPOOLSS_PRINTER_INFO_NAME | \
+ SPOOLSS_PRINTER_INFO_PARAMETERS | \
+ SPOOLSS_PRINTER_INFO_PORTNAME | \
+ SPOOLSS_PRINTER_INFO_PRINTERNAME | \
+ SPOOLSS_PRINTER_INFO_PRINTPROCESSOR | \
+ SPOOLSS_PRINTER_INFO_PRIORITY | \
+ SPOOLSS_PRINTER_INFO_SECDESC | \
+ SPOOLSS_PRINTER_INFO_SEPFILE | \
+ SPOOLSS_PRINTER_INFO_SERVERNAME | \
+ SPOOLSS_PRINTER_INFO_SHARENAME | \
+ SPOOLSS_PRINTER_INFO_STARTTIME | \
+ SPOOLSS_PRINTER_INFO_STATUS | \
+ SPOOLSS_PRINTER_INFO_UNTILTIME
+
+WERROR winreg_create_printer(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ const char *sharename);
+
+/**
+ * @internal
+ *
+ * @brief Update the information of a printer in the registry.
+ *
+ * @param[in] mem_ctx The talloc memory context to use.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[in] sharename The share name.
+ *
+ * @param[in] info2_mask A bitmask which defines which values should be set.
+ *
+ * @param[in] info2 A SetPrinterInfo2 structure with the data to set.
+ *
+ * @param[in] devmode A device mode structure with the data to set.
+ *
+ * @param[in] secdesc A security descriptor structure with the data to set.
+ *
+ * @return On success WERR_OK, a corresponding DOS error is
+ * something went wrong.
+ */
+WERROR winreg_update_printer(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ const char *sharename,
+ uint32_t info2_mask,
+ struct spoolss_SetPrinterInfo2 *info2,
+ struct spoolss_DeviceMode *devmode,
+ struct security_descriptor *secdesc);
+
+
+/**
+ * @brief Get the information of a printer stored in the registry.
+ *
+ * @param[in] mem_ctx The talloc memory context to use.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[in] printer The name of the printer to get.
+ *
+ * @param[out] pinfo2 A pointer to store a PRINTER_INFO_2 structure.
+ *
+ * @return On success WERR_OK, a corresponding DOS error is
+ * something went wrong.
+ */
+WERROR winreg_get_printer(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ const char *printer,
+ struct spoolss_PrinterInfo2 **pinfo2);
+
+/**
+ * @brief Get the security descriptor for a printer.
+ *
+ * @param[in] mem_ctx The talloc memory context to use.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[in] sharename The share name.
+ *
+ * @param[out] psecdesc A pointer to store the security descriptor.
+ *
+ * @return On success WERR_OK, a corresponding DOS error is
+ * something went wrong.
+ */
+WERROR winreg_get_printer_secdesc(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ const char *sharename,
+ struct spoolss_security_descriptor **psecdesc);
+
+/**
+ * @brief Get the security descriptor for a printserver.
+ *
+ * @param[in] mem_ctx The talloc memory context to use.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[out] psecdesc A pointer to store the security descriptor.
+ *
+ * @return On success WERR_OK, a corresponding DOS error is
+ * something went wrong.
+ */
+
+WERROR winreg_get_printserver_secdesc(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ struct spoolss_security_descriptor **psecdesc);
+
+/**
+ * @brief Set the security descriptor for a printer.
+ *
+ * @param[in] mem_ctx The talloc memory context to use.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[in] sharename The share name.
+ *
+ * @param[in] secdesc The security descriptor to save.
+ *
+ * @return On success WERR_OK, a corresponding DOS error is
+ * something went wrong.
+ */
+WERROR winreg_set_printer_secdesc(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ const char *sharename,
+ const struct spoolss_security_descriptor *secdesc);
+
+/**
+ * @brief Set the security descriptor for a printserver.
+ *
+ * @param[in] mem_ctx The talloc memory context to use.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[in] secdesc The security descriptor to save.
+ *
+ * @return On success WERR_OK, a corresponding DOS error is
+ * something went wrong.
+ */
+WERROR winreg_set_printserver_secdesc(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ const struct spoolss_security_descriptor *secdesc);
+
+
+/**
+ * @internal
+ *
+ * @brief Set printer data over the winreg pipe.
+ *
+ * @param[in] mem_ctx The talloc memory context to use.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[in] printer The printer name.
+ *
+ * @param[in] key The key of the printer data to store the value.
+ *
+ * @param[in] value The value name to save.
+ *
+ * @param[in] type The type of the value to use.
+ *
+ o @param[in] data The data which should be saved under the given value.
+ *
+ * @param[in] data_size The size of the data.
+ *
+ * @return On success WERR_OK, a corresponding DOS error is
+ * something went wrong.
+ */
+WERROR winreg_set_printer_dataex(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ const char *printer,
+ const char *key,
+ const char *value,
+ enum winreg_Type type,
+ uint8_t *data,
+ uint32_t data_size);
+
+/**
+ * @internal
+ *
+ * @brief Get printer data over a winreg pipe.
+ *
+ * @param[in] mem_ctx The talloc memory context to use.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[in] printer The printer name.
+ *
+ * @param[in] key The key of the printer data to get the value.
+ *
+ * @param[in] value The name of the value to query.
+ *
+ * @param[in] type The type of the value to query.
+ *
+ * @param[out] data A pointer to store the data.
+ *
+ * @param[out] data_size A pointer to store the size of the data.
+ *
+ * @return On success WERR_OK, a corresponding DOS error is
+ * something went wrong.
+ */
+WERROR winreg_get_printer_dataex(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ const char *printer,
+ const char *key,
+ const char *value,
+ enum winreg_Type *type,
+ uint8_t **data,
+ uint32_t *data_size);
+
+/**
+ * @internal
+ *
+ * @brief Enumerate on the values of a given key and provide the data.
+ *
+ * @param[in] mem_ctx The talloc memory context to use.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[in] printer The printer name.
+ *
+ * @param[in] key The key of the printer data to get the value.
+ *
+ * @param[out] pnum_values A pointer to store the number of values we found.
+ *
+ * @param[out] penum_values A pointer to store the values and its data.
+ *
+ * @return WERR_OK on success, the corresponding DOS error
+ * code if something gone wrong.
+ */
+WERROR winreg_enum_printer_dataex(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ const char *printer,
+ const char *key,
+ uint32_t *pnum_values,
+ struct spoolss_PrinterEnumValues **penum_values);
+
+/**
+ * @internal
+ *
+ * @brief Delete printer data over a winreg pipe.
+ *
+ * @param[in] mem_ctx The talloc memory context to use.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[in] printer The printer name.
+ *
+ * @param[in] key The key of the printer data to delete.
+ *
+ * @param[in] value The name of the value to delete.
+ *
+ * @return On success WERR_OK, a corresponding DOS error is
+ * something went wrong.
+ */
+WERROR winreg_delete_printer_dataex(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ const char *printer,
+ const char *key,
+ const char *value);
+
+/**
+ * @internal
+ *
+ * @brief Enumerate on the subkeys of a given key and provide the data.
+ *
+ * @param[in] mem_ctx The talloc memory context to use.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[in] printer The printer name.
+ *
+ * @param[in] key The key of the printer data to get the value.
+ *
+ * @param[out] pnum_subkeys A pointer to store the number of subkeys found.
+ *
+ * @param[in] psubkeys A pointer to an array to store the names of the subkeys
+ * found.
+ *
+ * @return WERR_OK on success, the corresponding DOS error
+ * code if something gone wrong.
+ */
+WERROR winreg_enum_printer_key(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ const char *printer,
+ const char *key,
+ uint32_t *pnum_subkeys,
+ const char ***psubkeys);
+
+/**
+ * @internal
+ *
+ * @brief Delete a key with subkeys of a given printer.
+ *
+ * @param[in] mem_ctx The talloc memory context to use.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[in] printer The printer name.
+ *
+ * @param[in] key The key of the printer to delete.
+ *
+ * @return On success WERR_OK, a corresponding DOS error is
+ * something went wrong.
+ */
+WERROR winreg_delete_printer_key(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ const char *printer,
+ const char *key);
+
+/**
+ * @brief Update the ChangeID of a printer.
+ *
+ * The ChangeID **must** be increasing over the lifetime of client's spoolss
+ * service in order for the client's cache to show updates.
+ *
+ * If a form is updated of a printer, the we need to update the ChangeID of the
+ * pritner.
+ *
+ * @param[in] mem_ctx The talloc memory context to use.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[in] printer The printer name.
+ *
+ * @return On success WERR_OK, a corresponding DOS error is
+ * something went wrong.
+ */
+WERROR winreg_printer_update_changeid(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ const char *printer);
+
+/**
+ * @brief Get the ChangeID of the given printer.
+ *
+ * @param[in] mem_ctx The talloc memory context to use.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[in] printer The printer name.
+ *
+ * @param[in] changeid A pointer to store the changeid.
+ *
+ * @return On success WERR_OK, a corresponding DOS error is
+ * something went wrong.
+ */
+WERROR winreg_printer_get_changeid(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ const char *printer,
+ uint32_t *pchangeid);
+
+/**
+ * @internal
+ *
+ * @brief This function adds a form to the list of available forms that can be
+ * selected for the specified printer.
+ *
+ * @param[in] mem_ctx The talloc memory context to use.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[in] form The form to add.
+ *
+ * @return WERR_OK on success.
+ * WERR_ALREADY_EXISTS if the form already exists or is a
+ * builtin form.
+ * A corresponding DOS error is something went wrong.
+ */
+WERROR winreg_printer_addform1(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ struct spoolss_AddFormInfo1 *form);
+
+/*
+ * @brief This function enumerates the forms supported by the specified printer.
+ *
+ * @param[in] mem_ctx The talloc memory context to use.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[out] pnum_info A pointer to store the FormInfo count.
+ *
+ * @param[out] pinfo A pointer to store an array with FormInfo.
+ *
+ * @return On success WERR_OK, a corresponding DOS error is
+ * something went wrong.
+ */
+WERROR winreg_printer_enumforms1(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ uint32_t *pnum_info,
+ union spoolss_FormInfo **pinfo);
+
+/**
+ * @brief This function removes a form name from the list of supported forms.
+ *
+ * @param[in] mem_ctx The talloc memory context to use.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[in] form_name The name of the form to delete.
+ *
+ * @return WERR_OK on success.
+ * WERR_INVALID_PARAMETER if the form is a builtin form.
+ * WERR_INVALID_FORM_NAME if the form or key doesn't exist.
+ * A corresponding DOS error is something went wrong.
+ */
+WERROR winreg_printer_deleteform1(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ const char *form_name);
+
+/**
+ * @brief This function sets the form information for the specified printer.
+ *
+ * If one provides both the name in the API call and inside the FormInfo
+ * structure, then the form gets renamed.
+ *
+ * @param[in] mem_ctx The talloc memory context to use.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[in] form_name The name of the form to set or rename.
+ *
+ * @param[in] form The FormInfo structure to save.
+ *
+ * @return WERR_OK on success.
+ * WERR_INVALID_PARAMETER if the form is a builtin form.
+ * A corresponding DOS error is something went wrong.
+ */
+WERROR winreg_printer_setform1(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ const char *form_name,
+ struct spoolss_AddFormInfo1 *form);
+
+/**
+ * @brief This function retrieves information about a specified form.
+ *
+ * @param[in] mem_ctx The talloc memory context to use.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[in] form_name The name of the form to query.
+ *
+ * @param[out] form A pointer to a form structure to fill out.
+ *
+ * @return On success WERR_OK, a corresponding DOS error is
+ * something went wrong.
+ */
+WERROR winreg_printer_getform1(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ const char *form_name,
+ struct spoolss_FormInfo1 *form);
+
+/**
+ * @brief This function adds a new spool driver
+ *
+ * @param[in] mem_ctx A talloc memory context.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[in] r The structure containing the new driver data.
+ *
+ * @param[out] driver_name Returns the driver name.
+ *
+ * @param[out] driver_version Returns the driver version.
+ *
+ * @return On success WERR_OK, a corresponding DOS error is
+ * something went wrong.
+ */
+WERROR winreg_add_driver(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ struct spoolss_AddDriverInfoCtr *r,
+ const char **driver_name,
+ uint32_t *driver_version);
+
+/**
+ * @brief This function gets printer driver information
+ *
+ * @param[in] mem_ctx A talloc memory context.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[in] architecture The architecture type.
+ *
+ * @param[in] driver_name The driver name.
+ *
+ * @param[in] driver_version The driver version.
+ *
+ * @param[out] _info8 The structure that holds the full driver information.
+ *
+ * @return On success WERR_OK, a corresponding DOS error is
+ * something went wrong.
+ */
+
+WERROR winreg_get_driver(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ const char *architecture,
+ const char *driver_name,
+ uint32_t driver_version,
+ struct spoolss_DriverInfo8 **_info8);
+
+/**
+ * @brief This function deletes a printer driver information
+ *
+ * @param[in] mem_ctx A talloc memory context.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[out] info8 The structure that holds the full driver information.
+ *
+ * @param[in] version The driver type version.
+ *
+ * @return On success WERR_OK, a corresponding DOS error is
+ * something went wrong.
+ */
+
+WERROR winreg_del_driver(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ struct spoolss_DriverInfo8 *info8,
+ uint32_t version);
+
+/**
+ * @brief This function gets printer drivers list for the specified
+ * architecture and type version
+ *
+ * @param[in] mem_ctx A talloc memory context.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[in] architecture The architecture type.
+ *
+ * @param[in] version The driver version.
+ *
+ * @param[out] num_drivers The number of drivers.
+ *
+ * @param[out] version The drivers names.
+ *
+ * @return On success WERR_OK, a corresponding DOS error is
+ * something went wrong.
+ */
+
+WERROR winreg_get_driver_list(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ const char *architecture,
+ uint32_t version,
+ uint32_t *num_drivers,
+ const char ***drivers);
+/**
+ * @brief This function gets a core printer driver
+ *
+ * @param[in] mem_ctx A talloc memory context.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[in] architecture The architecture type.
+ *
+ * @param[in] core_driver_guid The core driver guid.
+ *
+ * @param[out] core_printer_driver The returned core printer driver definition
+ *
+ * @return On success WERR_OK, a corresponding DOS error is
+ * something went wrong.
+ */
+
+WERROR winreg_get_core_driver(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *architecture,
+ const struct GUID *core_driver_guid,
+ struct spoolss_CorePrinterDriver **_core_printer_driver);
+
+/**
+ * @brief This function adds a core printer driver
+ *
+ * @param[in] mem_ctx A talloc memory context.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[in] architecture The architecture type.
+ *
+ * @param[in] core_driver_driver The core driver.
+ *
+ * @return On success WERR_OK, a corresponding DOS error is
+ * something went wrong.
+ */
+
+WERROR winreg_add_core_driver(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *architecture,
+ const struct spoolss_CorePrinterDriver *r);
+
+/**
+ * @brief This function adds a driver package
+ *
+ * @param[in] mem_ctx A talloc memory context.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[in] package_id The package ID.
+ *
+ * @param[in] architecture The architecture type.
+ *
+ * @param[in] driver_store_path The local DriverStorePath
+ *
+ * @param[in] cab_path The local CabFile path
+ *
+ * @return On success WERR_OK, a corresponding DOS error is
+ * something went wrong.
+ */
+
+WERROR winreg_add_driver_package(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *package_id,
+ const char *architecture,
+ const char *driver_store_path,
+ const char *cab_path);
+
+/**
+ * @brief This function gets a driver package
+ *
+ * @param[in] mem_ctx A talloc memory context.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[in] package_id The package ID.
+ *
+ * @param[in] architecture The architecture type.
+ *
+ * @param[in] driver_store_path The pointer to a local DriverStorePath
+ *
+ * @param[in] cab_path The pointer to a local CabFile path
+ *
+ * @return On success WERR_OK, a corresponding DOS error is
+ * something went wrong.
+ */
+
+WERROR winreg_get_driver_package(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *package_id,
+ const char *architecture,
+ const char **driver_store_path,
+ const char **cab_path);
+
+/**
+ * @brief This function deletes a driver package
+ *
+ * @param[in] mem_ctx A talloc memory context.
+ *
+ * @param[in] b The dcerpc binding handle
+ *
+ * @param[in] package_id The package ID.
+ *
+ * @param[in] architecture The architecture type.
+ *
+ * @return On success WERR_OK, a corresponding DOS error is
+ * something went wrong.
+ */
+
+WERROR winreg_del_driver_package(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *winreg_handle,
+ const char *package_id,
+ const char *architecture);
+
+#endif /* _RPC_CLIENT_CLI_WINREG_SPOOLSS_H_ */
diff --git a/source3/rpc_client/init_lsa.c b/source3/rpc_client/init_lsa.c
new file mode 100644
index 0000000..2681197
--- /dev/null
+++ b/source3/rpc_client/init_lsa.c
@@ -0,0 +1,60 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Guenther Deschner 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 "rpc_client/init_lsa.h"
+#include "../librpc/gen_ndr/lsa.h"
+
+/*******************************************************************
+ inits a structure.
+********************************************************************/
+
+void init_lsa_String(struct lsa_String *name, const char *s)
+{
+ name->string = s;
+ name->size = 2 * strlen_m(s);
+ name->length = name->size;
+}
+
+/*******************************************************************
+ inits a structure.
+********************************************************************/
+
+void init_lsa_StringLarge(struct lsa_StringLarge *name, const char *s)
+{
+ name->string = s;
+}
+
+/*******************************************************************
+ inits a structure.
+********************************************************************/
+
+void init_lsa_AsciiString(struct lsa_AsciiString *name, const char *s)
+{
+ name->string = s;
+}
+
+/*******************************************************************
+ inits a structure.
+********************************************************************/
+
+void init_lsa_AsciiStringLarge(struct lsa_AsciiStringLarge *name, const char *s)
+{
+ name->string = s;
+}
diff --git a/source3/rpc_client/init_lsa.h b/source3/rpc_client/init_lsa.h
new file mode 100644
index 0000000..e4e5c11
--- /dev/null
+++ b/source3/rpc_client/init_lsa.h
@@ -0,0 +1,35 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Guenther Deschner 2008.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _RPC_CLIENT_INIT_LSA_H_
+#define _RPC_CLIENT_INIT_LSA_H_
+
+struct lsa_String;
+struct lsa_StringLarge;
+struct lsa_AsciiString;
+struct lsa_AsciiStringLarge;
+
+/* The following definitions come from rpc_client/init_lsa.c */
+
+void init_lsa_String(struct lsa_String *name, const char *s);
+void init_lsa_StringLarge(struct lsa_StringLarge *name, const char *s);
+void init_lsa_AsciiString(struct lsa_AsciiString *name, const char *s);
+void init_lsa_AsciiStringLarge(struct lsa_AsciiStringLarge *name, const char *s);
+
+#endif /* _RPC_CLIENT_INIT_LSA_H_ */
diff --git a/source3/rpc_client/init_samr.c b/source3/rpc_client/init_samr.c
new file mode 100644
index 0000000..52fa2f9
--- /dev/null
+++ b/source3/rpc_client/init_samr.c
@@ -0,0 +1,126 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Guenther Deschner 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 "../libcli/auth/libcli_auth.h"
+#include "rpc_client/init_samr.h"
+#include "librpc/rpc/dcerpc_samr.h"
+
+#include "lib/crypto/gnutls_helpers.h"
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+/*************************************************************************
+ inits a samr_CryptPasswordEx structure
+ *************************************************************************/
+
+NTSTATUS init_samr_CryptPasswordEx(const char *pwd,
+ DATA_BLOB *session_key,
+ struct samr_CryptPasswordEx *pwd_buf)
+{
+ return encode_rc4_passwd_buffer(pwd, session_key, pwd_buf);
+}
+
+/*************************************************************************
+ inits a samr_CryptPassword structure
+ *************************************************************************/
+
+NTSTATUS init_samr_CryptPassword(const char *pwd,
+ DATA_BLOB *session_key,
+ struct samr_CryptPassword *pwd_buf)
+{
+ /* samr_CryptPassword */
+ gnutls_cipher_hd_t cipher_hnd = NULL;
+ gnutls_datum_t sess_key = {
+ .data = session_key->data,
+ .size = session_key->length,
+ };
+ bool ok;
+ int rc;
+
+ ok = encode_pw_buffer(pwd_buf->data, pwd, STR_UNICODE);
+ if (!ok) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ rc = gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &sess_key,
+ NULL);
+ if (rc != 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+ rc = gnutls_cipher_encrypt(cipher_hnd,
+ pwd_buf->data,
+ 516);
+ gnutls_cipher_deinit(cipher_hnd);
+ if (rc != 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS init_samr_CryptPasswordAES(TALLOC_CTX *mem_ctx,
+ const char *password,
+ DATA_BLOB *salt,
+ DATA_BLOB *session_key,
+ struct samr_EncryptedPasswordAES *ppwd_buf)
+{
+ uint8_t pw_data[514] = {0};
+ DATA_BLOB plaintext = {
+ .data = pw_data,
+ .length = sizeof(pw_data),
+ };
+ DATA_BLOB ciphertext = data_blob_null;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ bool ok;
+
+ if (ppwd_buf == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ok = encode_pwd_buffer514_from_str(pw_data, password, STR_UNICODE);
+ if (!ok) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ status = samba_gnutls_aead_aes_256_cbc_hmac_sha512_encrypt(
+ mem_ctx,
+ &plaintext,
+ session_key,
+ &samr_aes256_enc_key_salt,
+ &samr_aes256_mac_key_salt,
+ salt,
+ &ciphertext,
+ ppwd_buf->auth_data);
+ BURN_DATA(pw_data);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ppwd_buf->cipher_len = ciphertext.length;
+ ppwd_buf->cipher = ciphertext.data;
+ ppwd_buf->PBKDF2Iterations = 0;
+
+ SMB_ASSERT(salt->length == sizeof(ppwd_buf->salt));
+ memcpy(ppwd_buf->salt, salt->data, salt->length);
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/rpc_client/init_samr.h b/source3/rpc_client/init_samr.h
new file mode 100644
index 0000000..71b4c0e
--- /dev/null
+++ b/source3/rpc_client/init_samr.h
@@ -0,0 +1,54 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Guenther Deschner 2008.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _RPC_CLIENT_INIT_SAMR_H_
+#define _RPC_CLIENT_INIT_SAMR_H_
+
+/* The following definitions come from rpc_client/init_samr.c */
+
+NTSTATUS init_samr_CryptPasswordEx(const char *pwd,
+ DATA_BLOB *session_key,
+ struct samr_CryptPasswordEx *pwd_buf);
+NTSTATUS init_samr_CryptPassword(const char *pwd,
+ DATA_BLOB *session_key,
+ struct samr_CryptPassword *pwd_buf);
+
+/**
+ * @brief Initialize a AES encrypted password structure.
+ *
+ * This takes a password and a session key and encrypts the password. The
+ * encrypted password is then stored in the encrypted passwors structure.
+ *
+ * @param mem_ctx The memory context to allocate the password buffer on.
+ *
+ * @param password The password to encrypt.
+ *
+ * @param session_key The session key used to encrypt the password.
+ *
+ * @param ppwd_buf A pointer to the talloc allocated password structure.
+ *
+ * @return On success NT_STATUS_OK, an error status code otherwise.
+ */
+NTSTATUS init_samr_CryptPasswordAES(TALLOC_CTX *mem_ctx,
+ const char *password,
+ DATA_BLOB *salt,
+ DATA_BLOB *session_key,
+ struct samr_EncryptedPasswordAES *ppwd_buf);
+
+#endif /* _RPC_CLIENT_INIT_SAMR_H_ */
diff --git a/source3/rpc_client/init_spoolss.c b/source3/rpc_client/init_spoolss.c
new file mode 100644
index 0000000..c341b82
--- /dev/null
+++ b/source3/rpc_client/init_spoolss.c
@@ -0,0 +1,479 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Guenther Deschner 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 "../librpc/gen_ndr/ndr_spoolss.h"
+#include "rpc_client/init_spoolss.h"
+#include "../libcli/security/security.h"
+#include "secrets.h"
+#include "passdb/machine_sid.h"
+
+/*******************************************************************
+********************************************************************/
+
+bool init_systemtime(struct spoolss_Time *r,
+ struct tm *unixtime)
+{
+ if (!r || !unixtime) {
+ return false;
+ }
+
+ r->year = unixtime->tm_year+1900;
+ r->month = unixtime->tm_mon+1;
+ r->day_of_week = unixtime->tm_wday;
+ r->day = unixtime->tm_mday;
+ r->hour = unixtime->tm_hour;
+ r->minute = unixtime->tm_min;
+ r->second = unixtime->tm_sec;
+ r->millisecond = 0;
+
+ return true;
+}
+
+time_t spoolss_Time_to_time_t(const struct spoolss_Time *r)
+{
+ struct tm unixtime = {
+ .tm_year = r->year - 1900,
+ .tm_mon = r->month - 1,
+ .tm_wday = r->day_of_week,
+ .tm_mday = r->day,
+ .tm_hour = r->hour,
+ .tm_min = r->minute,
+ .tm_sec = r->second,
+ };
+
+ return mktime(&unixtime);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+bool spoolss_timestr_to_NTTIME(const char *str,
+ NTTIME *data)
+{
+ struct tm tm;
+ time_t t;
+
+ if (strequal(str, "01/01/1601")) {
+ *data = 0;
+ return true;
+ }
+
+ ZERO_STRUCT(tm);
+
+ if (sscanf(str, "%d/%d/%d",
+ &tm.tm_mon, &tm.tm_mday, &tm.tm_year) != 3) {
+ return false;
+ }
+ tm.tm_mon -= 1;
+ tm.tm_year -= 1900;
+ tm.tm_isdst = -1;
+
+ t = mktime(&tm);
+ unix_to_nt_time(data, t);
+
+ return true;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+bool spoolss_driver_version_to_qword(const char *str,
+ uint64_t *data)
+{
+ unsigned int v1, v2, v3, v4 = 0;
+
+ if ((sscanf(str, "%u.%u.%u.%u", &v1, &v2, &v3, &v4) != 4) &&
+ (sscanf(str, "%u.%u.%u", &v1, &v2, &v3) != 3))
+ {
+ return false;
+ }
+
+ *data = ((uint64_t)(v1 & 0xFFFF) << 48) +
+ ((uint64_t)(v2 & 0xFFFF) << 32) +
+ ((uint64_t)(v3 & 0xFFFF) << 16) +
+ (uint64_t)(v4 & 0xFFFF);
+
+ return true;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR pull_spoolss_PrinterData(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *blob,
+ union spoolss_PrinterData *data,
+ enum winreg_Type type)
+{
+ enum ndr_err_code ndr_err;
+ ndr_err = ndr_pull_union_blob(blob, mem_ctx, data, type,
+ (ndr_pull_flags_fn_t)ndr_pull_spoolss_PrinterData);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_GEN_FAILURE;
+ }
+ return WERR_OK;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR push_spoolss_PrinterData(TALLOC_CTX *mem_ctx, DATA_BLOB *blob,
+ enum winreg_Type type,
+ union spoolss_PrinterData *data)
+{
+ enum ndr_err_code ndr_err;
+ ndr_err = ndr_push_union_blob(blob, mem_ctx, data, type,
+ (ndr_push_flags_fn_t)ndr_push_spoolss_PrinterData);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_GEN_FAILURE;
+ }
+ return WERR_OK;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+void spoolss_printerinfo2_to_setprinterinfo2(const struct spoolss_PrinterInfo2 *i,
+ struct spoolss_SetPrinterInfo2 *s)
+{
+ s->servername = i->servername;
+ s->printername = i->printername;
+ s->sharename = i->sharename;
+ s->portname = i->portname;
+ s->drivername = i->drivername;
+ s->comment = i->comment;
+ s->location = i->location;
+ s->devmode_ptr = 0;
+ s->sepfile = i->sepfile;
+ s->printprocessor = i->printprocessor;
+ s->datatype = i->datatype;
+ s->parameters = i->parameters;
+ s->secdesc_ptr = 0;
+ s->attributes = i->attributes;
+ s->priority = i->priority;
+ s->defaultpriority = i->defaultpriority;
+ s->starttime = i->starttime;
+ s->untiltime = i->untiltime;
+ s->status = i->status;
+ s->cjobs = i->cjobs;
+ s->averageppm = i->averageppm;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+bool driver_info_ctr_to_info8(struct spoolss_AddDriverInfoCtr *r,
+ struct spoolss_DriverInfo8 *_info8)
+{
+ struct spoolss_DriverInfo8 info8;
+
+ ZERO_STRUCT(info8);
+
+ switch (r->level) {
+ case 3:
+ info8.version = r->info.info3->version;
+ info8.driver_name = r->info.info3->driver_name;
+ info8.architecture = r->info.info3->architecture;
+ info8.driver_path = r->info.info3->driver_path;
+ info8.data_file = r->info.info3->data_file;
+ info8.config_file = r->info.info3->config_file;
+ info8.help_file = r->info.info3->help_file;
+ info8.monitor_name = r->info.info3->monitor_name;
+ info8.default_datatype = r->info.info3->default_datatype;
+ if (r->info.info3->dependent_files && r->info.info3->dependent_files->string) {
+ info8.dependent_files = r->info.info3->dependent_files->string;
+ }
+ break;
+ case 6:
+ info8.version = r->info.info6->version;
+ info8.driver_name = r->info.info6->driver_name;
+ info8.architecture = r->info.info6->architecture;
+ info8.driver_path = r->info.info6->driver_path;
+ info8.data_file = r->info.info6->data_file;
+ info8.config_file = r->info.info6->config_file;
+ info8.help_file = r->info.info6->help_file;
+ info8.monitor_name = r->info.info6->monitor_name;
+ info8.default_datatype = r->info.info6->default_datatype;
+ if (r->info.info6->dependent_files && r->info.info6->dependent_files->string) {
+ info8.dependent_files = r->info.info6->dependent_files->string;
+ }
+ info8.driver_date = r->info.info6->driver_date;
+ info8.driver_version = r->info.info6->driver_version;
+ info8.manufacturer_name = r->info.info6->manufacturer_name;
+ info8.manufacturer_url = r->info.info6->manufacturer_url;
+ info8.hardware_id = r->info.info6->hardware_id;
+ info8.provider = r->info.info6->provider;
+ break;
+ case 8:
+ info8.version = r->info.info8->version;
+ info8.driver_name = r->info.info8->driver_name;
+ info8.architecture = r->info.info8->architecture;
+ info8.driver_path = r->info.info8->driver_path;
+ info8.data_file = r->info.info8->data_file;
+ info8.config_file = r->info.info8->config_file;
+ info8.help_file = r->info.info8->help_file;
+ info8.monitor_name = r->info.info8->monitor_name;
+ info8.default_datatype = r->info.info8->default_datatype;
+ if (r->info.info8->dependent_files && r->info.info8->dependent_files->string) {
+ info8.dependent_files = r->info.info8->dependent_files->string;
+ }
+ if (r->info.info8->previous_names && r->info.info8->previous_names->string) {
+ info8.previous_names = r->info.info8->previous_names->string;
+ }
+ info8.driver_date = r->info.info8->driver_date;
+ info8.driver_version = r->info.info8->driver_version;
+ info8.manufacturer_name = r->info.info8->manufacturer_name;
+ info8.manufacturer_url = r->info.info8->manufacturer_url;
+ info8.hardware_id = r->info.info8->hardware_id;
+ info8.provider = r->info.info8->provider;
+ info8.print_processor = r->info.info8->print_processor;
+ info8.vendor_setup = r->info.info8->vendor_setup;
+ if (r->info.info8->color_profiles && r->info.info8->color_profiles->string) {
+ info8.color_profiles = r->info.info8->color_profiles->string;
+ }
+ info8.inf_path = r->info.info8->inf_path;
+ info8.printer_driver_attributes = r->info.info8->printer_driver_attributes;
+ if (r->info.info8->core_driver_dependencies && r->info.info8->core_driver_dependencies->string) {
+ info8.core_driver_dependencies = r->info.info8->core_driver_dependencies->string;
+ }
+ info8.min_inbox_driver_ver_date = r->info.info8->min_inbox_driver_ver_date;
+ info8.min_inbox_driver_ver_version = r->info.info8->min_inbox_driver_ver_version;
+ break;
+ default:
+ return false;
+ }
+
+ *_info8 = info8;
+
+ return true;
+}
+
+/****************************************************************************
+ Create and allocate a default devicemode.
+****************************************************************************/
+
+WERROR spoolss_create_default_devmode(TALLOC_CTX *mem_ctx,
+ const char *devicename,
+ struct spoolss_DeviceMode **devmode)
+{
+ struct spoolss_DeviceMode *dm;
+ char *dname;
+
+ dm = talloc_zero(mem_ctx, struct spoolss_DeviceMode);
+ if (dm == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ dname = talloc_asprintf(dm, "%s", devicename);
+ if (dname == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ if (strlen(dname) > MAXDEVICENAME) {
+ dname[MAXDEVICENAME] = '\0';
+ }
+ dm->devicename = dname;
+
+ dm->formname = talloc_strdup(dm, "Letter");
+ if (dm->formname == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ dm->specversion = DMSPEC_NT4_AND_ABOVE;
+ dm->driverversion = 0x0400;
+ dm->size = 0x00DC;
+ dm->__driverextra_length = 0;
+ dm->fields = DEVMODE_FORMNAME |
+ DEVMODE_TTOPTION |
+ DEVMODE_PRINTQUALITY |
+ DEVMODE_DEFAULTSOURCE |
+ DEVMODE_COPIES |
+ DEVMODE_SCALE |
+ DEVMODE_PAPERSIZE |
+ DEVMODE_ORIENTATION;
+ dm->orientation = DMORIENT_PORTRAIT;
+ dm->papersize = DMPAPER_LETTER;
+ dm->paperlength = 0;
+ dm->paperwidth = 0;
+ dm->scale = 0x64;
+ dm->copies = 1;
+ dm->defaultsource = DMBIN_FORMSOURCE;
+ dm->printquality = DMRES_HIGH; /* 0x0258 */
+ dm->color = DMRES_MONOCHROME;
+ dm->duplex = DMDUP_SIMPLEX;
+ dm->yresolution = 0;
+ dm->ttoption = DMTT_SUBDEV;
+ dm->collate = DMCOLLATE_FALSE;
+ dm->icmmethod = 0;
+ dm->icmintent = 0;
+ dm->mediatype = 0;
+ dm->dithertype = 0;
+
+ dm->logpixels = 0;
+ dm->bitsperpel = 0;
+ dm->pelswidth = 0;
+ dm->pelsheight = 0;
+ dm->displayflags = 0;
+ dm->displayfrequency = 0;
+ dm->reserved1 = 0;
+ dm->reserved2 = 0;
+ dm->panningwidth = 0;
+ dm->panningheight = 0;
+
+ dm->driverextra_data.data = NULL;
+ dm->driverextra_data.length = 0;
+
+ *devmode = dm;
+ return WERR_OK;
+}
+
+WERROR spoolss_create_default_secdesc(TALLOC_CTX *mem_ctx,
+ struct spoolss_security_descriptor **secdesc)
+{
+ struct security_ace ace[7]; /* max number of ace entries */
+ int i = 0;
+ uint32_t sa;
+ struct security_acl *psa = NULL;
+ struct security_descriptor *psd = NULL;
+ struct dom_sid adm_sid;
+ size_t sd_size;
+
+ /* Create an ACE where Everyone is allowed to print */
+
+ sa = PRINTER_ACE_PRINT;
+ init_sec_ace(&ace[i++], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED,
+ sa, SEC_ACE_FLAG_CONTAINER_INHERIT);
+
+ /* Add the domain admins group if we are a DC */
+
+ if ( IS_DC ) {
+ struct dom_sid domadmins_sid;
+
+ sid_compose(&domadmins_sid, get_global_sam_sid(),
+ DOMAIN_RID_ADMINS);
+
+ sa = PRINTER_ACE_FULL_CONTROL;
+ init_sec_ace(&ace[i++], &domadmins_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED, sa,
+ SEC_ACE_FLAG_OBJECT_INHERIT | SEC_ACE_FLAG_INHERIT_ONLY);
+ init_sec_ace(&ace[i++], &domadmins_sid, SEC_ACE_TYPE_ACCESS_ALLOWED,
+ sa, SEC_ACE_FLAG_CONTAINER_INHERIT);
+ }
+ else if (secrets_fetch_domain_sid(lp_workgroup(), &adm_sid)) {
+ sid_append_rid(&adm_sid, DOMAIN_RID_ADMINISTRATOR);
+
+ sa = PRINTER_ACE_FULL_CONTROL;
+ init_sec_ace(&ace[i++], &adm_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED, sa,
+ SEC_ACE_FLAG_OBJECT_INHERIT | SEC_ACE_FLAG_INHERIT_ONLY);
+ init_sec_ace(&ace[i++], &adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED,
+ sa, SEC_ACE_FLAG_CONTAINER_INHERIT);
+ }
+
+ /* add BUILTIN\Administrators as FULL CONTROL */
+
+ sa = PRINTER_ACE_FULL_CONTROL;
+ init_sec_ace(&ace[i++], &global_sid_Builtin_Administrators,
+ SEC_ACE_TYPE_ACCESS_ALLOWED, sa,
+ SEC_ACE_FLAG_OBJECT_INHERIT | SEC_ACE_FLAG_INHERIT_ONLY);
+ init_sec_ace(&ace[i++], &global_sid_Builtin_Administrators,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ sa, SEC_ACE_FLAG_CONTAINER_INHERIT);
+
+ /* add BUILTIN\Print Operators as FULL CONTROL */
+
+ sa = PRINTER_ACE_FULL_CONTROL;
+ init_sec_ace(&ace[i++], &global_sid_Builtin_Print_Operators,
+ SEC_ACE_TYPE_ACCESS_ALLOWED, sa,
+ SEC_ACE_FLAG_OBJECT_INHERIT | SEC_ACE_FLAG_INHERIT_ONLY);
+ init_sec_ace(&ace[i++], &global_sid_Builtin_Print_Operators,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ sa, SEC_ACE_FLAG_CONTAINER_INHERIT);
+
+ /* Make the security descriptor owned by the BUILTIN\Administrators */
+
+ /* The ACL revision number in rpc_secdesc.h differs from the one
+ created by NT when setting ACE entries in printer
+ descriptors. NT4 complains about the property being edited by a
+ NT5 machine. */
+
+ if ((psa = make_sec_acl(mem_ctx, NT4_ACL_REVISION, i, ace)) != NULL) {
+ psd = make_sec_desc(mem_ctx,
+ SD_REVISION,
+ SEC_DESC_SELF_RELATIVE,
+ &global_sid_Builtin_Administrators,
+ &global_sid_Builtin_Administrators,
+ NULL,
+ psa,
+ &sd_size);
+ }
+
+ if (psd == NULL) {
+ DEBUG(0,("construct_default_printer_sd: Failed to make SEC_DESC.\n"));
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ DEBUG(4,("construct_default_printer_sdb: size = %u.\n",
+ (unsigned int)sd_size));
+
+ *secdesc = psd;
+
+ return WERR_OK;
+}
+
+const char *spoolss_get_short_filesys_environment(const char *environment)
+{
+ if (strequal(environment, SPOOLSS_ARCHITECTURE_x64)) {
+ return "amd64";
+ } else if (strequal(environment, SPOOLSS_ARCHITECTURE_NT_X86)) {
+ return "x86";
+ } else {
+ return NULL;
+ }
+}
+
+/* Windows 7 and Windows Server 2008 R2 */
+#define GLOBAL_SPOOLSS_CLIENT_OS_MAJOR_DEFAULT 6
+#define GLOBAL_SPOOLSS_CLIENT_OS_MINOR_DEFAULT 1
+#define GLOBAL_SPOOLSS_CLIENT_OS_BUILD_DEFAULT 7007
+
+WERROR spoolss_init_spoolss_UserLevel1(TALLOC_CTX *mem_ctx,
+ const char *username,
+ struct spoolss_UserLevel1 *r)
+{
+ ZERO_STRUCTP(r);
+
+ r->size = 28;
+ r->client = talloc_asprintf(mem_ctx, "\\\\%s", lp_netbios_name());
+ W_ERROR_HAVE_NO_MEMORY(r->client);
+ r->user = talloc_strdup(mem_ctx, username);
+ W_ERROR_HAVE_NO_MEMORY(r->user);
+ r->processor = 0;
+
+ r->major = lp_parm_int(GLOBAL_SECTION_SNUM,
+ "spoolss_client", "os_major",
+ GLOBAL_SPOOLSS_CLIENT_OS_MAJOR_DEFAULT);
+ r->minor = lp_parm_int(GLOBAL_SECTION_SNUM,
+ "spoolss_client", "os_minor",
+ GLOBAL_SPOOLSS_CLIENT_OS_MINOR_DEFAULT);
+ r->build = lp_parm_int(GLOBAL_SECTION_SNUM,
+ "spoolss_client", "os_build",
+ GLOBAL_SPOOLSS_CLIENT_OS_BUILD_DEFAULT);
+
+ return WERR_OK;
+}
diff --git a/source3/rpc_client/init_spoolss.h b/source3/rpc_client/init_spoolss.h
new file mode 100644
index 0000000..062e37b
--- /dev/null
+++ b/source3/rpc_client/init_spoolss.h
@@ -0,0 +1,55 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Guenther Deschner 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/>.
+ */
+
+#ifndef _RPC_CLIENT_INIT_SPOOLSS_H_
+#define _RPC_CLIENT_INIT_SPOOLSS_H_
+
+/* The following definitions come from rpc_client/init_spoolss.c */
+
+bool init_systemtime(struct spoolss_Time *r,
+ struct tm *unixtime);
+time_t spoolss_Time_to_time_t(const struct spoolss_Time *r);
+bool spoolss_timestr_to_NTTIME(const char *str,
+ NTTIME *data);
+bool spoolss_driver_version_to_qword(const char *str,
+ uint64_t *data);
+WERROR pull_spoolss_PrinterData(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *blob,
+ union spoolss_PrinterData *data,
+ enum winreg_Type type);
+WERROR push_spoolss_PrinterData(TALLOC_CTX *mem_ctx, DATA_BLOB *blob,
+ enum winreg_Type type,
+ union spoolss_PrinterData *data);
+void spoolss_printerinfo2_to_setprinterinfo2(const struct spoolss_PrinterInfo2 *i,
+ struct spoolss_SetPrinterInfo2 *s);
+bool driver_info_ctr_to_info8(struct spoolss_AddDriverInfoCtr *r,
+ struct spoolss_DriverInfo8 *_info8);
+
+WERROR spoolss_create_default_devmode(TALLOC_CTX *mem_ctx,
+ const char *devicename,
+ struct spoolss_DeviceMode **devmode);
+
+WERROR spoolss_create_default_secdesc(TALLOC_CTX *mem_ctx,
+ struct spoolss_security_descriptor **secdesc);
+const char *spoolss_get_short_filesys_environment(const char *environment);
+WERROR spoolss_init_spoolss_UserLevel1(TALLOC_CTX *mem_ctx,
+ const char *username,
+ struct spoolss_UserLevel1 *r);
+
+#endif /* _RPC_CLIENT_INIT_SPOOLSS_H_ */
diff --git a/source3/rpc_client/local_np.c b/source3/rpc_client/local_np.c
new file mode 100644
index 0000000..10c6434
--- /dev/null
+++ b/source3/rpc_client/local_np.c
@@ -0,0 +1,852 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * 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 "source3/include/includes.h"
+#include <spawn.h>
+#include "local_np.h"
+#include "lib/async_req/async_sock.h"
+#include "librpc/gen_ndr/ndr_named_pipe_auth.h"
+#include "libcli/named_pipe_auth/npa_tstream.h"
+#include "libcli/named_pipe_auth/tstream_u32_read.h"
+#include "lib/util/tevent_unix.h"
+#include "auth/auth_util.h"
+#include "libcli/security/dom_sid.h"
+#include "libcli/security/security_token.h"
+#include "nsswitch/winbind_client.h"
+
+/**
+ * @file local_np.c
+ *
+ * Connect to a local named pipe by connecting to
+ * samba-dcerpcd. Start samba-dcerpcd if it isn't
+ * already running.
+ */
+
+extern bool override_logfile;
+
+struct np_sock_connect_state {
+ struct tevent_context *ev;
+ struct samba_sockaddr addr;
+ const struct named_pipe_auth_req *npa_req;
+ struct named_pipe_auth_rep *npa_rep;
+
+ DATA_BLOB npa_blob;
+ struct iovec iov;
+
+ int sock;
+ struct tevent_req *subreq;
+ struct tstream_context *transport;
+ struct tstream_context *npa_stream;
+};
+
+static void np_sock_connect_cleanup(
+ struct tevent_req *req, enum tevent_req_state req_state);
+static void np_sock_connect_before(void *private_data);
+static void np_sock_connect_after(void *private_data);
+static void np_sock_connect_connected(struct tevent_req *subreq);
+static void np_sock_connect_written(struct tevent_req *subreq);
+static void np_sock_connect_read_done(struct tevent_req *subreq);
+
+static struct tevent_req *np_sock_connect_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *sockpath,
+ const struct named_pipe_auth_req *npa_req)
+{
+ struct tevent_req *req = NULL;
+ struct np_sock_connect_state *state = NULL;
+ size_t len;
+ int ret;
+ bool ok;
+
+ req = tevent_req_create(mem_ctx, &state, struct np_sock_connect_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->npa_req = npa_req;
+ state->sock = -1;
+ state->addr.u.un.sun_family = AF_UNIX;
+
+ state->npa_rep = talloc_zero(state, struct named_pipe_auth_rep);
+ if (tevent_req_nomem(state->npa_rep, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_req_set_cleanup_fn(req, np_sock_connect_cleanup);
+
+ state->addr.sa_socklen = sizeof(struct sockaddr_un);
+ len = strlcpy(state->addr.u.un.sun_path,
+ sockpath,
+ sizeof(state->addr.u.un.sun_path));
+ if (len >= sizeof(state->addr.u.un.sun_path)) {
+ tevent_req_error(req, ENAMETOOLONG);
+ return tevent_req_post(req, ev);
+ }
+
+ state->sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (state->sock == -1) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+
+ ret = set_blocking(state->sock, true);
+ if (ret == -1) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+ ok = set_close_on_exec(state->sock);
+ if (!ok) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+
+ state->subreq = async_connect_send(
+ state,
+ ev,
+ state->sock,
+ &state->addr.u.sa,
+ state->addr.sa_socklen,
+ np_sock_connect_before,
+ np_sock_connect_after,
+ NULL);
+ if (tevent_req_nomem(state->subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(state->subreq, np_sock_connect_connected, req);
+
+ return req;
+}
+
+static void np_sock_connect_cleanup(
+ struct tevent_req *req, enum tevent_req_state req_state)
+{
+ struct np_sock_connect_state *state = tevent_req_data(
+ req, struct np_sock_connect_state);
+
+ TALLOC_FREE(state->subreq);
+ TALLOC_FREE(state->transport);
+
+ if (state->sock != -1) {
+ close(state->sock);
+ state->sock = -1;
+ }
+}
+
+static void np_sock_connect_before(void *private_data)
+{
+ become_root();
+}
+
+static void np_sock_connect_after(void *private_data)
+{
+ unbecome_root();
+}
+
+static void np_sock_connect_connected(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct np_sock_connect_state *state = tevent_req_data(
+ req, struct np_sock_connect_state);
+ enum ndr_err_code ndr_err;
+ int ret, err;
+
+ SMB_ASSERT(subreq == state->subreq);
+
+ ret = async_connect_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+ state->subreq = NULL;
+ if (ret == -1) {
+ DBG_DEBUG("async_connect_recv returned %s\n", strerror(err));
+ tevent_req_error(req, err);
+ return;
+ }
+
+ /*
+ * As a quick workaround for bug 15310 we have done the
+ * connect in blocking mode (see np_sock_connect_send()). The
+ * rest of our code expects a nonblocking socket, activate
+ * this after the connect succeeded.
+ */
+ ret = set_blocking(state->sock, false);
+ if (ret == -1) {
+ tevent_req_error(req, errno);
+ return;
+ }
+
+ ret = tstream_bsd_existing_socket(
+ state, state->sock, &state->transport);
+ if (ret == -1) {
+ err = errno;
+ DBG_DEBUG("tstream_bsd_existing_socket failed: %s\n",
+ strerror(err));
+ tevent_req_error(req, err);
+ return;
+ }
+ state->sock = -1;
+
+ ndr_err = ndr_push_struct_blob(
+ &state->npa_blob,
+ state,
+ state->npa_req,
+ (ndr_push_flags_fn_t)ndr_push_named_pipe_auth_req);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DBG_DEBUG("ndr_push_struct_blob failed: %s\n",
+ ndr_errstr(ndr_err));
+ tevent_req_error(req, ndr_map_error2errno(ndr_err));
+ return;
+ }
+ state->iov = (struct iovec) {
+ .iov_base = state->npa_blob.data,
+ .iov_len = state->npa_blob.length,
+ };
+
+ subreq = tstream_writev_send(
+ state, state->ev, state->transport, &state->iov, 1);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, np_sock_connect_written, req);
+}
+
+static void np_sock_connect_written(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct np_sock_connect_state *state = tevent_req_data(
+ req, struct np_sock_connect_state);
+ int ret, err;
+
+ ret = tstream_writev_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ DBG_DEBUG("tstream_writev_recv returned %s\n", strerror(err));
+ tevent_req_error(req, err);
+ return;
+ }
+
+ subreq = tstream_u32_read_send(
+ state, state->ev, 0x00FFFFFF, state->transport);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, np_sock_connect_read_done, req);
+}
+
+static void np_sock_connect_read_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct np_sock_connect_state *state = tevent_req_data(
+ req, struct np_sock_connect_state);
+ DATA_BLOB in;
+ int ret;
+ enum ndr_err_code ndr_err;
+
+ ret = tstream_u32_read_recv(subreq, state, &in.data, &in.length);
+ TALLOC_FREE(subreq);
+ if (tevent_req_error(req, ret)) {
+ return;
+ }
+
+ ndr_err = ndr_pull_struct_blob_all(
+ &in,
+ state->npa_rep,
+ state->npa_rep,
+ (ndr_pull_flags_fn_t)ndr_pull_named_pipe_auth_rep);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DBG_DEBUG("ndr_pull_named_pipe_auth_rep failed: %s\n",
+ ndr_errstr(ndr_err));
+ tevent_req_error(req, ndr_map_error2errno(ndr_err));
+ return;
+ }
+ if (state->npa_rep->level != 8) {
+ DBG_DEBUG("npa level = %" PRIu32 ", expected 8\n",
+ state->npa_rep->level);
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ ret = tstream_npa_existing_stream(state,
+ &state->transport,
+ state->npa_rep->info.info8.file_type,
+ &state->npa_stream);
+ if (ret == -1) {
+ ret = errno;
+ DBG_DEBUG("tstream_npa_existing_stream failed: %s\n",
+ strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static int np_sock_connect_recv(
+ struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct tstream_context **stream)
+{
+ struct np_sock_connect_state *state = tevent_req_data(
+ req, struct np_sock_connect_state);
+ int err;
+
+ if (tevent_req_is_unix_error(req, &err)) {
+ tevent_req_received(req);
+ return err;
+ }
+ *stream = talloc_move(mem_ctx, &state->npa_stream);
+ tevent_req_received(req);
+ return 0;
+}
+
+struct start_rpc_host_state {
+ int ready_fd;
+ struct tevent_req *read_ready_req;
+};
+
+static void start_rpc_host_cleanup(
+ struct tevent_req *req, enum tevent_req_state req_state);
+static void start_rpc_host_ready(struct tevent_req *subreq);
+
+/*
+ * Start samba-dcerpcd and wait for it to report ready.
+ */
+static struct tevent_req *start_rpc_host_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct start_rpc_host_state *state = NULL;
+ int ret;
+ int ready_fds[2] = { -1, -1 };
+ char **argv = NULL;
+ pid_t pid;
+ bool ok;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct start_rpc_host_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ ret = pipe(ready_fds);
+ if (ret == -1) {
+ ret = errno;
+ DBG_DEBUG("pipe() failed: %s\n", strerror(ret));
+ goto fail;
+ }
+
+ ok = smb_set_close_on_exec(ready_fds[0]);
+ if (!ok) {
+ ret = errno;
+ DBG_DEBUG("smb_set_close_on_exec failed: %s\n",
+ strerror(ret));
+ goto fail;
+ }
+
+ argv = str_list_make_empty(mem_ctx);
+ str_list_add_printf(
+ &argv, "%s/samba-dcerpcd", get_dyn_SAMBA_LIBEXECDIR());
+ if (!is_default_dyn_CONFIGFILE()) {
+ str_list_add_printf(
+ &argv, "--configfile=%s", get_dyn_CONFIGFILE());
+ }
+ str_list_add_printf(&argv, "--libexec-rpcds");
+ str_list_add_printf(&argv, "--ready-signal-fd=%d", ready_fds[1]);
+ str_list_add_printf(&argv, "--np-helper");
+ str_list_add_printf(
+ &argv, "--debuglevel=%d", debuglevel_get_class(DBGC_RPC_SRV));
+ if (!is_default_dyn_LOGFILEBASE()) {
+ str_list_add_printf(
+ &argv, "--log-basename=%s", get_dyn_LOGFILEBASE());
+ }
+ if (argv == NULL) {
+ errno = ENOMEM;
+ goto fail;
+ }
+
+ become_root();
+ ret = posix_spawn(&pid, argv[0], NULL, NULL, argv, environ);
+ unbecome_root();
+ if (ret != 0) {
+ DBG_DEBUG("posix_spawn() failed: %s\n", strerror(ret));
+ goto fail;
+ }
+
+ state->ready_fd = ready_fds[0];
+ ready_fds[0] = -1;
+ tevent_req_set_cleanup_fn(req, start_rpc_host_cleanup);
+
+ close(ready_fds[1]);
+ ready_fds[1] = -1;
+
+ subreq = read_packet_send(state, ev, state->ready_fd, 1, NULL, NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, start_rpc_host_ready, req);
+ return req;
+
+fail:
+ if (ready_fds[0] != -1) {
+ close(ready_fds[0]);
+ ready_fds[0] = -1;
+ }
+ if (ready_fds[1] != -1) {
+ close(ready_fds[1]);
+ ready_fds[1] = -1;
+ }
+ tevent_req_error(req, ret);
+ return tevent_req_post(req, ev);
+}
+
+static void start_rpc_host_cleanup(
+ struct tevent_req *req, enum tevent_req_state req_state)
+{
+ struct start_rpc_host_state *state = tevent_req_data(
+ req, struct start_rpc_host_state);
+
+ if (state->ready_fd != -1) {
+ close(state->ready_fd);
+ state->ready_fd = -1;
+ }
+}
+
+static void start_rpc_host_ready(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct start_rpc_host_state *state = tevent_req_data(
+ req, struct start_rpc_host_state);
+ uint8_t *buf;
+ int err;
+ ssize_t nread;
+
+ nread = read_packet_recv(subreq, state, &buf, &err);
+ TALLOC_FREE(subreq);
+ if (nread == -1) {
+ tevent_req_error(req, err);
+ return;
+ }
+
+ close(state->ready_fd);
+ state->ready_fd = -1;
+
+ tevent_req_done(req);
+}
+
+static int start_rpc_host_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_unix(req);
+}
+
+struct local_np_connect_state {
+ struct tevent_context *ev;
+ const char *socketpath;
+ struct named_pipe_auth_req *npa_req;
+ struct tstream_context *npa_stream;
+};
+
+static void local_np_connect_connected(struct tevent_req *subreq);
+static void local_np_connect_started(struct tevent_req *subreq);
+static void local_np_connect_retried(struct tevent_req *subreq);
+
+/**
+ * @brief Async connect to a local named pipe RPC interface
+ *
+ * Start "samba-dcerpcd" on demand if it does not exist
+ *
+ * @param[in] mem_ctx The memory context to use.
+ * @param[in] ev The tevent context to use.
+ *
+ * @param[in] pipename The raw pipename to connect to without path
+ * @param[in] remote_client_name The client name to transmit
+ * @param[in] remote_client_addr The client addr to transmit
+ * @param[in] local_server_name The server name to transmit
+ * @param[in] local_server_addr The server addr to transmit
+ * @param[in] session_info The authorization info to use
+ * @param[in] need_idle_server Does this need to be an exclusive server?
+ * @return The tevent_req that was started
+ */
+
+struct tevent_req *local_np_connect_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *pipename,
+ enum dcerpc_transport_t transport,
+ const char *remote_client_name,
+ const struct tsocket_address *remote_client_addr,
+ const char *local_server_name,
+ const struct tsocket_address *local_server_addr,
+ const struct auth_session_info *session_info,
+ bool need_idle_server)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct local_np_connect_state *state = NULL;
+ struct named_pipe_auth_req_info8 *i8 = NULL;
+ const char *socket_dir = NULL;
+ char *lower_case_pipename = NULL;
+ struct dom_sid npa_sid = global_sid_Samba_NPA_Flags;
+ uint32_t npa_flags = 0;
+ struct security_token *token = NULL;
+ NTSTATUS status;
+ size_t num_npa_sids;
+ bool ok;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct local_np_connect_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+
+ num_npa_sids =
+ security_token_count_flag_sids(session_info->security_token,
+ &npa_sid,
+ 1,
+ NULL);
+ if (num_npa_sids != 0) {
+ DBG_ERR("ERROR: %zu NPA Flags SIDs have already been "
+ "detected in the security token!\n",
+ num_npa_sids);
+ tevent_req_error(req, EACCES);
+ return tevent_req_post(req, ev);
+ }
+
+ socket_dir = lp_parm_const_string(
+ GLOBAL_SECTION_SNUM, "external_rpc_pipe", "socket_dir",
+ lp_ncalrpc_dir());
+ if (socket_dir == NULL) {
+ DBG_DEBUG("external_rpc_pipe:socket_dir not set\n");
+ tevent_req_error(req, EINVAL);
+ return tevent_req_post(req, ev);
+ }
+
+ lower_case_pipename = strlower_talloc(state, pipename);
+ if (tevent_req_nomem(lower_case_pipename, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * Ensure we cannot process a path that exits
+ * the socket_dir.
+ */
+ if (ISDOTDOT(lower_case_pipename) ||
+ (strchr(lower_case_pipename, '/')!=NULL))
+ {
+ DBG_DEBUG("attempt to connect to invalid pipe pathname %s\n",
+ lower_case_pipename);
+ tevent_req_error(req, ENOENT);
+ return tevent_req_post(req, ev);
+ }
+
+ state->socketpath = talloc_asprintf(
+ state, "%s/np/%s", socket_dir, lower_case_pipename);
+ if (tevent_req_nomem(state->socketpath, req)) {
+ return tevent_req_post(req, ev);
+ }
+ TALLOC_FREE(lower_case_pipename);
+
+ state->npa_req = talloc_zero(state, struct named_pipe_auth_req);
+ if (tevent_req_nomem(state->npa_req, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->npa_req->level = 8;
+
+ i8 = &state->npa_req->info.info8;
+
+ i8->transport = transport;
+
+ /* we don't have "int" in IDL, make sure we don't overflow */
+ SMB_ASSERT(i8->transport == transport);
+
+ if (remote_client_name == NULL) {
+ remote_client_name = get_myname(state->npa_req);
+ if (remote_client_name == NULL) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+ }
+ i8->remote_client_name = remote_client_name;
+
+ if (remote_client_addr == NULL) {
+ struct tsocket_address *addr = NULL;
+ int ret = tsocket_address_inet_from_strings(
+ state->npa_req, "ip", NULL, 0, &addr);
+ if (ret != 0) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+ remote_client_addr = addr;
+ }
+ i8->remote_client_addr =
+ tsocket_address_inet_addr_string(remote_client_addr,
+ state->npa_req);
+ if (i8->remote_client_addr == NULL) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+ i8->remote_client_port = tsocket_address_inet_port(remote_client_addr);
+
+ if (local_server_name == NULL) {
+ local_server_name = remote_client_name;
+ }
+ i8->local_server_name = local_server_name;
+
+ if (local_server_addr == NULL) {
+ struct tsocket_address *addr = NULL;
+ int ret = tsocket_address_inet_from_strings(
+ state->npa_req, "ip", NULL, 0, &addr);
+ if (ret != 0) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+ local_server_addr = addr;
+ }
+ i8->local_server_addr =
+ tsocket_address_inet_addr_string(local_server_addr,
+ state->npa_req);
+ if (i8->local_server_addr == NULL) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+ i8->local_server_port = tsocket_address_inet_port(local_server_addr);
+
+ i8->session_info = talloc_zero(state->npa_req,
+ struct auth_session_info_transport);
+ if (tevent_req_nomem(i8->session_info, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ i8->session_info->session_info =
+ copy_session_info(i8->session_info, session_info);
+ if (tevent_req_nomem(i8->session_info->session_info, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (need_idle_server) {
+ npa_flags |= SAMBA_NPA_FLAGS_NEED_IDLE;
+ }
+
+ ok = winbind_env_set();
+ if (ok) {
+ npa_flags |= SAMBA_NPA_FLAGS_WINBIND_OFF;
+ }
+
+ ok = sid_append_rid(&npa_sid, npa_flags);
+ if (!ok) {
+ tevent_req_error(req, EINVAL);
+ return tevent_req_post(req, ev);
+ }
+
+ token = i8->session_info->session_info->security_token;
+
+ status = add_sid_to_array_unique(token,
+ &npa_sid,
+ &token->sids,
+ &token->num_sids);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_oom(req);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = np_sock_connect_send(
+ state, state->ev, state->socketpath, state->npa_req);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, local_np_connect_connected, req);
+
+ return req;
+}
+
+static void local_np_connect_connected(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct local_np_connect_state *state = tevent_req_data(
+ req, struct local_np_connect_state);
+ int ret;
+
+ ret = np_sock_connect_recv(subreq, state, &state->npa_stream);
+ TALLOC_FREE(subreq);
+
+ if (ret == 0) {
+ tevent_req_done(req);
+ return;
+ }
+
+ DBG_DEBUG("np_sock_connect failed: %s\n", strerror(ret));
+
+ if (!lp_rpc_start_on_demand_helpers()) {
+ /*
+ * samba-dcerpcd should already be started in
+ * daemon/standalone mode when "rpc start on demand
+ * helpers = false". We are prohibited from starting
+ * on demand as a named-pipe helper.
+ */
+ DBG_ERR("Can't connect to a running samba-dcerpcd. smb.conf "
+ "config prohibits starting as named pipe helper as "
+ "the [global] section contains "
+ "\"rpc start on demand helpers = false\".\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ /*
+ * samba-dcerpcd isn't running. We need to start it.
+ * Note if it doesn't start we treat this as a fatal
+ * error for connecting to the named pipe and don't
+ * keep trying to restart for this connection.
+ */
+ subreq = start_rpc_host_send(state, state->ev);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, local_np_connect_started, req);
+}
+
+static void local_np_connect_started(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct local_np_connect_state *state = tevent_req_data(
+ req, struct local_np_connect_state);
+ int ret;
+
+ ret = start_rpc_host_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_error(req, ret)) {
+ DBG_DEBUG("start_rpc_host_recv failed: %s\n",
+ strerror(ret));
+ return;
+ }
+
+ subreq = np_sock_connect_send(
+ state, state->ev, state->socketpath, state->npa_req);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, local_np_connect_retried, req);
+}
+
+static void local_np_connect_retried(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct local_np_connect_state *state = tevent_req_data(
+ req, struct local_np_connect_state);
+ int ret;
+
+ ret = np_sock_connect_recv(subreq, state, &state->npa_stream);
+ TALLOC_FREE(subreq);
+ if (tevent_req_error(req, ret)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+/**
+ * @brief Receive handle to a local named pipe RPC interface
+ *
+ * @param[in] req The tevent_req that started the operation
+ * @param[in] ev The tevent context to use.
+ * @param[in] mem_ctx The memory context to put pstream on
+ * @param[out] pstream The established connection to the RPC server
+ *
+ * @return 0/errno
+ */
+
+int local_np_connect_recv(
+ struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct tstream_context **pstream)
+{
+ struct local_np_connect_state *state = tevent_req_data(
+ req, struct local_np_connect_state);
+ int err;
+
+ if (tevent_req_is_unix_error(req, &err)) {
+ tevent_req_received(req);
+ return err;
+ }
+
+ *pstream = talloc_move(mem_ctx, &state->npa_stream);
+ return 0;
+}
+
+/**
+ * @brief Sync connect to a local named pipe RPC interface
+ *
+ * Start "samba-dcerpcd" on demand if it does not exist
+ *
+ * @param[in] pipename The raw pipename to connect to without path
+ * @param[in] remote_client_name The client name to transmit
+ * @param[in] remote_client_addr The client addr to transmit
+ * @param[in] local_server_name The server name to transmit
+ * @param[in] local_server_addr The server addr to transmit
+ * @param[in] session_info The authorization info to use
+ * @param[in] need_idle_server Does this need to be an exclusive server?
+ * @param[in] mem_ctx The memory context to use.
+ * @param[out] pstream The established connection to the RPC server
+ * @return 0/errno
+ */
+
+int local_np_connect(
+ const char *pipename,
+ enum dcerpc_transport_t transport,
+ const char *remote_client_name,
+ const struct tsocket_address *remote_client_addr,
+ const char *local_server_name,
+ const struct tsocket_address *local_server_addr,
+ const struct auth_session_info *session_info,
+ bool need_idle_server,
+ TALLOC_CTX *mem_ctx,
+ struct tstream_context **pstream)
+{
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ int ret = ENOMEM;
+
+ ev = samba_tevent_context_init(mem_ctx);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = local_np_connect_send(
+ ev,
+ ev,
+ pipename,
+ transport,
+ remote_client_name,
+ remote_client_addr,
+ local_server_name,
+ local_server_addr,
+ session_info,
+ need_idle_server);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_unix(req, ev, &ret)) {
+ goto fail;
+ }
+ ret = local_np_connect_recv(req, mem_ctx, pstream);
+ fail:
+ TALLOC_FREE(req);
+ TALLOC_FREE(ev);
+ return ret;
+}
diff --git a/source3/rpc_client/local_np.h b/source3/rpc_client/local_np.h
new file mode 100644
index 0000000..093faad
--- /dev/null
+++ b/source3/rpc_client/local_np.h
@@ -0,0 +1,56 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * 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 _LOCAL_NP_H_
+#define _LOCAL_NP_H_
+
+#include <replace.h>
+#include "lib/tsocket/tsocket.h"
+#include "librpc/rpc/rpc_common.h"
+
+struct auth_session_info;
+
+struct tevent_req *local_np_connect_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *pipename,
+ enum dcerpc_transport_t transport,
+ const char *remote_client_name,
+ const struct tsocket_address *remote_client_addr,
+ const char *local_server_name,
+ const struct tsocket_address *local_server_addr,
+ const struct auth_session_info *session_info,
+ bool need_idle_server);
+
+int local_np_connect_recv(
+ struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct tstream_context **pstream);
+
+int local_np_connect(
+ const char *pipename,
+ enum dcerpc_transport_t transport,
+ const char *remote_client_name,
+ const struct tsocket_address *remote_client_addr,
+ const char *local_server_name,
+ const struct tsocket_address *local_server_addr,
+ const struct auth_session_info *session_info,
+ bool need_idle_server,
+ TALLOC_CTX *mem_ctx,
+ struct tstream_context **stream);
+
+#endif /* _LOCAL_NP_H_ */
diff --git a/source3/rpc_client/py_mdscli.c b/source3/rpc_client/py_mdscli.c
new file mode 100644
index 0000000..ccb0342
--- /dev/null
+++ b/source3/rpc_client/py_mdscli.c
@@ -0,0 +1,572 @@
+/*
+ Python interface to cli_mdssvc
+
+ Copyright (C) Ralph Boehme 2019
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "lib/replace/system/python.h"
+#include <pytalloc.h>
+#include "includes.h"
+#include "python/py3compat.h"
+#include "python/modules.h"
+#include "lib/util/talloc_stack.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "librpc/rpc/rpc_common.h"
+#include "librpc/rpc/pyrpc_util.h"
+#include "rpc_client/cli_mdssvc.h"
+#include "rpc_client/cli_mdssvc_private.h"
+
+static PyObject *search_get_results(PyObject *self,
+ PyObject *args,
+ PyObject *kwargs)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ const char * const kwnames[] = {"pipe", NULL};
+ PyObject *pypipe = NULL;
+ PyObject *result = NULL;
+ dcerpc_InterfaceObject *pipe = NULL;
+ struct tevent_req *req = NULL;
+ struct mdscli_search_ctx *search = NULL;
+ uint64_t *cnids = NULL;
+ size_t i;
+ size_t ncnids;
+ NTSTATUS status;
+ int ret;
+ bool ok;
+
+ if (!PyArg_ParseTupleAndKeywords(args,
+ kwargs,
+ "O",
+ discard_const_p(char *, kwnames),
+ &pypipe)) {
+ PyErr_SetString(PyExc_RuntimeError, "Failed to parse args");
+ goto out;
+ }
+
+ ok = py_check_dcerpc_type(pypipe,
+ "samba.dcerpc.base",
+ "ClientConnection");
+ if (!ok) {
+ goto out;
+ }
+
+ pipe = (dcerpc_InterfaceObject *)pypipe;
+
+ search = pytalloc_get_type(self, struct mdscli_search_ctx);
+ if (search == NULL) {
+ goto out;
+ }
+
+ /*
+ * We must use the async send/recv versions in order to pass the correct
+ * tevent context, here and any other place we call mdscli_*
+ * functions. Using the sync version we would be polling a temporary
+ * event context, but unfortunately the s4 Python RPC bindings dispatch
+ * events through
+ *
+ * dcerpc_bh_raw_call_send()
+ * -> dcerpc_request_send()
+ * -> dcerpc_schedule_io_trigger()
+ * -> dcerpc_send_request()
+ * -> tstream_writev_queue_send()
+ *
+ * on an hardcoded event context allocated via
+ *
+ * py_dcerpc_interface_init_helper()
+ * -> dcerpc_pipe_connect()
+ */
+again:
+ req = mdscli_get_results_send(frame,
+ pipe->ev,
+ search);
+ if (req == NULL) {
+ PyErr_NoMemory();
+ goto out;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) {
+ PyErr_SetNTSTATUS(status);
+ goto out;
+ }
+
+ status = mdscli_get_results_recv(req, frame, &cnids);
+ TALLOC_FREE(req);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_PENDING)) {
+ sleep(1);
+ goto again;
+ }
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_MATCHES))
+ {
+ PyErr_SetNTSTATUS(status);
+ goto out;
+ }
+
+ result = Py_BuildValue("[]");
+
+ ncnids = talloc_array_length(cnids);
+ for (i = 0; i < ncnids; i++) {
+ char *path = NULL;
+ PyObject *pypath = NULL;
+
+ req = mdscli_get_path_send(frame,
+ pipe->ev,
+ search->mdscli_ctx,
+ cnids[i]);
+ if (req == NULL) {
+ PyErr_NoMemory();
+ Py_DECREF(result);
+ result = NULL;
+ goto out;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) {
+ PyErr_SetNTSTATUS(status);
+ Py_DECREF(result);
+ result = NULL;
+ goto out;
+ }
+
+ status = mdscli_get_path_recv(req, frame, &path);
+ TALLOC_FREE(req);
+ PyErr_NTSTATUS_NOT_OK_RAISE(status);
+
+ pypath = PyUnicode_FromString(path);
+ if (pypath == NULL) {
+ PyErr_NoMemory();
+ Py_DECREF(result);
+ result = NULL;
+ goto out;
+ }
+
+ ret = PyList_Append(result, pypath);
+ Py_DECREF(pypath);
+ if (ret == -1) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "list append failed");
+ Py_DECREF(result);
+ result = NULL;
+ goto out;
+ }
+ }
+
+out:
+ talloc_free(frame);
+ return result;
+}
+
+static PyObject *search_close(PyObject *self,
+ PyObject *args,
+ PyObject *kwargs)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ const char * const kwnames[] = {"pipe", NULL};
+ PyObject *pypipe = NULL;
+ dcerpc_InterfaceObject *pipe = NULL;
+ struct tevent_req *req = NULL;
+ struct mdscli_search_ctx *search = NULL;
+ NTSTATUS status;
+ bool ok;
+
+ if (!PyArg_ParseTupleAndKeywords(args,
+ kwargs,
+ "O",
+ discard_const_p(char *, kwnames),
+ &pypipe)) {
+ PyErr_SetString(PyExc_RuntimeError, "Failed to parse args");
+ goto fail;
+ }
+
+ ok = py_check_dcerpc_type(pypipe,
+ "samba.dcerpc.base",
+ "ClientConnection");
+ if (!ok) {
+ goto fail;
+ }
+
+ pipe = (dcerpc_InterfaceObject *)pypipe;
+
+ search = pytalloc_get_type(self, struct mdscli_search_ctx);
+ if (search == NULL) {
+ goto fail;
+ }
+
+ req = mdscli_close_search_send(frame,
+ pipe->ev,
+ &search);
+ if (req == NULL) {
+ PyErr_NoMemory();
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) {
+ PyErr_SetNTSTATUS(status);
+ goto fail;
+ }
+
+ status = mdscli_close_search_recv(req);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_MATCHES))
+ {
+ PyErr_SetNTSTATUS(status);
+ goto fail;
+ }
+ TALLOC_FREE(req);
+
+ talloc_free(frame);
+ Py_INCREF(Py_None);
+ return Py_None;
+
+fail:
+ talloc_free(frame);
+ return NULL;
+}
+
+static PyMethodDef search_methods[] = {
+ {
+ .ml_name = "get_results",
+ .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, search_get_results),
+ .ml_flags = METH_VARARGS|METH_KEYWORDS,
+ .ml_doc = "",
+ },
+ {
+ .ml_name = "close",
+ .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, search_close),
+ .ml_flags = METH_VARARGS|METH_KEYWORDS,
+ .ml_doc = "",
+ },
+ {0},
+};
+
+static PyObject *search_new(PyTypeObject *type,
+ PyObject *args,
+ PyObject *kwds)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct mdscli_search_ctx *search = NULL;
+ PyObject *self = NULL;
+
+ search = talloc_zero(frame, struct mdscli_search_ctx);
+ if (search == NULL) {
+ PyErr_NoMemory();
+ talloc_free(frame);
+ return NULL;
+ }
+
+ self = pytalloc_steal(type, search);
+ talloc_free(frame);
+ return self;
+}
+
+static PyTypeObject search_type = {
+ .tp_name = "mdscli.ctx.search",
+ .tp_new = search_new,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_doc = "search([....]) -> mdssvc client search context\n",
+ .tp_methods = search_methods,
+};
+
+static PyObject *conn_sharepath(PyObject *self,
+ PyObject *unused)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct mdscli_ctx *ctx = NULL;
+ char *sharepath = NULL;
+ PyObject *result = NULL;
+
+ ctx = pytalloc_get_type(self, struct mdscli_ctx);
+ if (ctx == NULL) {
+ goto fail;
+ }
+
+ sharepath = mdscli_get_basepath(frame, ctx);
+ if (sharepath == NULL) {
+ PyErr_NoMemory();
+ goto fail;
+ }
+
+ result = PyUnicode_FromString(sharepath);
+
+fail:
+ talloc_free(frame);
+ return result;
+}
+
+static PyObject *conn_search(PyObject *self,
+ PyObject *args,
+ PyObject *kwargs)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ PyObject *pypipe = NULL;
+ dcerpc_InterfaceObject *pipe = NULL;
+ struct mdscli_ctx *ctx = NULL;
+ PyObject *result = NULL;
+ char *query = NULL;
+ char *basepath = NULL;
+ struct tevent_req *req = NULL;
+ struct mdscli_search_ctx *search = NULL;
+ const char * const kwnames[] = {
+ "pipe", "query", "basepath", NULL
+ };
+ NTSTATUS status;
+ bool ok;
+
+ if (!PyArg_ParseTupleAndKeywords(args,
+ kwargs,
+ "Oss",
+ discard_const_p(char *, kwnames),
+ &pypipe,
+ &query,
+ &basepath)) {
+ PyErr_SetString(PyExc_RuntimeError, "Failed to parse args");
+ goto fail;
+ }
+
+ ok = py_check_dcerpc_type(pypipe,
+ "samba.dcerpc.base",
+ "ClientConnection");
+ if (!ok) {
+ goto fail;
+ }
+
+ pipe = (dcerpc_InterfaceObject *)pypipe;
+
+ ctx = pytalloc_get_type(self, struct mdscli_ctx);
+ if (ctx == NULL) {
+ goto fail;
+ }
+
+ req = mdscli_search_send(frame,
+ pipe->ev,
+ ctx,
+ query,
+ basepath,
+ false);
+ if (req == NULL) {
+ PyErr_NoMemory();
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) {
+ PyErr_SetNTSTATUS(status);
+ goto fail;
+ }
+
+ status = mdscli_search_recv(req, frame, &search);
+ PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+ result = pytalloc_steal(&search_type, search);
+
+fail:
+ talloc_free(frame);
+ return result;
+}
+
+static PyObject *conn_disconnect(PyObject *self,
+ PyObject *args,
+ PyObject *kwargs)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ PyObject *pypipe = NULL;
+ dcerpc_InterfaceObject *pipe = NULL;
+ struct mdscli_ctx *ctx = NULL;
+ struct tevent_req *req = NULL;
+ const char * const kwnames[] = {"pipe", NULL};
+ NTSTATUS status;
+ bool ok;
+
+ if (!PyArg_ParseTupleAndKeywords(args,
+ kwargs,
+ "O",
+ discard_const_p(char *, kwnames),
+ &pypipe)) {
+ PyErr_SetString(PyExc_RuntimeError, "Failed to parse args");
+ goto fail;
+ }
+
+ ok = py_check_dcerpc_type(pypipe,
+ "samba.dcerpc.base",
+ "ClientConnection");
+ if (!ok) {
+ goto fail;
+ }
+
+ pipe = (dcerpc_InterfaceObject *)pypipe;
+
+ ctx = pytalloc_get_type(self, struct mdscli_ctx);
+ if (ctx == NULL) {
+ goto fail;
+ }
+
+ req = mdscli_disconnect_send(frame, pipe->ev, ctx);
+ if (req == NULL) {
+ PyErr_NoMemory();
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) {
+ PyErr_SetNTSTATUS(status);
+ goto fail;
+ }
+
+ status = mdscli_disconnect_recv(req);
+ PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+ talloc_free(frame);
+ Py_INCREF(Py_None);
+ return Py_None;
+
+fail:
+ talloc_free(frame);
+ return NULL;
+}
+
+static PyMethodDef conn_methods[] = {
+ {
+ .ml_name = "sharepath",
+ .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, conn_sharepath),
+ .ml_flags = METH_NOARGS,
+ .ml_doc = "mdscli.conn.sharepath(...) -> get share basepath",
+ },
+ {
+ .ml_name = "search",
+ .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, conn_search),
+ .ml_flags = METH_VARARGS|METH_KEYWORDS,
+ .ml_doc = "mdscli.conn.search(...) -> run mdssvc query",
+ },
+ {
+ .ml_name = "disconnect",
+ .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, conn_disconnect),
+ .ml_flags = METH_VARARGS|METH_KEYWORDS,
+ .ml_doc = "mdscli.conn.disconnect(...) -> disconnect",
+ },
+ {0},
+};
+
+static PyObject *conn_new(PyTypeObject *type,
+ PyObject *args,
+ PyObject *kwargs)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ const char * const kwnames[] = { "pipe", "share", "mountpoint", NULL };
+ PyObject *pypipe = NULL;
+ dcerpc_InterfaceObject *pipe = NULL;
+ struct tevent_req *req = NULL;
+ char *share = NULL;
+ char *mountpoint = NULL;
+ struct mdscli_ctx *ctx = NULL;
+ PyObject *self = NULL;
+ NTSTATUS status;
+ bool ok;
+
+ if (!PyArg_ParseTupleAndKeywords(args,
+ kwargs,
+ "Oss",
+ discard_const_p(char *, kwnames),
+ &pypipe,
+ &share,
+ &mountpoint)) {
+ PyErr_SetString(PyExc_RuntimeError, "Failed to parse args");
+ goto fail;
+ }
+
+ ok = py_check_dcerpc_type(pypipe,
+ "samba.dcerpc.base",
+ "ClientConnection");
+ if (!ok) {
+ goto fail;
+ }
+
+ pipe = (dcerpc_InterfaceObject *)pypipe;
+
+ req = mdscli_connect_send(frame,
+ pipe->ev,
+ pipe->binding_handle,
+ share,
+ mountpoint);
+ if (req == NULL) {
+ PyErr_NoMemory();
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) {
+ PyErr_SetNTSTATUS(status);
+ goto fail;
+ }
+
+ status = mdscli_connect_recv(req, frame, &ctx);
+ PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+ self = pytalloc_steal(type, ctx);
+
+fail:
+ talloc_free(frame);
+ return self;
+}
+
+static PyTypeObject conn_type = {
+ .tp_name = "mdscli.conn",
+ .tp_new = conn_new,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_doc = "conn([....]) -> mdssvc connection\n",
+ .tp_methods = conn_methods,
+};
+
+static PyMethodDef mdscli_methods[] = {
+ {0},
+};
+
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "mdscli",
+ .m_doc = "RPC mdssvc client",
+ .m_size = -1,
+ .m_methods = mdscli_methods,
+};
+
+MODULE_INIT_FUNC(mdscli)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ PyObject *m = NULL;
+ int ret;
+
+ ret = pytalloc_BaseObject_PyType_Ready(&conn_type);
+ if (ret < 0) {
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ ret = pytalloc_BaseObject_PyType_Ready(&search_type);
+ if (ret < 0) {
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ m = PyModule_Create(&moduledef);
+ if (m == NULL) {
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ Py_INCREF(&conn_type);
+ PyModule_AddObject(m, "conn", (PyObject *)&conn_type);
+
+ Py_INCREF(&search_type);
+ PyModule_AddObject(m, "search", (PyObject *)&search_type);
+
+ TALLOC_FREE(frame);
+ return m;
+}
diff --git a/source3/rpc_client/rpc_client.h b/source3/rpc_client/rpc_client.h
new file mode 100644
index 0000000..f1be075
--- /dev/null
+++ b/source3/rpc_client/rpc_client.h
@@ -0,0 +1,52 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * RPC Pipe client routines
+ *
+ * Copyright (c) 2005 Jeremy Allison
+ * Copyright (c) 2010 Simo Sorce
+ *
+ * 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 _RPC_CLIENT_H
+#define _RPC_CLIENT_H
+
+#include "librpc/gen_ndr/dcerpc.h"
+#include "librpc/rpc/dcerpc.h"
+#include "../librpc/ndr/libndr.h"
+#include "rpc_client/rpc_transport.h"
+
+struct dcerpc_binding_handle;
+
+struct rpc_pipe_client {
+ struct rpc_pipe_client *prev, *next;
+
+ struct rpc_cli_transport *transport;
+ struct dcerpc_binding_handle *binding_handle;
+
+ struct ndr_syntax_id abstract_syntax;
+ struct ndr_syntax_id transfer_syntax;
+ bool verified_pcontext;
+
+ char *desthost;
+ char *srv_name_slash;
+
+ uint16_t max_xmit_frag;
+
+ struct pipe_auth_data *auth;
+};
+
+#endif /* _RPC_CLIENT_H */
diff --git a/source3/rpc_client/rpc_transport.h b/source3/rpc_client/rpc_transport.h
new file mode 100644
index 0000000..f352f60
--- /dev/null
+++ b/source3/rpc_client/rpc_transport.h
@@ -0,0 +1,104 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC client transport
+ * Copyright (C) Volker Lendecke 2009
+ * Copyright (C) Simo Sorce 2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _RPC_CLIENT_RPC_TRANSPORT_H_
+#define _RPC_CLIENT_RPC_TRANSPORT_H_
+
+#include "librpc/rpc/dcerpc.h"
+
+/**
+ * rpc_cli_transport defines a transport mechanism to ship rpc requests
+ * asynchronously to a server and receive replies
+ */
+
+struct rpc_cli_transport {
+
+ enum dcerpc_transport_t transport;
+
+ /**
+ * Trigger an async read from the server. May return a short read.
+ */
+ struct tevent_req *(*read_send)(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ uint8_t *data, size_t size,
+ void *priv);
+ /**
+ * Get the result from the read_send operation.
+ */
+ NTSTATUS (*read_recv)(struct tevent_req *req, ssize_t *preceived);
+
+ /**
+ * Trigger an async write to the server. May return a short write.
+ */
+ struct tevent_req *(*write_send)(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const uint8_t *data, size_t size,
+ void *priv);
+ /**
+ * Get the result from the read_send operation.
+ */
+ NTSTATUS (*write_recv)(struct tevent_req *req, ssize_t *psent);
+
+ /**
+ * This is an optimization for the SMB transport. It models the
+ * TransactNamedPipe API call: Send and receive data in one round
+ * trip. The transport implementation is free to set this to NULL,
+ * cli_pipe.c will fall back to the explicit write/read routines.
+ */
+ struct tevent_req *(*trans_send)(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const uint8_t *data, size_t data_len,
+ uint32_t max_rdata_len,
+ void *priv);
+ /**
+ * Get the result from the trans_send operation.
+ */
+ NTSTATUS (*trans_recv)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint8_t **prdata, uint32_t *prdata_len);
+
+ bool (*is_connected)(void *priv);
+ unsigned int (*set_timeout)(void *priv, unsigned int timeout);
+
+ void *priv;
+};
+
+/* The following definitions come from rpc_client/rpc_transport_np.c */
+struct cli_state;
+struct tevent_req *rpc_transport_np_init_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const struct ndr_interface_table *table);
+NTSTATUS rpc_transport_np_init_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_cli_transport **presult);
+
+/* The following definitions come from rpc_client/rpc_transport_sock.c */
+
+NTSTATUS rpc_transport_sock_init(TALLOC_CTX *mem_ctx, int fd,
+ struct rpc_cli_transport **presult);
+
+/* The following definitions come from rpc_client/rpc_transport_tstream.c */
+
+NTSTATUS rpc_transport_tstream_init(TALLOC_CTX *mem_ctx,
+ struct tstream_context **stream,
+ struct rpc_cli_transport **presult);
+struct tstream_context *rpc_transport_get_tstream(
+ struct rpc_cli_transport *transport);
+#endif /* _RPC_CLIENT_RPC_TRANSPORT_H_ */
diff --git a/source3/rpc_client/rpc_transport_np.c b/source3/rpc_client/rpc_transport_np.c
new file mode 100644
index 0000000..21266c3
--- /dev/null
+++ b/source3/rpc_client/rpc_transport_np.c
@@ -0,0 +1,179 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC client transport over named pipes
+ * Copyright (C) Volker Lendecke 2009
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "librpc/rpc/dcerpc_util.h"
+#include "rpc_client/rpc_transport.h"
+#include "librpc/ndr/ndr_table.h"
+#include "libcli/smb/smbXcli_base.h"
+#include "libcli/smb/tstream_smbXcli_np.h"
+#include "client.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_CLI
+
+struct rpc_transport_np_init_state {
+ struct rpc_cli_transport *transport;
+ int retries;
+ struct tevent_context *ev;
+ struct smbXcli_conn *conn;
+ int timeout;
+ struct timeval abs_timeout;
+ const char *pipe_name;
+ struct smbXcli_session *session;
+ struct smbXcli_tcon *tcon;
+ uint16_t pid;
+};
+
+static void rpc_transport_np_init_pipe_open(struct tevent_req *subreq);
+
+struct tevent_req *rpc_transport_np_init_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const struct ndr_interface_table *table)
+{
+ struct tevent_req *req;
+ struct rpc_transport_np_init_state *state;
+ struct tevent_req *subreq;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct rpc_transport_np_init_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ state->tcon = cli->smb2.tcon;
+ state->session = cli->smb2.session;
+ } else {
+ state->tcon = cli->smb1.tcon;
+ state->session = cli->smb1.session;
+ state->pid = cli->smb1.pid;
+ }
+
+ state->ev = ev;
+ state->conn = cli->conn;
+ state->timeout = cli->timeout;
+ state->abs_timeout = timeval_current_ofs_msec(cli->timeout);
+ state->pipe_name = dcerpc_default_transport_endpoint(state, NCACN_NP,
+ table);
+ if (tevent_req_nomem(state->pipe_name, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ while (state->pipe_name[0] == '\\') {
+ state->pipe_name++;
+ }
+
+ subreq = tstream_smbXcli_np_open_send(state, ev, state->conn,
+ state->session, state->tcon,
+ state->pid, state->timeout,
+ state->pipe_name);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, rpc_transport_np_init_pipe_open, req);
+
+ return req;
+}
+
+static void rpc_transport_np_init_pipe_open_retry(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval t,
+ void *priv_data)
+{
+ struct tevent_req *subreq;
+ struct tevent_req *req = talloc_get_type(priv_data, struct tevent_req);
+ struct rpc_transport_np_init_state *state = tevent_req_data(
+ req, struct rpc_transport_np_init_state);
+
+ subreq = tstream_smbXcli_np_open_send(state, ev,
+ state->conn,
+ state->session,
+ state->tcon,
+ state->pid,
+ state->timeout,
+ state->pipe_name);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, rpc_transport_np_init_pipe_open, req);
+ state->retries++;
+}
+
+static void rpc_transport_np_init_pipe_open(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct rpc_transport_np_init_state *state = tevent_req_data(
+ req, struct rpc_transport_np_init_state);
+ NTSTATUS status;
+ struct tstream_context *stream;
+
+ status = tstream_smbXcli_np_open_recv(subreq, state, &stream);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_NOT_AVAILABLE)
+ && (!timeval_expired(&state->abs_timeout))) {
+ struct tevent_timer *te;
+ /*
+ * Retry on STATUS_PIPE_NOT_AVAILABLE, Windows starts some
+ * servers (FssagentRpc) on demand.
+ */
+ DEBUG(2, ("RPC pipe %s not available, retry %d\n",
+ state->pipe_name, state->retries));
+ te = tevent_add_timer(state->ev, state,
+ timeval_current_ofs_msec(100 * state->retries),
+ rpc_transport_np_init_pipe_open_retry, req);
+ if (tevent_req_nomem(te, req)) {
+ DEBUG(2, ("Failed to create asynchronous "
+ "tevent_timer\n"));
+ }
+ return;
+ }
+
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ status = rpc_transport_tstream_init(state,
+ &stream,
+ &state->transport);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS rpc_transport_np_init_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_cli_transport **presult)
+{
+ struct rpc_transport_np_init_state *state = tevent_req_data(
+ req, struct rpc_transport_np_init_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ *presult = talloc_move(mem_ctx, &state->transport);
+ return NT_STATUS_OK;
+}
diff --git a/source3/rpc_client/rpc_transport_sock.c b/source3/rpc_client/rpc_transport_sock.c
new file mode 100644
index 0000000..a833e44
--- /dev/null
+++ b/source3/rpc_client/rpc_transport_sock.c
@@ -0,0 +1,53 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC client transport over a socket
+ * Copyright (C) Volker Lendecke 2009
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "../lib/tsocket/tsocket.h"
+#include "rpc_client/rpc_transport.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_CLI
+
+NTSTATUS rpc_transport_sock_init(TALLOC_CTX *mem_ctx, int fd,
+ struct rpc_cli_transport **presult)
+{
+ struct rpc_cli_transport *result;
+ struct tstream_context *stream;
+ int ret;
+ NTSTATUS status;
+
+ set_blocking(fd, false);
+
+ ret = tstream_bsd_existing_socket(mem_ctx, fd, &stream);
+ if (ret != 0) {
+ status = map_nt_error_from_unix(errno);
+ return status;
+ }
+
+ status = rpc_transport_tstream_init(mem_ctx,
+ &stream,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(stream);
+ return status;
+ }
+
+ *presult = result;
+ return NT_STATUS_OK;
+}
diff --git a/source3/rpc_client/rpc_transport_tstream.c b/source3/rpc_client/rpc_transport_tstream.c
new file mode 100644
index 0000000..02fc320
--- /dev/null
+++ b/source3/rpc_client/rpc_transport_tstream.c
@@ -0,0 +1,564 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC client transport over tstream
+ * Copyright (C) Simo Sorce 2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "rpc_client/rpc_transport.h"
+#include "lib/tsocket/tsocket.h"
+#include "libcli/smb/tstream_smbXcli_np.h"
+#include "cli_pipe.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_CLI
+
+struct rpc_tstream_state {
+ struct tstream_context *stream;
+ struct tevent_queue *read_queue;
+ struct tevent_queue *write_queue;
+ unsigned int timeout;
+};
+
+static void rpc_tstream_disconnect(struct rpc_tstream_state *s)
+{
+ TALLOC_FREE(s->stream);
+}
+
+static bool rpc_tstream_is_connected(void *priv)
+{
+ struct rpc_tstream_state *transp =
+ talloc_get_type_abort(priv, struct rpc_tstream_state);
+ ssize_t ret;
+
+ if (!transp->stream) {
+ return false;
+ }
+
+ if (!tstream_is_smbXcli_np(transp->stream)) {
+ return true;
+ }
+
+ ret = tstream_pending_bytes(transp->stream);
+ if (ret == -1) {
+ return false;
+ }
+
+ return true;
+}
+
+static unsigned int rpc_tstream_set_timeout(void *priv, unsigned int timeout)
+{
+ struct rpc_tstream_state *transp =
+ talloc_get_type_abort(priv, struct rpc_tstream_state);
+ int orig_timeout;
+ bool ok;
+
+ ok = rpc_tstream_is_connected(transp);
+ if (!ok) {
+ return 0;
+ }
+
+ if (tstream_is_smbXcli_np(transp->stream)) {
+ transp->timeout = timeout;
+ return tstream_smbXcli_np_set_timeout(transp->stream, timeout);
+ }
+
+ orig_timeout = transp->timeout;
+
+ transp->timeout = timeout;
+
+ return orig_timeout;
+}
+
+struct rpc_tstream_next_vector_state {
+ uint8_t *buf;
+ size_t len;
+ off_t ofs;
+};
+
+static void rpc_tstream_next_vector_init(
+ struct rpc_tstream_next_vector_state *s,
+ uint8_t *buf, size_t len)
+{
+ *s = (struct rpc_tstream_next_vector_state) {
+ .buf = buf, .len = MIN(len, UINT16_MAX),
+ };
+}
+
+static int rpc_tstream_next_vector(struct tstream_context *stream,
+ void *private_data,
+ TALLOC_CTX *mem_ctx,
+ struct iovec **_vector,
+ size_t *count)
+{
+ struct rpc_tstream_next_vector_state *state =
+ (struct rpc_tstream_next_vector_state *)private_data;
+ struct iovec *vector;
+
+ if (state->ofs == state->len) {
+ *_vector = NULL;
+ *count = 0;
+ return 0;
+ }
+
+ vector = talloc_array(mem_ctx, struct iovec, 1);
+ if (!vector) {
+ return -1;
+ }
+
+ vector[0].iov_base = state->buf;
+ vector[0].iov_len = state->len;
+
+ state->ofs = state->len;
+
+ *_vector = vector;
+ *count = 1;
+ return 0;
+}
+
+struct rpc_tstream_read_state {
+ struct rpc_tstream_state *transp;
+ struct rpc_tstream_next_vector_state next_vector;
+ ssize_t nread;
+};
+
+static void rpc_tstream_read_done(struct tevent_req *subreq);
+
+static struct tevent_req *rpc_tstream_read_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ uint8_t *data, size_t size,
+ void *priv)
+{
+ struct rpc_tstream_state *transp =
+ talloc_get_type_abort(priv, struct rpc_tstream_state);
+ struct tevent_req *req, *subreq;
+ struct rpc_tstream_read_state *state;
+ struct timeval endtime;
+
+ req = tevent_req_create(mem_ctx, &state, struct rpc_tstream_read_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ if (!rpc_tstream_is_connected(transp)) {
+ NTSTATUS status = NT_STATUS_CONNECTION_DISCONNECTED;
+ if (tstream_is_smbXcli_np(transp->stream)) {
+ status = NT_STATUS_PIPE_DISCONNECTED;
+ }
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+ state->transp = transp;
+ rpc_tstream_next_vector_init(&state->next_vector, data, size);
+
+ subreq = tstream_readv_pdu_queue_send(state, ev,
+ transp->stream,
+ transp->read_queue,
+ rpc_tstream_next_vector,
+ &state->next_vector);
+ if (subreq == NULL) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return tevent_req_post(req, ev);
+ }
+
+ endtime = timeval_current_ofs_msec(transp->timeout);
+ if (!tevent_req_set_endtime(subreq, ev, endtime)) {
+ goto fail;
+ }
+
+ tevent_req_set_callback(subreq, rpc_tstream_read_done, req);
+ return req;
+ fail:
+ TALLOC_FREE(req);
+ return NULL;
+}
+
+static void rpc_tstream_read_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct rpc_tstream_read_state *state =
+ tevent_req_data(req, struct rpc_tstream_read_state);
+ int err;
+
+ state->nread = tstream_readv_pdu_queue_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+ if (state->nread < 0) {
+ rpc_tstream_disconnect(state->transp);
+ tevent_req_nterror(req, map_nt_error_from_unix(err));
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static NTSTATUS rpc_tstream_read_recv(struct tevent_req *req, ssize_t *size)
+{
+ struct rpc_tstream_read_state *state = tevent_req_data(
+ req, struct rpc_tstream_read_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *size = state->nread;
+ return NT_STATUS_OK;
+}
+
+struct rpc_tstream_write_state {
+ struct tevent_context *ev;
+ struct rpc_tstream_state *transp;
+ struct iovec iov;
+ ssize_t nwritten;
+};
+
+static void rpc_tstream_write_done(struct tevent_req *subreq);
+
+static struct tevent_req *rpc_tstream_write_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const uint8_t *data, size_t size,
+ void *priv)
+{
+ struct rpc_tstream_state *transp =
+ talloc_get_type_abort(priv, struct rpc_tstream_state);
+ struct tevent_req *req, *subreq;
+ struct rpc_tstream_write_state *state;
+ struct timeval endtime;
+
+ req = tevent_req_create(mem_ctx, &state, struct rpc_tstream_write_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ if (!rpc_tstream_is_connected(transp)) {
+ NTSTATUS status = NT_STATUS_CONNECTION_DISCONNECTED;
+ if (tstream_is_smbXcli_np(transp->stream)) {
+ status = NT_STATUS_PIPE_DISCONNECTED;
+ }
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+ state->ev = ev;
+ state->transp = transp;
+ state->iov.iov_base = discard_const_p(void *, data);
+ state->iov.iov_len = size;
+
+ subreq = tstream_writev_queue_send(state, ev,
+ transp->stream,
+ transp->write_queue,
+ &state->iov, 1);
+ if (subreq == NULL) {
+ goto fail;
+ }
+
+ endtime = timeval_current_ofs_msec(transp->timeout);
+ if (!tevent_req_set_endtime(subreq, ev, endtime)) {
+ goto fail;
+ }
+
+ tevent_req_set_callback(subreq, rpc_tstream_write_done, req);
+ return req;
+ fail:
+ TALLOC_FREE(req);
+ return NULL;
+}
+
+static void rpc_tstream_write_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct rpc_tstream_write_state *state =
+ tevent_req_data(req, struct rpc_tstream_write_state);
+ int err;
+
+ state->nwritten = tstream_writev_queue_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+ if (state->nwritten < 0) {
+ rpc_tstream_disconnect(state->transp);
+ tevent_req_nterror(req, map_nt_error_from_unix(err));
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static NTSTATUS rpc_tstream_write_recv(struct tevent_req *req, ssize_t *sent)
+{
+ struct rpc_tstream_write_state *state =
+ tevent_req_data(req, struct rpc_tstream_write_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *sent = state->nwritten;
+ return NT_STATUS_OK;
+}
+
+struct rpc_tstream_trans_state {
+ struct tevent_context *ev;
+ struct rpc_tstream_state *transp;
+ struct iovec req;
+ uint32_t max_rdata_len;
+ struct iovec rep;
+};
+
+static void rpc_tstream_trans_writev(struct tevent_req *subreq);
+static void rpc_tstream_trans_readv_pdu(struct tevent_req *subreq);
+
+static int rpc_tstream_trans_next_vector(struct tstream_context *stream,
+ void *private_data,
+ TALLOC_CTX *mem_ctx,
+ struct iovec **_vector,
+ size_t *count);
+
+static struct tevent_req *rpc_tstream_trans_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const uint8_t *data, size_t data_len,
+ uint32_t max_rdata_len,
+ void *priv)
+{
+ struct rpc_tstream_state *transp =
+ talloc_get_type_abort(priv, struct rpc_tstream_state);
+ struct tevent_req *req, *subreq;
+ struct rpc_tstream_trans_state *state;
+ struct timeval endtime;
+ bool use_trans = false;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct rpc_tstream_trans_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (!rpc_tstream_is_connected(transp)) {
+ NTSTATUS status = NT_STATUS_CONNECTION_DISCONNECTED;
+ if (tstream_is_smbXcli_np(transp->stream)) {
+ status = NT_STATUS_PIPE_DISCONNECTED;
+ }
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+ state->ev = ev;
+ state->transp = transp;
+ state->req.iov_len = data_len;
+ state->req.iov_base = discard_const_p(void *, data);
+ state->max_rdata_len = max_rdata_len;
+
+ endtime = timeval_current_ofs_msec(transp->timeout);
+
+ if (tstream_is_smbXcli_np(transp->stream)) {
+ use_trans = true;
+ }
+ if (tevent_queue_length(transp->write_queue) > 0) {
+ use_trans = false;
+ }
+ if (tevent_queue_length(transp->read_queue) > 0) {
+ use_trans = false;
+ }
+
+ if (use_trans) {
+ tstream_smbXcli_np_use_trans(transp->stream);
+ }
+
+ subreq = tstream_writev_queue_send(state, ev,
+ transp->stream,
+ transp->write_queue,
+ &state->req, 1);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ if (!tevent_req_set_endtime(subreq, ev, endtime)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, rpc_tstream_trans_writev, req);
+
+ subreq = tstream_readv_pdu_queue_send(state, ev,
+ transp->stream,
+ transp->read_queue,
+ rpc_tstream_trans_next_vector,
+ state);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ if (!tevent_req_set_endtime(subreq, ev, endtime)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, rpc_tstream_trans_readv_pdu, req);
+
+ return req;
+}
+
+static void rpc_tstream_trans_writev(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct rpc_tstream_trans_state *state =
+ tevent_req_data(req,
+ struct rpc_tstream_trans_state);
+ int ret;
+ int err;
+
+ ret = tstream_writev_queue_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ rpc_tstream_disconnect(state->transp);
+ tevent_req_nterror(req, map_nt_error_from_unix(err));
+ return;
+ }
+}
+
+static int rpc_tstream_trans_next_vector(struct tstream_context *stream,
+ void *private_data,
+ TALLOC_CTX *mem_ctx,
+ struct iovec **_vector,
+ size_t *count)
+{
+ struct rpc_tstream_trans_state *state =
+ talloc_get_type_abort(private_data,
+ struct rpc_tstream_trans_state);
+ struct iovec *vector;
+
+ if (state->max_rdata_len == state->rep.iov_len) {
+ *_vector = NULL;
+ *count = 0;
+ return 0;
+ }
+
+ state->rep.iov_base = talloc_array(state, uint8_t,
+ state->max_rdata_len);
+ if (state->rep.iov_base == NULL) {
+ return -1;
+ }
+ state->rep.iov_len = state->max_rdata_len;
+
+ vector = talloc_array(mem_ctx, struct iovec, 1);
+ if (!vector) {
+ return -1;
+ }
+
+ vector[0] = state->rep;
+
+ *_vector = vector;
+ *count = 1;
+ return 0;
+}
+
+static void rpc_tstream_trans_readv_pdu(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct rpc_tstream_trans_state *state =
+ tevent_req_data(req,
+ struct rpc_tstream_trans_state);
+ int ret;
+ int err;
+
+ ret = tstream_readv_pdu_queue_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ rpc_tstream_disconnect(state->transp);
+ tevent_req_nterror(req, map_nt_error_from_unix(err));
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static NTSTATUS rpc_tstream_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint8_t **prdata, uint32_t *prdata_len)
+{
+ struct rpc_tstream_trans_state *state =
+ tevent_req_data(req,
+ struct rpc_tstream_trans_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ *prdata = (uint8_t *)talloc_move(mem_ctx, &state->rep.iov_base);
+ *prdata_len = state->rep.iov_len;
+ return NT_STATUS_OK;
+}
+
+/**
+* @brief Initialize a tstream transport facility
+* NOTE: this function will talloc_steal, the stream and the queues.
+*
+* @param mem_ctx - memory context used to allocate the transport
+* @param stream - a ready to use tstream
+* @param presult - the transport structure
+*
+* @return - a NT Status error code.
+*/
+NTSTATUS rpc_transport_tstream_init(TALLOC_CTX *mem_ctx,
+ struct tstream_context **stream,
+ struct rpc_cli_transport **presult)
+{
+ struct rpc_cli_transport *result;
+ struct rpc_tstream_state *state;
+
+ result = talloc(mem_ctx, struct rpc_cli_transport);
+ if (result == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ state = talloc(result, struct rpc_tstream_state);
+ if (state == NULL) {
+ TALLOC_FREE(result);
+ return NT_STATUS_NO_MEMORY;
+ }
+ result->priv = state;
+
+ state->read_queue = tevent_queue_create(state, "read_queue");
+ if (state->read_queue == NULL) {
+ TALLOC_FREE(result);
+ return NT_STATUS_NO_MEMORY;
+ }
+ state->write_queue = tevent_queue_create(state, "write_queue");
+ if (state->write_queue == NULL) {
+ TALLOC_FREE(result);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->stream = talloc_move(state, stream);
+ state->timeout = 10000; /* 10 seconds. */
+
+ if (tstream_is_smbXcli_np(state->stream)) {
+ result->trans_send = rpc_tstream_trans_send;
+ result->trans_recv = rpc_tstream_trans_recv;
+ } else {
+ result->trans_send = NULL;
+ result->trans_recv = NULL;
+ }
+ result->write_send = rpc_tstream_write_send;
+ result->write_recv = rpc_tstream_write_recv;
+ result->read_send = rpc_tstream_read_send;
+ result->read_recv = rpc_tstream_read_recv;
+ result->is_connected = rpc_tstream_is_connected;
+ result->set_timeout = rpc_tstream_set_timeout;
+
+ *presult = result;
+ return NT_STATUS_OK;
+}
+
+struct tstream_context *rpc_transport_get_tstream(
+ struct rpc_cli_transport *transport)
+{
+ struct rpc_tstream_state *state = talloc_get_type_abort(
+ transport->priv, struct rpc_tstream_state);
+ return state->stream;
+}
diff --git a/source3/rpc_client/util_netlogon.c b/source3/rpc_client/util_netlogon.c
new file mode 100644
index 0000000..52bd40b
--- /dev/null
+++ b/source3/rpc_client/util_netlogon.c
@@ -0,0 +1,449 @@
+/*
+ Unix SMB/CIFS implementation.
+ Authentication utility functions
+ Copyright (C) Volker Lendecke 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../librpc/gen_ndr/netlogon.h"
+#include "../libcli/security/security.h"
+#include "rpc_client/util_netlogon.h"
+
+#define COPY_LSA_STRING(mem_ctx, in, out, name) do { \
+ if (in->name.string) { \
+ out->name.string = talloc_strdup(mem_ctx, in->name.string); \
+ NT_STATUS_HAVE_NO_MEMORY(out->name.string); \
+ } \
+} while (0)
+
+NTSTATUS copy_netr_SamBaseInfo(TALLOC_CTX *mem_ctx,
+ const struct netr_SamBaseInfo *in,
+ struct netr_SamBaseInfo *out)
+{
+ /* first copy all, then realloc pointers */
+ *out = *in;
+
+ COPY_LSA_STRING(mem_ctx, in, out, account_name);
+ COPY_LSA_STRING(mem_ctx, in, out, full_name);
+ COPY_LSA_STRING(mem_ctx, in, out, logon_script);
+ COPY_LSA_STRING(mem_ctx, in, out, profile_path);
+ COPY_LSA_STRING(mem_ctx, in, out, home_directory);
+ COPY_LSA_STRING(mem_ctx, in, out, home_drive);
+
+ if (in->groups.count) {
+ out->groups.rids = (struct samr_RidWithAttribute *)
+ talloc_memdup(mem_ctx, in->groups.rids,
+ (sizeof(struct samr_RidWithAttribute) *
+ in->groups.count));
+ NT_STATUS_HAVE_NO_MEMORY(out->groups.rids);
+ }
+
+ COPY_LSA_STRING(mem_ctx, in, out, logon_server);
+ COPY_LSA_STRING(mem_ctx, in, out, logon_domain);
+
+ if (in->domain_sid) {
+ out->domain_sid = dom_sid_dup(mem_ctx, in->domain_sid);
+ NT_STATUS_HAVE_NO_MEMORY(out->domain_sid);
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS copy_netr_SamInfo3(TALLOC_CTX *mem_ctx,
+ const struct netr_SamInfo3 *in,
+ struct netr_SamInfo3 **pout)
+{
+ struct netr_SamInfo3 *info3 = NULL;
+ unsigned int i;
+ NTSTATUS status;
+
+ info3 = talloc_zero(mem_ctx, struct netr_SamInfo3);
+ if (info3 == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ status = copy_netr_SamBaseInfo(info3, &in->base, &info3->base);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ if (in->sidcount) {
+ info3->sidcount = in->sidcount;
+ info3->sids = talloc_array(info3, struct netr_SidAttr,
+ in->sidcount);
+ if (info3->sids == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ for (i = 0; i < in->sidcount; i++) {
+ info3->sids[i].sid = dom_sid_dup(info3->sids,
+ in->sids[i].sid);
+ if (info3->sids[i].sid == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ info3->sids[i].attributes = in->sids[i].attributes;
+ }
+ }
+
+ *pout = info3;
+ info3 = NULL;
+
+ status = NT_STATUS_OK;
+out:
+ TALLOC_FREE(info3);
+ return status;
+}
+
+NTSTATUS map_validation_to_info3(TALLOC_CTX *mem_ctx,
+ uint16_t validation_level,
+ union netr_Validation *validation,
+ struct netr_SamInfo3 **info3_p)
+{
+ struct netr_SamInfo3 *info3 = NULL;
+ struct netr_SamInfo6 *info6 = NULL;
+ NTSTATUS status;
+
+ if (validation == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ switch (validation_level) {
+ case 3:
+ if (validation->sam3 == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = copy_netr_SamInfo3(mem_ctx,
+ validation->sam3,
+ &info3);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ break;
+ case 6:
+ if (validation->sam6 == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ info6 = validation->sam6;
+
+ info3 = talloc_zero(mem_ctx, struct netr_SamInfo3);
+ if (info3 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = copy_netr_SamBaseInfo(info3,
+ &info6->base,
+ &info3->base);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(info3);
+ return status;
+ }
+
+ if (validation->sam6->sidcount > 0) {
+ int i;
+
+ info3->sidcount = info6->sidcount;
+
+ info3->sids = talloc_array(info3,
+ struct netr_SidAttr,
+ info3->sidcount);
+ if (info3->sids == NULL) {
+ TALLOC_FREE(info3);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < info3->sidcount; i++) {
+ info3->sids[i].sid = dom_sid_dup(
+ info3->sids, info6->sids[i].sid);
+ if (info3->sids[i].sid == NULL) {
+ TALLOC_FREE(info3);
+ return NT_STATUS_NO_MEMORY;
+ }
+ info3->sids[i].attributes =
+ info6->sids[i].attributes;
+ }
+ }
+ break;
+ default:
+ return NT_STATUS_BAD_VALIDATION_CLASS;
+ }
+
+ *info3_p = info3;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS copy_netr_SamInfo6(TALLOC_CTX *mem_ctx,
+ const struct netr_SamInfo6 *in,
+ struct netr_SamInfo6 **pout)
+{
+ struct netr_SamInfo6 *info6 = NULL;
+ unsigned int i;
+ NTSTATUS status;
+
+ info6 = talloc_zero(mem_ctx, struct netr_SamInfo6);
+ if (info6 == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ status = copy_netr_SamBaseInfo(info6, &in->base, &info6->base);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ if (in->sidcount) {
+ info6->sidcount = in->sidcount;
+ info6->sids = talloc_array(info6, struct netr_SidAttr,
+ in->sidcount);
+ if (info6->sids == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ for (i = 0; i < in->sidcount; i++) {
+ info6->sids[i].sid = dom_sid_dup(info6->sids,
+ in->sids[i].sid);
+ if (info6->sids[i].sid == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ info6->sids[i].attributes = in->sids[i].attributes;
+ }
+ }
+
+ if (in->dns_domainname.string != NULL) {
+ info6->dns_domainname.string = talloc_strdup(info6,
+ in->dns_domainname.string);
+ if (info6->dns_domainname.string == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ }
+
+ if (in->principal_name.string != NULL) {
+ info6->principal_name.string = talloc_strdup(info6,
+ in->principal_name.string);
+ if (info6->principal_name.string == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ }
+
+ *pout = info6;
+ info6 = NULL;
+
+ status = NT_STATUS_OK;
+out:
+ TALLOC_FREE(info6);
+ return status;
+}
+
+NTSTATUS map_validation_to_info6(TALLOC_CTX *mem_ctx,
+ uint16_t validation_level,
+ union netr_Validation *validation,
+ struct netr_SamInfo6 **info6_p)
+{
+ struct netr_SamInfo3 *info3 = NULL;
+ struct netr_SamInfo6 *info6 = NULL;
+ NTSTATUS status;
+
+ if (validation == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ switch (validation_level) {
+ case 3:
+ if (validation->sam3 == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ info3 = validation->sam3;
+
+ info6 = talloc_zero(mem_ctx, struct netr_SamInfo6);
+ if (info6 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = copy_netr_SamBaseInfo(info6,
+ &info3->base,
+ &info6->base);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(info6);
+ return status;
+ }
+
+ if (validation->sam3->sidcount > 0) {
+ int i;
+
+ info6->sidcount = info3->sidcount;
+
+ info6->sids = talloc_array(info6,
+ struct netr_SidAttr,
+ info6->sidcount);
+ if (info6->sids == NULL) {
+ TALLOC_FREE(info6);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < info6->sidcount; i++) {
+ info6->sids[i].sid = dom_sid_dup(
+ info6->sids, info3->sids[i].sid);
+ if (info6->sids[i].sid == NULL) {
+ TALLOC_FREE(info6);
+ return NT_STATUS_NO_MEMORY;
+ }
+ info6->sids[i].attributes =
+ info3->sids[i].attributes;
+ }
+ }
+ break;
+ case 6:
+ if (validation->sam6 == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = copy_netr_SamInfo6(mem_ctx,
+ validation->sam6,
+ &info6);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ break;
+ default:
+ return NT_STATUS_BAD_VALIDATION_CLASS;
+ }
+
+ *info6_p = info6;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS map_info3_to_validation(TALLOC_CTX *mem_ctx,
+ struct netr_SamInfo3 *info3,
+ uint16_t *_validation_level,
+ union netr_Validation **_validation)
+{
+ union netr_Validation *validation = NULL;
+ NTSTATUS status;
+
+ validation = talloc_zero(mem_ctx, union netr_Validation);
+ if (validation == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = copy_netr_SamInfo3(mem_ctx,
+ info3,
+ &validation->sam3);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(validation);
+ return status;
+ }
+
+ * _validation_level = 3;
+ *_validation = validation;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS map_info6_to_validation(TALLOC_CTX *mem_ctx,
+ const struct netr_SamInfo6 *info6,
+ uint16_t *_validation_level,
+ union netr_Validation **_validation)
+{
+ union netr_Validation *validation = NULL;
+ NTSTATUS status;
+
+ validation = talloc_zero(mem_ctx, union netr_Validation);
+ if (validation == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = copy_netr_SamInfo6(validation,
+ info6,
+ &validation->sam6);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(validation);
+ return status;
+ }
+
+ * _validation_level = 6;
+ *_validation = validation;
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS copy_netr_DsRGetDCNameInfo(TALLOC_CTX *mem_ctx,
+ const struct netr_DsRGetDCNameInfo *in,
+ struct netr_DsRGetDCNameInfo **pout)
+{
+ struct netr_DsRGetDCNameInfo *r;
+
+ r = talloc_zero(mem_ctx, struct netr_DsRGetDCNameInfo);
+ if (r == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r->dc_unc = talloc_strdup(r, in->dc_unc);
+ if (r->dc_unc == NULL) {
+ talloc_free(r);
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->dc_address = talloc_strdup(r, in->dc_address);
+ if (r->dc_address == NULL) {
+ talloc_free(r);
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->dc_address_type = in->dc_address_type;
+ r->domain_guid = in->domain_guid;
+ r->domain_name = talloc_strdup(r, in->domain_name);
+ if (r->domain_name == NULL) {
+ talloc_free(r);
+ return NT_STATUS_NO_MEMORY;
+ }
+ /* forest could be empty */
+ if (in->forest_name != NULL) {
+ r->forest_name = talloc_strdup(r, in->forest_name);
+ if (r->forest_name == NULL) {
+ talloc_free(r);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ r->dc_flags = in->dc_flags;
+ if (in->dc_site_name != NULL) {
+ r->dc_site_name = talloc_strdup(r, in->dc_site_name);
+ if (r->dc_site_name == NULL) {
+ talloc_free(r);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ if (in->client_site_name != NULL) {
+ r->client_site_name = talloc_strdup(r, in->client_site_name);
+ if (r->client_site_name == NULL) {
+ talloc_free(r);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ *pout = r;
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/rpc_client/util_netlogon.h b/source3/rpc_client/util_netlogon.h
new file mode 100644
index 0000000..85680a9
--- /dev/null
+++ b/source3/rpc_client/util_netlogon.h
@@ -0,0 +1,54 @@
+/*
+ Unix SMB/CIFS implementation.
+ Authentication utility functions
+ Copyright (C) Volker Lendecke 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _RPC_CLIENT_UTIL_NETLOGON_H_
+#define _RPC_CLIENT_UTIL_NETLOGON_H_
+
+/* The following definitions come from rpc_client/util_netlogon.c */
+
+NTSTATUS copy_netr_SamBaseInfo(TALLOC_CTX *mem_ctx,
+ const struct netr_SamBaseInfo *in,
+ struct netr_SamBaseInfo *out);
+NTSTATUS copy_netr_SamInfo3(TALLOC_CTX *mem_ctx,
+ const struct netr_SamInfo3 *in,
+ struct netr_SamInfo3 **pout);
+NTSTATUS map_validation_to_info3(TALLOC_CTX *mem_ctx,
+ uint16_t validation_level,
+ union netr_Validation *validation,
+ struct netr_SamInfo3 **info3_p);
+NTSTATUS copy_netr_SamInfo6(TALLOC_CTX *mem_ctx,
+ const struct netr_SamInfo6 *in,
+ struct netr_SamInfo6 **pout);
+NTSTATUS map_validation_to_info6(TALLOC_CTX *mem_ctx,
+ uint16_t validation_level,
+ union netr_Validation *validation,
+ struct netr_SamInfo6 **info6_p);
+NTSTATUS map_info3_to_validation(TALLOC_CTX *mem_ctx,
+ struct netr_SamInfo3 *info3,
+ uint16_t *_validation_level,
+ union netr_Validation **_validation);
+NTSTATUS map_info6_to_validation(TALLOC_CTX *mem_ctx,
+ const struct netr_SamInfo6 *info6,
+ uint16_t *_validation_level,
+ union netr_Validation **_validation);
+NTSTATUS copy_netr_DsRGetDCNameInfo(TALLOC_CTX *mem_ctx,
+ const struct netr_DsRGetDCNameInfo *in,
+ struct netr_DsRGetDCNameInfo **pout);
+
+#endif /* _RPC_CLIENT_UTIL_NETLOGON_H_ */
diff --git a/source3/rpc_client/wsp_cli.c b/source3/rpc_client/wsp_cli.c
new file mode 100644
index 0000000..15b6e36
--- /dev/null
+++ b/source3/rpc_client/wsp_cli.c
@@ -0,0 +1,2221 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Window Search Service
+ *
+ * Copyright (c) Noel Power
+ *
+ * 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 "client.h"
+#include "rpc_client/wsp_cli.h"
+#include "rpc_client/rpc_client.h"
+#include "param/param.h"
+#include "auth/credentials/credentials.h"
+#include <tevent.h>
+#include <util/tevent_ntstatus.h>
+#include "libcli/tstream_binding_handle/tstream_binding_handle.h"
+#include "lib/tsocket/tsocket.h"
+#include "librpc/wsp/wsp_util.h"
+#include "librpc/gen_ndr/ndr_wsp.h"
+#include "rpc_client/cli_pipe.h"
+#include "libcli/smb/smbXcli_base.h"
+
+#define MSG_HDR_SIZE 16
+#define USED 1
+/*
+ * 32-bit Windows XP operating system, 32-bit Windows Server 2003 operating
+ * system, 32-bit Windows Home Server server software, 32-bit Windows Vista
+ * with Windows Search 4.0, 32-bit Windows Server 2003 with Windows
+ * Search 4.0. All of these versions of Windows are running
+ * Windows Search 4.0.
+*/
+
+static const uint32_t CLIENTVERSION = 0x00010700;
+
+/*
+ * DBPROP_CI_SCOPE_FLAGS
+ * containing QUERY_DEEP
+ * QUERY_DEEP (0x1) indicates that files in the scope directory and all
+ * subdirectories are included in the results. If clear, only files in
+ * the scope directory are included in the results.
+ */
+static int32_t scope_flags_vector[] = {0x00000001};
+/*
+ * Search everywhere "\\" is the root scope
+ */
+static const char * root_scope_string_vector[] = {"\\"};
+
+/* sets sensible defaults */
+static void init_wsp_prop(struct wsp_cdbprop *prop)
+{
+ *prop = (struct wsp_cdbprop){0};
+ prop->colid.ekind = DBKIND_GUID_PROPID;
+}
+
+
+static bool create_restriction_array(TALLOC_CTX *ctx,
+ struct wsp_crestriction **pelements,
+ uint32_t nnodes)
+{
+ struct wsp_crestriction *elements = talloc_zero_array(ctx,
+ struct wsp_crestriction,
+ nnodes);
+ if (elements == NULL) {
+ return false;
+ }
+ *pelements = elements;
+ return true;
+}
+
+
+static bool create_noderestriction(TALLOC_CTX *ctx,
+ struct wsp_cnoderestriction *pnode,
+ uint32_t nnodes)
+{
+ bool ok;
+ pnode->cnode = nnodes;
+ ok = create_restriction_array(ctx, &pnode->panode, nnodes);
+ return ok;
+}
+
+static bool fill_sortarray(TALLOC_CTX *ctx, struct wsp_csort **dest,
+ struct wsp_csort *src, uint32_t num)
+{
+ uint32_t i;
+ struct wsp_csort *psort = talloc_zero_array(ctx, struct wsp_csort,
+ num);
+ if (psort == NULL) {
+ return false;
+ }
+ for (i = 0; i < num; i++) {
+ psort[i] = src[i];
+ }
+ *dest = psort;
+ return true;
+}
+
+
+
+static bool set_fullpropspec(TALLOC_CTX *ctx, struct wsp_cfullpropspec *prop,
+ const char* propname, uint32_t kind)
+{
+ struct GUID guid = {0};
+ const struct full_propset_info *prop_info = NULL;
+
+ prop_info = get_propset_info_with_guid(propname, &guid);
+ if (!prop_info) {
+ DBG_ERR("Failed to handle property named %s\n",
+ propname);
+ return false;
+ }
+ prop->guidpropset = guid;
+ prop->ulkind = kind;
+ if (kind == PRSPEC_LPWSTR) {
+ prop->name_or_id.propname.vstring = talloc_strdup(ctx,
+ propname);
+ if (prop->name_or_id.propname.vstring == NULL) {
+ DBG_ERR("out of memory");
+ return false;
+ }
+ prop->name_or_id.propname.len = strlen(propname);
+ } else {
+ prop->name_or_id.prspec = prop_info->id;
+ }
+ return true;
+}
+
+struct binding
+{
+ uint32_t status_off;
+ uint32_t value_off;
+ uint32_t len_off;
+};
+
+static bool set_ctablecolumn(TALLOC_CTX *ctx, struct wsp_ctablecolumn *tablecol,
+ const char* propname, struct binding *offsets,
+ uint32_t value_size)
+{
+ struct wsp_cfullpropspec *prop = &tablecol->propspec;
+
+ if (!set_fullpropspec(ctx, prop, propname, PRSPEC_PROPID)) {
+ return false;
+ }
+ tablecol->vtype =VT_VARIANT ;
+ tablecol->aggregateused = USED;
+ tablecol->valueused = USED;
+ tablecol->valueoffset.value = offsets->value_off;
+ tablecol->valuesize.value = value_size;
+ tablecol->statusused = USED;
+ tablecol->statusoffset.value = offsets->status_off;
+ tablecol->lengthused = USED;
+ tablecol->lengthoffset.value = offsets->len_off;
+ return true;
+}
+
+
+static bool fill_uint32_vec(TALLOC_CTX* ctx,
+ uint32_t **pdest,
+ uint32_t* ivector, uint32_t elems)
+{
+ uint32_t i;
+ uint32_t *dest = talloc_zero_array(ctx, uint32_t, elems);
+ if (dest == NULL) {
+ return false;
+ }
+
+ for ( i = 0; i < elems; i++ ) {
+ dest[ i ] = ivector[ i ];
+ }
+ *pdest = dest;
+ return true;
+}
+
+static bool init_propset1(TALLOC_CTX* tmp_ctx,
+ struct wsp_cdbpropset *propertyset)
+{
+ uint32_t i;
+ GUID_from_string(DBPROPSET_FSCIFRMWRK_EXT,
+ &propertyset->guidpropertyset);
+
+ propertyset->cproperties = 4;
+ propertyset->aprops =
+ talloc_zero_array(tmp_ctx, struct wsp_cdbprop,
+ propertyset->cproperties);
+ if (propertyset->aprops == NULL) {
+ return false;
+ }
+
+ /* initialise first 4 props */
+ for( i = 0; i < propertyset->cproperties; i++) {
+ init_wsp_prop(&propertyset->aprops[i]);
+ }
+
+ /*
+ * see MS-WSP 2.2.1.31.1 & 4.1 Protocol examples, Example 1
+ * and also as seen in various windows network traces
+ * set value prop[0] - 'catalog to search'
+ */
+
+ propertyset->aprops[0].dbpropid = DBPROP_CI_CATALOG_NAME;
+ /* The name of the Catalog to Query */
+ set_variant_lpwstr(tmp_ctx, &propertyset->aprops[0].vvalue,
+ "Windows\\SystemIndex");
+ /*
+ * set value prop[1] 'Regular Query'
+ */
+
+ propertyset->aprops[1].dbpropid = DBPROP_CI_QUERY_TYPE;
+ set_variant_i4(tmp_ctx, &propertyset->aprops[1].vvalue,
+ CINORMAL);
+
+ /*
+ * set value prop[2] 'search subfolders'
+ */
+ propertyset->aprops[2].dbpropid = DBPROP_CI_SCOPE_FLAGS;
+ set_variant_i4_vector(tmp_ctx, &propertyset->aprops[2].vvalue,
+ scope_flags_vector, ARRAY_SIZE(scope_flags_vector));
+
+ /*
+ * set value prop[3] 'root scope'
+ */
+ propertyset->aprops[3].dbpropid = DBPROP_CI_INCLUDE_SCOPES;
+ set_variant_lpwstr_vector(tmp_ctx,
+ &propertyset->aprops[3].vvalue,
+ root_scope_string_vector,
+ ARRAY_SIZE(root_scope_string_vector));
+ return true;
+}
+
+static bool init_propset2(TALLOC_CTX* tmp_ctx,
+ struct wsp_cdbpropset *propertyset,
+ const char* server)
+{
+ uint32_t i;
+
+ GUID_from_string(DBPROPSET_CIFRMWRKCORE_EXT,
+ &propertyset->guidpropertyset);
+
+ propertyset->cproperties = 1;
+ propertyset->aprops =
+ talloc_zero_array(tmp_ctx, struct wsp_cdbprop,
+ propertyset->cproperties);
+ if (propertyset->aprops == NULL) {
+ return false;
+ }
+
+ /* initialise first 1 props */
+ for( i = 0; i < propertyset->cproperties; i++) {
+ init_wsp_prop(&propertyset->aprops[i]);
+ }
+
+ /*
+ * see MS-WSP 2.2.1.31.1 & 4.1 Protocol examples, Example 1
+ * and also as seen in various windows network traces
+ * set value prop[0] - 'machines to search'
+ */
+ propertyset->aprops[0].dbpropid = DBPROP_MACHINE;
+ set_variant_bstr(tmp_ctx, &propertyset->aprops[0].vvalue,
+ server);
+ return true;
+}
+
+static bool init_apropset0(TALLOC_CTX* tmp_ctx,
+ struct wsp_cdbpropset *propertyset)
+{
+ uint32_t i;
+
+ GUID_from_string(DBPROPSET_MSIDXS_ROWSETEXT,
+ &propertyset->guidpropertyset);
+
+ propertyset->cproperties = 7;
+ propertyset->aprops =
+ talloc_zero_array(tmp_ctx, struct wsp_cdbprop,
+ propertyset->cproperties);
+ if (propertyset->aprops == NULL) {
+ return false;
+ }
+
+ /* initialise props */
+ for( i = 0; i < propertyset->cproperties; i++) {
+ init_wsp_prop(&propertyset->aprops[i]);
+ }
+
+ /*
+ * see MS-WSP 2.2.1.31.1 & 4.1 Protocol examples, Example 1
+ * set value prop[0]
+ * MSIDXSPROP_ROWSETQUERYSTATUS - 'ignored'
+ */
+ propertyset->aprops[0].dbpropid = MSIDXSPROP_ROWSETQUERYSTATUS;
+ set_variant_i4(tmp_ctx, &propertyset->aprops[0].vvalue, 0x00000000);
+
+ /*
+ * set value prop[1]
+ * MSIDXSPROP_COMMAND_LOCALE_STRING - 'EN'
+ */
+ propertyset->aprops[1].dbpropid = MSIDXSPROP_COMMAND_LOCALE_STRING;
+ set_variant_bstr(tmp_ctx, &propertyset->aprops[1].vvalue,
+ "en-us");
+
+ /*
+ * set value prop[2]
+ * MSIDXSPROP_QUERY_RESTRICTION - 'ignored'
+ */
+ propertyset->aprops[2].dbpropid = MSIDXSPROP_QUERY_RESTRICTION;
+ set_variant_bstr(tmp_ctx, &propertyset->aprops[2].vvalue,
+ "");
+
+ /*
+ * set value prop[3]
+ * MSIDXSPROP_PARSE_TREE - 'ignored'
+ */
+ propertyset->aprops[3].dbpropid = MSIDXSPROP_PARSE_TREE;
+ set_variant_bstr(tmp_ctx, &propertyset->aprops[3].vvalue,
+ "");
+
+ /*
+ * set value prop[4]
+ * MSIDXSPROP_MAX_RANK - 'ignored'
+ */
+ propertyset->aprops[4].dbpropid = MSIDXSPROP_MAX_RANK;
+ set_variant_i4(tmp_ctx, &propertyset->aprops[4].vvalue, 0x00000000);
+
+ /*
+ * set value prop[5]
+ * MSIDXSPROP_RESULTS_FOUND - 'ignored'
+ */
+ propertyset->aprops[5].dbpropid = MSIDXSPROP_RESULTS_FOUND;
+ set_variant_i4(tmp_ctx, &propertyset->aprops[5].vvalue, 0x00000000);
+
+ /*
+ * set value prop[6]
+ * ? - '' (unknown property id)
+ */
+ propertyset->aprops[6].dbpropid = 0x00000008;
+ set_variant_i4(tmp_ctx, &propertyset->aprops[6].vvalue, 0x00000000);
+ return true;
+}
+
+static bool init_apropset1(TALLOC_CTX* tmp_ctx,
+ struct wsp_cdbpropset *propertyset)
+{
+ uint32_t i;
+ GUID_from_string(DBPROPSET_QUERYEXT,
+ &propertyset->guidpropertyset);
+
+ propertyset->cproperties = 11;
+ propertyset->aprops =
+ talloc_zero_array(tmp_ctx, struct wsp_cdbprop,
+ propertyset->cproperties);
+ if (propertyset->aprops == NULL) {
+ return false;
+ }
+
+ /* init properties */
+ for( i = 0; i < propertyset->cproperties; i++) {
+ init_wsp_prop(&propertyset->aprops[i]);
+ }
+
+ /*
+ * see MS-WSP 2.2.1.31.1 & 4.1 Protocol examples, Example 1
+ * set value prop[0]
+ * DBPROP_USECONTENTINDEX - 'forced use of the full text index
+ * is false.'
+ */
+ propertyset->aprops[0].dbpropid = DBPROP_USECONTENTINDEX;
+ set_variant_vt_bool(tmp_ctx, &propertyset->aprops[0].vvalue, false);
+
+ /*
+ * set value prop[1]
+ * DBPROP_DEFERNONINDEXEDTRIMMING - 'trimming of security
+ * results will not be deferred'
+ */
+ propertyset->aprops[1].dbpropid = DBPROP_DEFERNONINDEXEDTRIMMING;
+ set_variant_vt_bool(tmp_ctx, &propertyset->aprops[1].vvalue, false);
+
+ /*
+ * set value prop[2]
+ * DBPROP_USEEXTENDEDDBTYPES - 'extended DB types are not used'
+ */
+ propertyset->aprops[2].dbpropid = DBPROP_USEEXTENDEDDBTYPES;
+ set_variant_vt_bool(tmp_ctx, &propertyset->aprops[2].vvalue, false);
+
+ /*
+ * set value prop[3]
+ * DBPROP_IGNORENOISEONLYCLAUSES = 'full text clauses consisting
+ * entirely of noise words will
+ * result in an error being returned'
+ */
+ propertyset->aprops[3].dbpropid = DBPROP_IGNORENOISEONLYCLAUSES;
+ set_variant_vt_bool(tmp_ctx, &propertyset->aprops[3].vvalue, false);
+
+ /*
+ * set value prop[4]
+ * DBPROP_GENERICOPTIONS_STRING - 'no generic options set'
+ */
+ propertyset->aprops[4].dbpropid = DBPROP_GENERICOPTIONS_STRING;
+ set_variant_bstr(tmp_ctx, &propertyset->aprops[4].vvalue, "");
+
+ /*
+ * set value prop[5]
+ * DBPROP_DEFERCATALOGVERIFICATION - 'catalog verification is not
+ * deferred.'
+ */
+ propertyset->aprops[5].dbpropid = DBPROP_DEFERCATALOGVERIFICATION;
+ set_variant_vt_bool(tmp_ctx, &propertyset->aprops[5].vvalue, false);
+
+ /*
+ * set value prop[6]
+ * DBPROP_IGNORESBRI - 'query can use the sort-by-rank index
+ * optimization'
+ */
+ propertyset->aprops[6].dbpropid = DBPROP_IGNORESBRI;
+ set_variant_vt_bool(tmp_ctx, &propertyset->aprops[6].vvalue, false);
+
+ /*
+ * set value prop[7]
+ * DBPROP_GENERATEPARSETREE - 'a parse tree is not generated for
+ * debugging.'
+ */
+ propertyset->aprops[7].dbpropid = DBPROP_GENERATEPARSETREE;
+ set_variant_vt_bool(tmp_ctx, &propertyset->aprops[7].vvalue, false);
+
+ /*
+ * set value prop[8]
+ * DBPROP_FREETEXTANYTERM - 'all terms from a FREETEXT clause
+ * appear in every matching document'
+ */
+ propertyset->aprops[8].dbpropid = DBPROP_FREETEXTANYTERM;
+ set_variant_vt_bool(tmp_ctx, &propertyset->aprops[8].vvalue, false);
+ /*
+ * set value prop[9]
+ * DBPROP_FREETEXTUSESTEMMING - 'stemming is not used when interpreting
+ * a FREETEXT clause'
+ */
+ propertyset->aprops[9].dbpropid = DBPROP_FREETEXTUSESTEMMING;
+ set_variant_vt_bool(tmp_ctx, &propertyset->aprops[9].vvalue, false);
+
+ /*
+ * set value prop[10]
+ * ? - ''
+ */
+ propertyset->aprops[10].dbpropid = 0x0000000f; /* ??? */
+ set_variant_vt_bool(tmp_ctx, &propertyset->aprops[10].vvalue, false);
+ return true;
+}
+
+static bool init_apropset2(TALLOC_CTX* tmp_ctx,
+ struct wsp_cdbpropset *propertyset,
+ const char* server)
+{
+ uint32_t i;
+ GUID_from_string(DBPROPSET_CIFRMWRKCORE_EXT,
+ &propertyset->guidpropertyset);
+
+ propertyset->cproperties = 1;
+ propertyset->aprops =
+ talloc_zero_array(tmp_ctx, struct wsp_cdbprop,
+ propertyset->cproperties);
+ if (propertyset->aprops == NULL) {
+ return false;
+ }
+
+ /* init properties */
+ for( i = 0; i < propertyset->cproperties; i++) {
+ init_wsp_prop(&propertyset->aprops[i]);
+ }
+
+ /*
+ * see MS-WSP 2.2.1.31.1 & 4.1 Protocol examples, Example 1
+ * and also as seen in various windows network traces
+ * set value prop[0]
+ * DBPROP_MACHINE - 'target server'
+ */
+ propertyset->aprops[0].dbpropid = DBPROP_MACHINE;
+ set_variant_bstr(tmp_ctx, &propertyset->aprops[0].vvalue, server);
+ return true;
+}
+
+
+static bool init_apropset3(TALLOC_CTX* tmp_ctx,
+ struct wsp_cdbpropset *propertyset)
+{
+ uint32_t i;
+
+ GUID_from_string(DBPROPSET_FSCIFRMWRK_EXT,
+ &propertyset->guidpropertyset);
+
+ propertyset->cproperties = 3;
+ propertyset->aprops =
+ talloc_zero_array(tmp_ctx, struct wsp_cdbprop,
+ propertyset->cproperties);
+ if (propertyset->aprops == NULL) {
+ return false;
+ }
+
+ /* init properties */
+ for( i = 0; i < propertyset->cproperties; i++) {
+ init_wsp_prop(&propertyset->aprops[i]);
+ }
+
+ /*
+ * see MS-WSP 2.2.1.31.1 & 4.1 Protocol examples, Example 1
+ * and also as seen in various windows network traces
+ * set value prop[0]
+ * DBPROP_CI_INCLUDE_SCOPES - 'search everywhere'
+ */
+ propertyset->aprops[0].dbpropid = DBPROP_CI_INCLUDE_SCOPES;
+ set_variant_array_bstr(tmp_ctx, &propertyset->aprops[0].vvalue,
+ root_scope_string_vector,
+ ARRAY_SIZE(root_scope_string_vector));
+
+ /*
+ * set value prop[1]
+ * DBPROP_CI_SCOPE_FLAGS - 'QUERY_DEEP'
+ */
+ propertyset->aprops[1].dbpropid = DBPROP_CI_SCOPE_FLAGS;
+ set_variant_array_i4(tmp_ctx, &propertyset->aprops[1].vvalue,
+ scope_flags_vector,
+ ARRAY_SIZE(scope_flags_vector));
+
+ /*
+ * set value prop[2]
+ * DBPROP_CI_CATALOG_NAME - 'index to use' (always the same)
+ */
+ propertyset->aprops[2].dbpropid = DBPROP_CI_CATALOG_NAME;
+ set_variant_bstr(tmp_ctx, &propertyset->aprops[2].vvalue,
+ "Windows\\SystemIndex");
+ return true;
+}
+
+bool init_connectin_request(TALLOC_CTX *ctx,
+ struct wsp_request* request,
+ const char* clientmachine,
+ const char* clientuser,
+ const char* server)
+{
+ enum ndr_err_code err;
+ struct connectin_propsets *props = NULL;
+ struct connectin_extpropsets *ext_props = NULL;
+ DATA_BLOB props_blob = data_blob_null;
+ struct ndr_push *ndr_props = NULL;
+ ndr_flags_type ndr_flags = NDR_SCALARS | NDR_BUFFERS;
+ bool result;
+ struct wsp_cpmconnectin *connectin =
+ &request->message.cpmconnect;
+
+ props = talloc_zero(ctx, struct connectin_propsets);
+ if (props == NULL) {
+ result = false;
+ DBG_ERR("out of memory\n");
+ goto out;
+ }
+
+ ext_props = talloc_zero(ctx, struct connectin_extpropsets) ;
+ if (ext_props == NULL) {
+ result = false;
+ DBG_ERR("out of memory\n");
+ goto out;
+ }
+
+ request->header.msg = CPMCONNECT;
+ connectin->iclientversion = CLIENTVERSION;
+ /*
+ * hmm just say the client is remote, if we
+ * are talking to windows it is, if not does
+ * it really matter?
+ */
+ connectin->fclientisremote = 0x00000001;
+ connectin->machinename = clientmachine;
+ connectin->username = clientuser;
+ props->cpropsets = 2;
+
+ /* =================== */
+ /* set up PropertySet1 */
+ /* =================== */
+ if (!init_propset1(ctx, &props->propertyset1)) {
+ result = false;
+ DBG_ERR("initialising propset1 failed\n");
+ goto out;
+ }
+
+ /* =================== */
+ /* set up PropertySet2 */
+ /* =================== */
+ if (!init_propset2(ctx, &props->propertyset2, server)) {
+ result = false;
+ DBG_ERR("initialising propset2 failed\n");
+ goto out;
+ }
+
+ /* 4 ExtPropSets */
+ ext_props->cextpropset = 4;
+ ext_props->apropertysets = talloc_zero_array(ctx, struct wsp_cdbpropset,
+ ext_props->cextpropset);
+
+ if (ext_props->apropertysets == NULL) {
+ result = false;
+ DBG_ERR("out of memory\n");
+ goto out;
+ }
+
+ /* ======================= */
+ /* set up aPropertySets[0] */
+ /* ======================= */
+ if (!init_apropset0(ctx, &ext_props->apropertysets[0])) {
+ result = false;
+ DBG_ERR("initialisation of apropset0 failed\n");
+ goto out;
+ }
+
+ /* ======================= */
+ /* set up aPropertySets[1] */
+ /* ======================= */
+ if (!init_apropset1(ctx, &ext_props->apropertysets[1])) {
+ result = false;
+ DBG_ERR("initialisation of apropset1 failed\n");
+ goto out;
+ }
+
+ /* ======================= */
+ /* set up aPropertySets[2] */
+ /* ======================= */
+ if (!init_apropset2(ctx, &ext_props->apropertysets[2], server)) {
+ result = false;
+ DBG_ERR("initialisation of apropset2 failed\n");
+ goto out;
+ }
+
+ /* ======================= */
+ /* set up aPropertySets[3] */
+ /* ======================= */
+ if (!init_apropset3(ctx, &ext_props->apropertysets[3])) {
+ result = false;
+ DBG_ERR("initialisation of apropset3 failed\n");
+ goto out;
+ }
+
+ /* we also have to fill the opaque blobs that contain the propsets */
+ ndr_props = ndr_push_init_ctx(ctx);
+ if (ndr_props == NULL) {
+ result = false;
+ DBG_ERR("out of memory\n");
+ goto out;
+ }
+
+ /* first connectin_propsets */
+ err = ndr_push_connectin_propsets(ndr_props, ndr_flags, props);
+ if (err) {
+ DBG_ERR("Failed to push propset, error %d\n", err);
+ result = false;
+ goto out;
+ }
+ props_blob = ndr_push_blob(ndr_props);
+ connectin->cbblob1 = props_blob.length;
+ connectin->propsets = talloc_zero_array(ctx, uint8_t,
+ connectin->cbblob1);
+ if (connectin->propsets == NULL) {
+ result = false;
+ DBG_ERR("out of memory\n");
+ goto out;
+ }
+
+ memcpy(connectin->propsets, props_blob.data, props_blob.length);
+
+ /* then connectin_extpropsets */
+ TALLOC_FREE(ndr_props);
+ ndr_props = ndr_push_init_ctx(ctx);
+
+ if (ndr_props == NULL) {
+ result = false;
+ DBG_ERR("out of memory\n");
+ goto out;
+ }
+
+ err = ndr_push_connectin_extpropsets(ndr_props, ndr_flags, ext_props);
+
+ if (err) {
+ DBG_ERR("Failed to push extpropset, error %d\n", err);
+ result = false;
+ goto out;
+ }
+
+ props_blob = ndr_push_blob(ndr_props);
+ connectin->cbblob2 = props_blob.length;
+ connectin->extpropsets = talloc_zero_array(ctx, uint8_t,
+ connectin->cbblob2);
+
+ if (connectin->extpropsets == NULL) {
+ result = false;
+ DBG_ERR("out of memory\n");
+ goto out;
+ }
+
+ memcpy(connectin->extpropsets, props_blob.data, props_blob.length);
+ TALLOC_FREE(ndr_props);
+ result = true;
+out:
+ return result;
+}
+
+void create_seekat_getrows_request(TALLOC_CTX * ctx,
+ struct wsp_request* request,
+ uint32_t cursor,
+ uint32_t bookmark,
+ uint32_t skip,
+ uint32_t rows,
+ uint32_t cbreserved,
+ uint32_t ulclientbase,
+ uint32_t cbrowwidth,
+ uint32_t fbwdfetch)
+{
+ struct wsp_cpmgetrowsin *getrows =
+ &request->message.cpmgetrows;
+ /* msg type */
+ request->header.msg = CPMGETROWS;
+ /* position */
+ getrows->hcursor = cursor;
+ /* max no. rows to receive */
+ getrows->crowstotransfer = rows;
+ /*
+ * size (length) of row in bytes, determined from value set
+ * by CPMSetBindings message
+ */
+ getrows->cbrowWidth = cbrowwidth;
+ /*
+ * according to we should calculate this (see MS-WSP 3.2.4.2.4)
+ * but it seems window always sets this to the max 16KB limit
+ * (most likely when any row value is variable size e.g. like a
+ * string/path)
+ */
+ getrows->cbreadbuffer = 0x00004000;
+ /*
+ * base value of buffer pointer
+ */
+ getrows->ulclientbase = ulclientbase;
+ getrows->cbreserved = cbreserved;
+ /* fetch rows in forward order */
+ getrows->fbwdfetch = fbwdfetch;
+ /* eRowSeekAt */
+ getrows->etype = EROWSEEKAT;
+ /* we don't handle chapters */
+ getrows->chapt = 0;
+ /* CRowsSeekAt (MS-WSP 2.2.1.37) */
+ getrows->seekdescription.crowseekat.bmkoffset = bookmark;
+ getrows->seekdescription.crowseekat.cskip = skip;
+ getrows->seekdescription.crowseekat.hregion = 0;
+}
+
+static bool extract_rowbuf_variable_type(TALLOC_CTX *ctx,
+ uint16_t type,
+ uint64_t offset,
+ DATA_BLOB *rows_buf, uint32_t len,
+ struct wsp_cbasestoragevariant *val)
+{
+ enum ndr_err_code err;
+ struct ndr_pull *ndr_pull = NULL;
+ ndr_flags_type ndr_flags = NDR_SCALARS | NDR_BUFFERS;
+ DATA_BLOB variant_blob = data_blob_null;
+ if (offset >= rows_buf->length) {
+ DBG_ERR("offset %"PRIu64" outside buffer range (buf len - %zu)",
+ offset,
+ rows_buf->length);
+ return false;
+ }
+ variant_blob.data = rows_buf->data + offset;
+ variant_blob.length = len;
+ ndr_pull = ndr_pull_init_blob(&variant_blob, ctx);
+
+ if (ndr_pull == NULL) {
+ DBG_ERR("out of memory\n");
+ return false;
+ }
+
+ switch (type) {
+ case VT_LPWSTR: {
+ const char *string = NULL;
+ ndr_set_flags(&ndr_pull->flags, LIBNDR_FLAG_STR_NULLTERM);
+ err = ndr_pull_string(ndr_pull, ndr_flags, &string);
+ if (err) {
+ DBG_ERR("error unmarshalling string from %p\n", variant_blob.data );
+ } else {
+ DBG_INFO("\tstring val ->%s<-\n", string );
+ val->vtype = type;
+ val->vvalue.vt_lpwstr.value = string;
+ }
+ break;
+ }
+ default:
+ DBG_ERR("#FIXME Unhandled variant type %s\n", get_vtype_name(type));
+ break;
+ }
+ return true;
+}
+
+static bool convert_variant_array_to_vector(TALLOC_CTX *ctx,
+ uint64_t count,
+ struct wsp_cbasestoragevariant **variant_array,
+ struct wsp_cbasestoragevariant *outval)
+{
+ uint64_t i;
+ uint16_t vtype;
+ union variant_types vvalue = {0};
+ vtype = variant_array[0]->vtype;
+
+ if (outval == NULL) {
+ return false;
+ }
+
+ if (count) {
+ switch (vtype) {
+ case VT_BSTR:
+ vvalue.vt_bstr_v.vvector_elements = count;
+ vvalue.vt_bstr_v.vvector_data =
+ talloc_zero_array(ctx,
+ struct vt_bstr, count);
+ if (vvalue.vt_bstr_v.vvector_data == NULL) {
+ return false;
+ }
+ break;
+ case VT_LPWSTR:
+ vvalue.vt_lpwstr_v.vvector_elements = count;
+ vvalue.vt_lpwstr_v.vvector_data =
+ talloc_zero_array(ctx,
+ struct vt_lpwstr, count);
+ if (vvalue.vt_lpwstr_v.vvector_data == NULL) {
+ return false;
+ }
+ break;
+ case VT_COMPRESSED_LPWSTR:
+ vvalue.vt_compresseed_lpwstr_v.vvector_elements
+ = count;
+ vvalue.vt_compresseed_lpwstr_v.vvector_data =
+ talloc_zero_array(ctx,
+ struct vt_compressed_lpwstr,
+ count);
+ if (vvalue.vt_compresseed_lpwstr_v.vvector_data == NULL) {
+ return false;
+ }
+ break;
+ default:
+ DBG_ERR("Can't convert array of %s to VECTOR\n",
+ get_vtype_name(vtype));
+ return false;
+ }
+ }
+ for (i = 0; i < count; i++) {
+ if (variant_array[i]->vtype != vtype) {
+ DBG_ERR("array item type %s doesn't match extpected "
+ "type %s\n",
+ get_vtype_name(variant_array[i]->vtype),
+ get_vtype_name(vtype));
+ return false;
+ }
+ switch (variant_array[i]->vtype) {
+ case VT_BSTR:
+ vvalue.vt_bstr_v.vvector_data[i]
+ = variant_array[i]->vvalue.vt_bstr;
+ break;
+ case VT_LPWSTR:
+ vvalue.vt_lpwstr_v.vvector_data[i]
+ = variant_array[i]->vvalue.vt_lpwstr;
+ break;
+ case VT_COMPRESSED_LPWSTR:
+ vvalue.vt_compresseed_lpwstr_v.vvector_data[i]
+ = variant_array[i]->vvalue.vt_compressed_lpwstr;
+ break;
+ default:
+ DBG_ERR("Can't convert array of %s to VECTOR\n",
+ get_vtype_name(vtype));
+ return false;
+ }
+ }
+ outval->vtype = vtype | VT_VECTOR;
+ outval->vvalue = vvalue;
+ return true;
+}
+
+/*
+ * get the addresses in rowbuf of variants to read from
+ * pvec_address will point to addresses,
+ * an array of n elements for a vector or array of 1 element
+ * if non-vector item.
+ *
+ * addresses stored in pvec_address
+ *
+ */
+static enum ndr_err_code extract_variant_addresses(TALLOC_CTX *ctx,
+ struct wsp_ctablevariant *tablevar,
+ bool is_64bit,
+ struct ndr_pull *ndr_pull,
+ ndr_flags_type flags,
+ uint64_t baseaddress,
+ DATA_BLOB *rows_buf,
+ uint64_t *pcount,
+ uint64_t **pvec_address)
+{
+ bool is_vector = tablevar->vtype & VT_VECTOR;
+ uint64_t count;
+ uint64_t addr;
+ uint64_t *vec_address = NULL;
+ enum ndr_err_code err;
+
+ /* read count (only if this is a vector) */
+ if (is_vector) {
+ if (is_64bit) {
+ err = ndr_pull_udlong(ndr_pull,
+ flags,
+ &count);
+ if (err) {
+ DBG_ERR("Failed to extract count\n");
+ goto out;
+ }
+ } else {
+ uint32_t count_32;
+ err = ndr_pull_uint32(ndr_pull,
+ flags,
+ &count_32);
+ if (err) {
+ DBG_ERR("Failed to extract count\n");
+ goto out;
+ }
+ count = (uint64_t)count_32;
+ }
+ } else {
+ count = 1;
+ }
+
+ /* ensure count is at least within buffer range */
+ if (count >= MAX_ROW_BUFF_SIZE || count >= rows_buf->length) {
+ DBG_ERR("count %"PRIu64" either exceeds max buffer size "
+ "or buffer size (%zu)",
+ count, rows_buf->length);
+ err = NDR_ERR_VALIDATE;
+ goto out;
+ }
+
+ /* read address */
+ if (is_64bit) {
+ err = ndr_pull_udlong(ndr_pull,
+ flags,
+ &addr);
+ if (err) {
+ DBG_ERR("Failed to extract address\n");
+ goto out;
+ }
+ } else {
+ uint32_t addr_32;
+ err = ndr_pull_uint32(ndr_pull, flags, &addr_32);
+ if (err) {
+ DBG_ERR("Failed to extract address\n");
+ goto out;
+ }
+ addr = addr_32;
+ }
+
+ if ((addr - baseaddress) >= rows_buf->length) {
+ DBG_ERR("offset %"PRIu64" outside buffer range "
+ "(buf len - %zu)\n",
+ addr - baseaddress,
+ rows_buf->length);
+ err = NDR_ERR_VALIDATE;
+ goto out;
+ }
+
+ vec_address = talloc_zero_array(ctx,
+ uint64_t, count);
+
+ if (vec_address == NULL) {
+ err = NDR_ERR_ALLOC;
+ goto out;
+ }
+
+ /*
+ * non vector case addr points to value
+ * otherwise addr points to list of addresses
+ * for the values in vector
+ */
+ if (is_vector == false) {
+ vec_address[0] = addr;
+ } else {
+ uint64_t array_offset = addr - baseaddress;
+ uint64_t i;
+ uint32_t intsize;
+
+ if (is_64bit) {
+ intsize = 8;
+ } else {
+ intsize = 4;
+ }
+
+ if (array_offset >= MAX_ROW_BUFF_SIZE
+ || array_offset >= rows_buf->length) {
+ DBG_ERR("offset %"PRIu64" either exceeds max buf size "
+ "or buffer size (%zu)",
+ array_offset, rows_buf->length);
+ err = NDR_ERR_VALIDATE;
+ goto out;
+ }
+
+ /* addr points to a list of int32 or int64 addresses */
+ for (i = 0; i < count; i++) {
+ /*
+ * read the addresses of the vector elements
+ * note: we can safely convert the uint64_t
+ * values here to uint32_t values as
+ * we are sure they are within range
+ * due to previous checks above.
+ */
+ if (smb_buffer_oob((uint32_t)rows_buf->length,
+ (uint32_t)array_offset,
+ intsize)) {
+ DBG_ERR("offset %"PRIu64" will be outside "
+ "buffer range (buf len - %zu) after "
+ "reading %s address\n",
+ array_offset,
+ rows_buf->length,
+ is_64bit ? "64 bit" : "32 bit");
+ err = NDR_ERR_VALIDATE;
+ goto out;
+ }
+ if (is_64bit) {
+ vec_address[i] =
+ PULL_LE_I64(rows_buf->data,
+ array_offset);
+ } else {
+ vec_address[i] =
+ (uint32_t)PULL_LE_I32(rows_buf->data,
+ array_offset);
+ }
+ array_offset += intsize;
+ }
+ }
+ err = NDR_ERR_SUCCESS;
+ *pcount = count;
+ *pvec_address = vec_address;
+out:
+ return err;
+}
+
+static enum ndr_err_code extract_crowvariant_variable(TALLOC_CTX *ctx,
+ struct wsp_ctablevariant *tablevar,
+ bool is_64bit,
+ struct ndr_pull *ndr_pull,
+ ndr_flags_type flags,
+ uint64_t baseaddress,
+ DATA_BLOB *rows_buf,
+ uint32_t len,
+ struct wsp_cbasestoragevariant *val)
+{
+ enum ndr_err_code err;
+ bool is_vector = tablevar->vtype & VT_VECTOR;
+ uint64_t count = 0;
+
+ uint64_t *vec_address = NULL;
+ struct wsp_cbasestoragevariant **variant_array = NULL;
+ int i;
+
+
+ err = extract_variant_addresses(ctx,
+ tablevar,
+ is_64bit,
+ ndr_pull,
+ flags,
+ baseaddress,
+ rows_buf,
+ &count,
+ &vec_address);
+
+ if (err) {
+ DBG_ERR("Failed to extract address and/or count\n");
+ goto out;
+ }
+
+ variant_array = talloc_zero_array(ctx,
+ struct wsp_cbasestoragevariant*,
+ count);
+
+ if (variant_array == NULL) {
+ err = NDR_ERR_ALLOC;
+ goto out;
+ }
+
+ if (is_vector == false) {
+ variant_array[0] = val;
+ } else {
+ for (i = 0; i < count; i++) {
+ variant_array[i] = talloc_zero(ctx,
+ struct wsp_cbasestoragevariant);
+ if (variant_array[i] == NULL) {
+ err = NDR_ERR_ALLOC;
+ goto out;
+ }
+ }
+ }
+
+ for (i = 0; i < count; i++) {
+ uint32_t tmplen = len;
+ uint64_t buf_offset;
+ buf_offset = vec_address[i] - baseaddress;
+ if (buf_offset >= rows_buf->length) {
+ DBG_ERR("offset %"PRIu64" outside buffer range "
+ "(buf len - %zu)\n",
+ buf_offset,
+ rows_buf->length);
+ err = NDR_ERR_VALIDATE;
+ goto out;
+ }
+ if (is_64bit
+ && (tablevar->vtype & ~(VT_VECTOR)) == VT_LPWSTR) {
+ /*
+ * we can't trust len if 64 bit mode
+ * (in 32 bit mode the length reported at len offset
+ * seem consistent and correct)
+ * So in this case instead of using the len
+ * at len offset we just use the full buffer
+ * from the point the value is stored at
+ * till the end of the buffer
+ */
+ tmplen = rows_buf->length - buf_offset;
+ }
+ if (!extract_rowbuf_variable_type(ctx,
+ tablevar->vtype & ~VT_VECTOR,
+ buf_offset,
+ rows_buf,
+ tmplen,
+ variant_array[i])) {
+ err = NDR_ERR_VALIDATE;
+ goto out;
+ }
+ }
+
+ if (is_vector) {
+ if (!convert_variant_array_to_vector(ctx,
+ count,
+ variant_array,
+ val)) {
+ err = NDR_ERR_VALIDATE;
+ goto out;
+ }
+ }
+ err = NDR_ERR_SUCCESS;
+out:
+ return err;
+}
+
+static enum ndr_err_code extract_crowvariant(TALLOC_CTX *ctx,
+ struct wsp_ctablevariant *tablevar,
+ bool is_64bit,
+ struct ndr_pull *ndr_pull,
+ ndr_flags_type flags,
+ uint64_t baseaddress,
+ DATA_BLOB *rows_buf, uint32_t len,
+ struct wsp_cbasestoragevariant *val)
+{
+ enum ndr_err_code err = NDR_ERR_SUCCESS;
+ bool is_vector = tablevar->vtype & VT_VECTOR;
+ bool is_array = tablevar->vtype & VT_ARRAY;
+
+ if (is_array) {
+ DBG_ERR("Not handling ARRAYs!!!\n");
+ err = NDR_ERR_VALIDATE;
+ goto out;
+ }
+
+ if (is_variable_size((tablevar->vtype & ~(VT_VECTOR)))) {
+ err = extract_crowvariant_variable(ctx,
+ tablevar,
+ is_64bit,
+ ndr_pull,
+ flags,
+ baseaddress,
+ rows_buf,
+ len,
+ val);
+
+ } else {
+ if (is_vector) {
+ DBG_ERR("Not handling VECTORs of fixed size values!!!\n");
+ err = NDR_ERR_VALIDATE;
+ goto out;
+ }
+ NDR_CHECK(ndr_pull_set_switch_value(ndr_pull,
+ &val->vvalue,
+ tablevar->vtype));
+ NDR_CHECK(ndr_pull_variant_types(ndr_pull, NDR_SCALARS, &val->vvalue));
+ val->vtype = tablevar->vtype;
+ }
+out:
+ return err;
+}
+
+static enum ndr_err_code process_columns(TALLOC_CTX *ctx,
+ bool is_64bit,
+ uint64_t baseaddress,
+ struct wsp_cpmsetbindingsin *bindingin,
+ DATA_BLOB *rows_buf,
+ uint32_t nrow,
+ struct wsp_cbasestoragevariant *cols)
+{
+ uint32_t i;
+ enum ndr_err_code err = NDR_ERR_SUCCESS;
+ struct ndr_pull *ndr_pull = NULL;
+ ndr_flags_type ndr_flags = NDR_SCALARS | NDR_BUFFERS;
+ uint64_t nrow_offset = (uint64_t)nrow * bindingin->brow;
+
+ if (nrow_offset >= rows_buf->length) {
+ DBG_ERR("offset %"PRIu64" outside buffer range (buf len - %zu)\n",
+ nrow_offset,
+ rows_buf->length);
+ err = NDR_ERR_ALLOC;
+ goto out;
+ }
+
+ /*
+ * process columns, column info is contained in cpmsetbindings
+ * for more information see 'Rows' description MS-WSP 2.2.4.1.2
+ * which describes how the server fills the buffer.
+ */
+ for (i = 0; i < bindingin->ccolumns; i++) {
+ struct wsp_ctablecolumn *tab_col = &bindingin->acolumns[i];
+ DATA_BLOB col_val_blob = data_blob_null;
+ uint64_t val_offset;
+ struct wsp_ctablevariant tablevariant = {0};
+ DBG_INFO("\nRow[%d]Col[%d] property %s type %s\n",nrow, i,
+ prop_from_fullprop(ctx, &tab_col->propspec),
+ get_vtype_name(tab_col->vtype));
+ if (tab_col->statusused) {
+ val_offset = nrow_offset + tab_col->statusoffset.value;
+ if (val_offset >= rows_buf->length) {
+ DBG_ERR("offset %"PRIu64" outside buffer range "
+ "(buf len - %zu)\n",
+ val_offset,
+ rows_buf->length);
+ err = NDR_ERR_ALLOC;
+ goto out;
+ }
+ DBG_INFO("\n\tstatusoffset 0x%x status is %s\n",
+ tab_col->statusoffset.value,
+ get_store_status(
+ (uint8_t)*(rows_buf->data
+ + val_offset)));
+ }
+ if (tab_col->lengthused) {
+ val_offset = nrow_offset + tab_col->lengthoffset.value;
+ if (val_offset >= rows_buf->length) {
+ DBG_ERR("offset %"PRIu64" outside buffer range "
+ "(buf len - %zu)\n",
+ val_offset,
+ rows_buf->length);
+ err = NDR_ERR_ALLOC;
+ goto out;
+ }
+ DBG_INFO("\n\tlen offset 0x%x value at length is 0x%x\n",
+ tab_col->lengthoffset.value,
+ PULL_LE_I32(rows_buf->data,
+ val_offset));
+ }
+ if (tab_col->valueused) {
+ uint32_t len = 0;
+ val_offset = nrow_offset + tab_col->valueoffset.value;
+ if (val_offset >= rows_buf->length) {
+ DBG_ERR("offset %"PRIu64" outside buffer range "
+ "(buf len - %zu)\n",
+ val_offset,
+ rows_buf->length);
+ err = NDR_ERR_ALLOC;
+ goto out;
+ }
+ DBG_INFO("\n\tvalueoffset:valuesize 0x%x:0x%x "
+ "crowvariant address = 0x%"PRIx64"\n",
+ tab_col->valueoffset.value,
+ tab_col->valuesize.value,
+ val_offset);
+
+ col_val_blob.data = rows_buf->data + val_offset;
+ col_val_blob.length = tab_col->valuesize.value;
+
+
+ if (tab_col->vtype != VT_VARIANT) {
+ DBG_ERR("Not handling non variant column "
+ "values\n");
+ err = NDR_ERR_VALIDATE;
+ goto out;
+ }
+ ndr_pull = ndr_pull_init_blob(&col_val_blob, ctx);
+ if (ndr_pull == NULL) {
+ err = NDR_ERR_ALLOC;
+ DBG_ERR("out of memory\n");
+ goto out;
+ }
+
+ err = ndr_pull_wsp_ctablevariant(ndr_pull,
+ ndr_flags,
+ &tablevariant);
+ if (err) {
+ DBG_ERR("!!! failed to pull fixed part of variant data for col data\n");
+ goto out;
+ }
+ DBG_INFO("\n");
+ DBG_INFO("\tcrowvariant contains %s \n",
+ get_vtype_name(tablevariant.vtype));
+
+ if (tab_col->lengthused) {
+ /*
+ * it seems the size is what's at
+ * lengthoffset - tab_col->valuesize.value
+ */
+ len = PULL_LE_I32(rows_buf->data,
+ nrow_offset
+ + tab_col->lengthoffset.value);
+ len = len - tab_col->valuesize.value;
+ }
+ err = extract_crowvariant(ctx,
+ &tablevariant,
+ is_64bit,
+ ndr_pull,
+ ndr_flags,
+ baseaddress,
+ rows_buf,
+ len,
+ &cols[i]);
+ }
+ }
+out:
+ return err;
+}
+
+/*
+ * extracts values from rows_buf into rowsarray
+ * based on the information in bindingsin
+ */
+enum ndr_err_code extract_rowsarray(
+ TALLOC_CTX * ctx,
+ DATA_BLOB *rows_buf,
+ bool is_64bit,
+ struct wsp_cpmsetbindingsin *bindingsin,
+ uint32_t cbreserved,
+ uint64_t baseaddress,
+ uint32_t rows,
+ struct wsp_cbasestoragevariant **rowsarray)
+{
+ uint32_t i;
+ enum ndr_err_code err = NDR_ERR_SUCCESS;
+ /*
+ * limit check the size of rows_buf
+ * see MS-WSP 2.2.3.11 which describes the size
+ * of the rows buffer MUST not exceed 0x0004000 bytes.
+ * This limit will ensure we can safely check
+ * limits based on uint32_t offsets
+ */
+
+ if (rows_buf->length > MAX_ROW_BUFF_SIZE) {
+ DBG_ERR("Buffer size 0x%zx exceeds 0x%x max buffer size\n",
+ rows_buf->length, MAX_ROW_BUFF_SIZE);
+ return NDR_ERR_BUFSIZE;
+ }
+
+ for (i = 0; i < rows; i++ ) {
+ struct wsp_cbasestoragevariant *cols =
+ talloc_zero_array(ctx,
+ struct wsp_cbasestoragevariant,
+ bindingsin->ccolumns);
+ uint64_t adjusted_address;
+ if (cols == NULL) {
+ return NDR_ERR_ALLOC;
+ }
+
+ /*
+ * cater for paddingrows (see MS-WSP 2.2.3.12)
+ * Rows buffer starts cbreserved bytes into messages
+ */
+ adjusted_address = baseaddress + cbreserved;
+
+ err = process_columns(ctx,
+ is_64bit,
+ adjusted_address,
+ bindingsin,
+ rows_buf,
+ i,
+ cols);
+ if (err) {
+ break;
+ }
+ rowsarray[i] = cols;
+ }
+ return err;
+}
+
+static bool process_query_node(TALLOC_CTX *ctx,
+ struct wsp_crestriction *crestriction,
+ t_query *node);
+
+static bool process_andornot_node(TALLOC_CTX *ctx,
+ struct wsp_crestriction *crestr,
+ t_query *node,
+ struct wsp_crestriction **left,
+ struct wsp_crestriction **right)
+{
+ struct wsp_cnoderestriction *restriction_node = NULL;
+
+ *left = NULL;
+ *right = NULL;
+
+ restriction_node =
+ &crestr->restriction.cnoderestriction;
+
+ crestr->weight = 1000;
+
+ if (node->type == eAND || node->type == eOR) {
+ if (node->type == eAND) {
+ crestr->ultype = RTAND;
+ } else {
+ crestr->ultype = RTOR;
+ }
+ if (!create_noderestriction(ctx, restriction_node, 2)) {
+ return false;
+ }
+ *left = &restriction_node->panode[0];
+ *right = &restriction_node->panode[1];
+ } else {
+ crestr->ultype = RTNOT;
+ crestr->restriction.restriction.restriction =
+ talloc_zero(ctx, struct wsp_crestriction);
+ if (crestr->restriction.restriction.restriction == NULL) {
+ DBG_ERR("out of memory\n");
+ return false;
+ }
+ crestr =
+ crestr->restriction.restriction.restriction;
+ }
+ if (*left == NULL) {
+ *left = crestr;
+ }
+ if (*right == NULL) {
+ *right = crestr;
+ }
+ return true;
+}
+
+static void process_value_node(TALLOC_CTX *ctx,
+ struct wsp_crestriction *crestriction,
+ t_query *node)
+{
+ *crestriction = *node->restriction;
+}
+
+static bool process_query_node(TALLOC_CTX *ctx,
+ struct wsp_crestriction *crestriction,
+ t_query *node)
+{
+ struct wsp_crestriction *left = NULL, *right = NULL;
+ if (node == NULL) {
+ return true;
+ }
+ switch (node->type) {
+ case eAND:
+ case eOR:
+ case eNOT:
+ if (!process_andornot_node(ctx, crestriction, node,
+ &left, &right)) {
+ return false;
+ }
+ break;
+ case eVALUE:
+ process_value_node(ctx, crestriction, node);
+ break;
+ default:
+ break;
+ }
+ if (!process_query_node(ctx, left, node->left)) {
+ return false;
+ }
+ if (!process_query_node(ctx, right, node->right)) {
+ return false;
+ }
+ return true;
+}
+
+bool create_querysearch_request(TALLOC_CTX * ctx,
+ struct wsp_request* request,
+ t_select_stmt *sql)
+{
+ uint32_t indices[sql->cols->num_cols];
+ uint32_t i;
+ uint32_t j;
+ struct wsp_cpmcreatequeryin *createquery =
+ &request->message.cpmcreatequery;
+
+ for (i = 0; i < sql->cols->num_cols; i++) {
+ indices[i] = i;
+ }
+
+ request->header.msg = CPMCREATEQUERY;
+ createquery->ccolumnsetpresent = 1;
+ createquery->columnset.columnset.count = sql->cols->num_cols;
+ if (!fill_uint32_vec(ctx, &createquery->columnset.columnset.indexes,
+ indices,
+ sql->cols->num_cols)) {
+ return false;
+ }
+
+ /* handle restrictions */
+ createquery->crestrictionpresent = 1;
+ createquery->restrictionarray.restrictionarray.count = 1;
+ createquery->restrictionarray.restrictionarray.ispresent = 1;
+
+ if (!create_restriction_array(ctx,
+ &createquery->restrictionarray.restrictionarray.restrictions,
+ createquery->restrictionarray.restrictionarray.count)) {
+ return false;
+ }
+
+
+ if (!process_query_node(ctx,
+ &createquery->restrictionarray.restrictionarray.restrictions[0],
+ sql->where)) {
+ return false;
+ }
+
+
+ /* handle rest */
+ createquery->csortsetpresent = 1;
+ if (createquery->csortsetpresent) {
+ /* sort on first column */
+ struct wsp_csort data[] = {
+ {0x00000000, 0x00000000, 0x00000000, WSP_DEFAULT_LCID},
+ };
+ struct wsp_csortset *sortset = NULL;
+ struct wsp_cingroupsortaggregsets *aggregsets = NULL;
+
+ aggregsets = &createquery->sortset.groupsortaggregsets;
+ aggregsets->ccount = 1;
+ aggregsets->sortsets =
+ talloc_zero_array(ctx,
+ struct wsp_cingroupsortaggregset,
+ aggregsets->ccount);
+ sortset = &aggregsets->sortsets[0].sortaggregset;
+ sortset->count = ARRAY_SIZE(data);
+ if (!fill_sortarray(ctx,
+ &sortset->sortarray,
+ data,sortset->count)) {
+ return false;
+ }
+ }
+
+ createquery->ccategorizationsetpresent = 0;
+
+ createquery->rowsetproperties.ubooleanoptions = 0x00000203;
+ createquery->rowsetproperties.ulmaxopenrows = 0x00000000;
+ createquery->rowsetproperties.ulmemoryusage = 0x00000000;
+ createquery->rowsetproperties.cmaxresults = 0x00000000;
+ createquery->rowsetproperties.ccmdtimeout = 0x00000005;
+
+ createquery->pidmapper.count = sql->cols->num_cols;
+ createquery->pidmapper.apropspec = talloc_zero_array(ctx,
+ struct wsp_cfullpropspec,
+ createquery->pidmapper.count);
+
+ if (createquery->pidmapper.apropspec == NULL) {
+ DBG_ERR("out of memory\n");
+ return false;
+ }
+
+ for(i = 0, j = 0; i < sql->cols->num_cols; i++) {
+ struct wsp_cfullpropspec *prop =
+ &createquery->pidmapper.apropspec[j];
+ char *propname = sql->cols->cols[i];
+ /*
+ * don't put RowID in pidmapper or windows will reject
+ * the query.
+ */
+ if (strequal(propname, "System.Search.RowID")) {
+ continue;
+ }
+ if (!set_fullpropspec(ctx,
+ prop, sql->cols->cols[i],
+ PRSPEC_PROPID)) {
+ DBG_ERR("Failed to handle property named %s\n",
+ sql->cols->cols[i]);
+ continue;
+ }
+ j++;
+ }
+ createquery->columnset.columnset.count = j;
+ createquery->pidmapper.count = j;
+ createquery->lcid = WSP_DEFAULT_LCID;
+ return true;
+}
+
+static int32_t getNextAddress(int32_t value_off,
+ int32_t status_off,
+ int32_t len_off,
+ int32_t max_value_size)
+{
+ return MAX(MAX(value_off + max_value_size, status_off + 1), len_off + 2);
+}
+
+static void create_binding_offsets(struct binding *binding, int no_cols,
+ int max_value_size)
+{
+ uint32_t buf_addr = 0x0;
+ uint32_t i;
+
+ uint32_t value_off = 0;
+ uint32_t len_off = 0;
+
+ /* initial state this will get incremented to the desired 0x2 */
+ uint32_t status_off = 0x1;
+ uint32_t avail = 0x4;
+ int status_remain = 0x2;
+ int len_remain = -1;
+
+ const static uint32_t WINDOW = 0x8;
+ const static uint32_t LEN_STAT_SIZE = 0x4;
+ for (i = 0; i < no_cols; i++) {
+ buf_addr = buf_addr + WINDOW;
+ value_off = buf_addr;
+
+ if (status_remain <= 0) {
+ if (avail) {
+ status_off = avail;
+ status_remain = LEN_STAT_SIZE;
+ avail = 0;
+ } else {
+ /*
+ * we prepare the address to allocate
+ * another block from here. It will
+ * be allocated automatically when we
+ * re-enter the loop
+ */
+ status_off = getNextAddress(value_off,
+ status_off,
+ len_off,
+ max_value_size) + WINDOW;
+ status_remain = LEN_STAT_SIZE;
+ buf_addr = status_off;
+ avail = buf_addr + LEN_STAT_SIZE;
+ }
+ } else {
+ status_off++;
+ buf_addr = getNextAddress(value_off,
+ status_off,
+ len_off,
+ max_value_size);
+ }
+
+ if (len_remain <= 0) {
+ if (avail) {
+ len_off = avail;
+ len_remain = LEN_STAT_SIZE;
+ avail = 0;
+ } else {
+ /*
+ * we prepare the address to allocate
+ * another block from here. It will
+ * be allocated automatically when we
+ * re-enter the loop
+ */
+ len_off = getNextAddress(value_off,
+ status_off,
+ len_off,
+ max_value_size) + WINDOW;
+ len_remain = LEN_STAT_SIZE;
+ buf_addr = len_off;
+ avail = buf_addr + LEN_STAT_SIZE;
+ }
+ } else {
+ len_off += 0x4;
+ buf_addr = getNextAddress(value_off,
+ status_off,
+ len_off,
+ max_value_size);
+ }
+ status_remain--;
+ len_remain -= LEN_STAT_SIZE;
+ binding[i].value_off = value_off;
+ binding[i].status_off = status_off;
+ binding[i].len_off = len_off;
+ }
+}
+
+static bool fill_bindings(TALLOC_CTX *ctx,
+ struct wsp_cpmsetbindingsin *bindingsin,
+ char **col_names,
+ bool is_64bit)
+{
+ uint32_t i;
+ struct binding *offsets = NULL;
+ uint32_t num_cols;
+ int maxvalue = is_64bit ? 0x18 : 0x10;
+
+ struct wsp_ctablecolumn *tablecols = bindingsin->acolumns;
+ bindingsin->brow = 0x0;
+ num_cols = bindingsin->ccolumns;
+
+ offsets = talloc_zero_array(ctx, struct binding, num_cols);
+
+ if (offsets == NULL) {
+ DBG_ERR("out of memory\n");
+ return false;
+ }
+
+ create_binding_offsets(offsets,
+ num_cols,
+ maxvalue);
+
+ for (i = 0; i < num_cols; i++) {
+ uint32_t max_off;
+ if (!set_ctablecolumn(ctx, &tablecols[i], col_names[i],
+ &offsets[i], maxvalue)) {
+ DBG_ERR("Failed to handle property named %s\n",
+ col_names[i]);
+ continue;
+ }
+ max_off = MAX(offsets[i].value_off + maxvalue,
+ offsets[i].status_off + 1);
+ max_off = MAX(max_off, offsets[i].len_off + 2);
+ if (max_off > bindingsin->brow) {
+ bindingsin->brow = max_off;
+ }
+ }
+ /* important */
+ bindingsin->brow += ndr_align_size(bindingsin->brow,4);
+ return true;
+}
+
+bool create_setbindings_request(TALLOC_CTX * ctx,
+ struct wsp_request* request,
+ t_select_stmt *sql,
+ uint32_t cursor,
+ bool is_64bit)
+{
+ struct wsp_cpmsetbindingsin *bindingsin =
+ &request->message.cpmsetbindings;
+
+ request->header.msg = CPMSETBINDINGSIN;
+ bindingsin->hcursor = cursor;
+ bindingsin->ccolumns = sql->cols->num_cols;
+
+ bindingsin->acolumns = talloc_zero_array(ctx,
+ struct wsp_ctablecolumn,
+ bindingsin->ccolumns);
+
+ if (bindingsin->acolumns == NULL) {
+ DBG_ERR("out of memory\n");
+ return false;
+ }
+
+ if (!fill_bindings(ctx, bindingsin, sql->cols->cols, is_64bit)) {
+ return false;
+ }
+
+ return true;
+}
+
+enum search_kind get_kind(const char* kind_str)
+{
+ enum search_kind result = UNKNOWN;
+ int i;
+ const static struct {
+ const char* str;
+ enum search_kind search_kind;
+ } kind_map[] = {
+ {"Calendar", CALENDAR},
+ {"Communication", COMMUNICATION},
+ {"Contact", CONTACT},
+ {"Document", DOCUMENT},
+ {"Email", EMAIL},
+ {"Feed", FEED},
+ {"Folder", FOLDER},
+ {"Game", GAME},
+ {"InstantMessage", INSTANTMESSAGE},
+ {"Journal", JOURNAL},
+ {"Link", LINK},
+ {"Movie", MOVIE},
+ {"Music", MUSIC},
+ {"Note", NOTE},
+ {"Picture", PICTURE},
+ {"Program", PROGRAM},
+ {"RecordedTV", RECORDEDTV},
+ {"SearchFolder", SEARCHFOLDER},
+ {"Task", TASK},
+ {"Video", VIDEO},
+ {"WebHistory", WEBHISTORY},
+ };
+ for (i = 0; i < ARRAY_SIZE(kind_map); i++) {
+ if (strequal(kind_str, kind_map[i].str)) {
+ result = kind_map[i].search_kind;
+ break;
+ }
+ }
+ return result;
+}
+
+struct wsp_client_ctx
+{
+ struct rpc_pipe_client *rpccli;
+ struct cli_state *cli_state;
+ struct dcerpc_binding_handle *h;
+};
+
+static NTSTATUS wsp_resp_pdu_complete(struct tstream_context *stream,
+ void *private_data,
+ DATA_BLOB blob,
+ size_t *packet_size)
+{
+ ssize_t to_read;
+
+ to_read = tstream_pending_bytes(stream);
+ if (to_read == -1) {
+ return NT_STATUS_IO_DEVICE_ERROR;
+ }
+
+ if (to_read > 0) {
+ *packet_size = blob.length + to_read;
+ return STATUS_MORE_ENTRIES;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS wsp_server_connect(TALLOC_CTX *mem_ctx,
+ const char *servername,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct cli_credentials *credentials,
+ struct cli_state *cli,
+ struct wsp_client_ctx **wsp_ctx)
+{
+ struct wsp_client_ctx *ctx = NULL;
+ struct dcerpc_binding_handle *h = NULL;
+ struct tstream_context *stream = NULL;
+ NTSTATUS status;
+
+ bool smb2_or_greater =
+ (lpcfg_client_max_protocol(lp_ctx) >= PROTOCOL_SMB2_02);
+
+ if (!smb2_or_greater) {
+ return NT_STATUS_PROTOCOL_NOT_SUPPORTED;
+ }
+
+ ctx = talloc_zero(mem_ctx, struct wsp_client_ctx);
+ if (ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ctx->cli_state = cli;
+
+
+ status = smb2cli_ioctl_pipe_wait(
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ "MsFteWds",
+ 1);
+
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("wait for pipe failed: %s)\n",
+ nt_errstr(status));
+ return status;
+ }
+
+ status = rpc_pipe_open_np(cli,
+ &ndr_table_msftewds,
+ &ctx->rpccli);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("failed to int the pipe)\n");
+ return status;
+ }
+
+ stream = rpc_transport_get_tstream(ctx->rpccli->transport);
+ h = tstream_binding_handle_create(ctx->rpccli,
+ NULL,
+ &stream,
+ MSG_HDR_SIZE,
+ wsp_resp_pdu_complete,
+ ctx, 42280);
+
+ if (!h) {
+ DBG_ERR("failed to create the pipe handle)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ctx->rpccli->binding_handle = h;
+ *wsp_ctx = ctx;
+
+ return status;
+}
+
+static NTSTATUS write_something(TALLOC_CTX* ctx,
+ struct rpc_pipe_client *p,
+ DATA_BLOB *blob_in,
+ DATA_BLOB *blob_out)
+{
+ uint32_t outflags;
+ struct dcerpc_binding_handle *handle = p->binding_handle;
+ NTSTATUS status;
+
+ status = dcerpc_binding_handle_raw_call(handle,
+ NULL,
+ 0,
+ 0,
+ blob_in->data,
+ blob_in->length,
+ ctx,
+ &blob_out->data,
+ &blob_out->length,
+ &outflags);
+ return status;
+}
+
+/* msg is expected to be created on the heap with talloc */
+static enum ndr_err_code parse_blob(TALLOC_CTX *ctx, DATA_BLOB *blob,
+ struct wsp_request *request,
+ struct wsp_response *response,
+ DATA_BLOB *unread)
+{
+ struct ndr_pull *ndr = NULL;
+ enum ndr_err_code err;
+ ndr_flags_type ndr_flags = NDR_SCALARS | NDR_BUFFERS;
+ uint32_t status = 0;
+
+ ndr = ndr_pull_init_blob(blob, ctx);
+
+ if (ndr == NULL) {
+ return NDR_ERR_ALLOC;
+ }
+
+ /* peek at the status */
+ status = PULL_LE_I32(blob->data, 4);
+
+ /* is hard error ?*/
+ if (status & 0x80000000 && blob->length == MSG_HDR_SIZE) {
+ /* just pull the header */
+ err = ndr_pull_wsp_header(ndr, ndr_flags, &response->header);
+ DBG_ERR("error: %s\n", nt_errstr(NT_STATUS(status)));
+ goto out;
+ }
+ err = ndr_pull_wsp_response(ndr, ndr_flags, response);
+ if (err) {
+ DBG_ERR("Failed to pull header from response blob error %d\n", err);
+ goto out;
+ }
+ if (DEBUGLEVEL >=6) {
+ NDR_PRINT_DEBUG(wsp_response, response);
+ }
+ if (response->header.msg == CPMGETROWS) {
+ if (request) {
+ /* point to rows buffer */
+ struct wsp_cpmgetrowsin *getrows =
+ &request->message.cpmgetrows;
+ ndr->offset = getrows->cbreserved;
+ }
+ }
+
+ if (ndr->offset < blob->length) {
+ int bytes = blob->length - ndr->offset;
+ *unread = data_blob_named(blob->data + ndr->offset,
+ bytes, "UNREAD");
+ DBG_WARNING("\nThere are unprocessed bytes (len 0x%x) "
+ "at end of message\n", bytes);
+ }
+
+out:
+ return err;
+}
+
+static void set_msg_checksum(DATA_BLOB *blob, struct wsp_header *hdr)
+{
+ /* point at payload */
+ uint32_t i;
+ uint8_t *buffer = blob->data + MSG_HDR_SIZE;
+ uint32_t buf_size = blob->length - MSG_HDR_SIZE;
+ uint32_t nwords = buf_size/4;
+ uint32_t offset = 0;
+ uint32_t checksum = 0;
+
+ static const uint32_t xor_const = 0x59533959;
+ for(i = 0; i < nwords; i++) {
+ checksum += PULL_LE_I32(buffer, offset);
+ offset += 4;
+ }
+
+ checksum ^= xor_const;
+ checksum -= hdr->msg;
+ hdr->checksum = checksum;
+}
+
+static enum ndr_err_code insert_header_and_checksum(TALLOC_CTX *ctx,
+ DATA_BLOB* blob,
+ struct wsp_header *header)
+{
+ enum ndr_err_code err;
+ ndr_flags_type ndr_flags = NDR_SCALARS | NDR_BUFFERS;
+ struct ndr_push *header_ndr = ndr_push_init_ctx(ctx);
+
+ if (header_ndr == NULL) {
+ return NDR_ERR_ALLOC;
+ }
+
+ if (header->msg == CPMCONNECT
+ || header->msg == CPMCREATEQUERY
+ || header->msg == CPMSETBINDINGSIN
+ || header->msg == CPMGETROWS
+ || header->msg == CPMFETCHVALUE) {
+
+ set_msg_checksum(blob, header);
+ }
+ err = ndr_push_wsp_header(header_ndr, ndr_flags, header);
+ if (err) {
+ DBG_ERR("Failed to push header, error %d\n", err);
+ return err;
+ }
+ memcpy(blob->data, header_ndr->data, MSG_HDR_SIZE);
+ return err;
+}
+
+NTSTATUS wsp_request_response(TALLOC_CTX* ctx,
+ struct wsp_client_ctx *wsp_ctx,
+ struct wsp_request* request,
+ struct wsp_response *response,
+ DATA_BLOB *unread)
+{
+ struct rpc_pipe_client *p = wsp_ctx->rpccli;
+ NTSTATUS status = NT_STATUS_OK;
+
+ ndr_flags_type ndr_flags = NDR_SCALARS | NDR_BUFFERS;
+ struct ndr_push* push_ndr = NULL;
+
+ enum ndr_err_code err;
+
+ DATA_BLOB req_blob;
+ DATA_BLOB resp_blob;
+
+ ZERO_STRUCT(req_blob);
+ ZERO_STRUCT(resp_blob);
+
+ push_ndr = ndr_push_init_ctx(ctx);
+ if (push_ndr == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* write message payload first */
+ push_ndr->offset = MSG_HDR_SIZE;
+ DBG_INFO("\n");
+
+ switch(request->header.msg) {
+ case CPMCONNECT:
+ {
+ struct wsp_cpmconnectin *connectin =
+ &request->message.cpmconnect;
+ err = ndr_push_wsp_cpmconnectin(push_ndr, ndr_flags,
+ connectin);
+ break;
+ }
+ case CPMCREATEQUERY:
+ {
+ struct wsp_cpmcreatequeryin* createquery =
+ &request->message.cpmcreatequery;
+ err = ndr_push_wsp_cpmcreatequeryin(push_ndr,
+ ndr_flags,
+ createquery);
+ req_blob = ndr_push_blob(push_ndr);
+ /* we need to set cpmcreatequery.size */
+ createquery->size =
+ req_blob.length - MSG_HDR_SIZE;
+ PUSH_LE_U32(req_blob.data, MSG_HDR_SIZE,
+ createquery->size);
+
+ break;
+ }
+ case CPMSETBINDINGSIN:
+ {
+ struct wsp_cpmsetbindingsin *bindingsin =
+ &request->message.cpmsetbindings;
+ err = ndr_push_wsp_cpmsetbindingsin(push_ndr, ndr_flags,
+ bindingsin);
+ req_blob = ndr_push_blob(push_ndr);
+ /* we need to set cpmsetbindings.bbindingdesc (size) */
+ bindingsin->bbindingdesc =
+ req_blob.length - MSG_HDR_SIZE - 16;
+ PUSH_LE_U32(req_blob.data, MSG_HDR_SIZE + 8,
+ bindingsin->bbindingdesc);
+ break;
+ }
+ case CPMGETROWS:
+ {
+ struct wsp_cpmgetrowsin *getrows =
+ &request->message.cpmgetrows;
+ err = ndr_push_wsp_cpmgetrowsin(push_ndr, ndr_flags,
+ getrows);
+ req_blob = ndr_push_blob(push_ndr);
+ getrows->cbseek = req_blob.length - MSG_HDR_SIZE - 32;
+ /* we need to set cpmgetrowsin.cbseek (size) */
+ PUSH_LE_U32(req_blob.data, MSG_HDR_SIZE + 12,
+ getrows->cbseek);
+ PUSH_LE_U32(req_blob.data, MSG_HDR_SIZE + 16,
+ getrows->cbreserved);
+ break;
+ }
+ case CPMGETQUERYSTATUS:
+ {
+ struct wsp_cpmgetquerystatusin *querystatus =
+ &request->message.cpmgetquerystatus;
+ err = ndr_push_wsp_cpmgetquerystatusin(
+ push_ndr,
+ ndr_flags,
+ querystatus);
+ break;
+ }
+ case CPMGETQUERYSTATUSEX:
+ {
+ struct wsp_cpmgetquerystatusexin *statusexin =
+ &request->message.cpmgetquerystatusex;
+ err = ndr_push_wsp_cpmgetquerystatusexin(
+ push_ndr,
+ ndr_flags,
+ statusexin);
+ break;
+ }
+ case CPMFREECURSOR:
+ {
+ struct wsp_cpmfreecursorin *freecursor =
+ &request->message.cpmfreecursor;
+ err = ndr_push_wsp_cpmfreecursorin(
+ push_ndr,
+ ndr_flags,
+ freecursor);
+ break;
+ }
+ case CPMFETCHVALUE:
+ {
+ struct wsp_cpmfetchvaluein *fetchvalue =
+ &request->message.cpmfetchvalue;
+ err = ndr_push_wsp_cpmfetchvaluein(
+ push_ndr,
+ ndr_flags,
+ fetchvalue);
+ break;
+ }
+
+ case CPMGETAPPROXIMATEPOSITION:
+ {
+ struct wsp_cpmgetapproximatepositionin *position =
+ &request->message.getapproximateposition;
+ err = ndr_push_wsp_cpmgetapproximatepositionin(
+ push_ndr,
+ ndr_flags,
+ position);
+ break;
+ }
+ default:
+ status = NT_STATUS_MESSAGE_NOT_FOUND;
+ goto out;
+ break;
+ }
+ if (err) {
+ DBG_ERR("failed to serialise message! (%d)\n", err);
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto out;
+ }
+ if (!req_blob.data) {
+ req_blob = ndr_push_blob(push_ndr);
+ }
+ err = insert_header_and_checksum(ctx, &req_blob, &request->header);
+
+ DBG_NOTICE("\nsending raw message from client len %d\n", (int)req_blob.length);
+ DBG_NOTICE("\nsending raw message from client\n");
+ DBG_NOTICE( "===============================\n");
+
+ dump_data(5, req_blob.data, req_blob.length);
+
+ status = write_something(ctx, p, &req_blob, &resp_blob);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Failed to write message\n");
+ goto out;
+ }
+ DBG_NOTICE("\nraw response from server\n");
+ DBG_NOTICE( "========================\n");
+ dump_data(5, resp_blob.data, resp_blob.length);
+
+ err = parse_blob(ctx,
+ &resp_blob,
+ request,
+ response,
+ unread);
+ if (err) {
+ DBG_ERR("Failed to parse response error %d\n", err);
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto out;
+ }
+ DBG_NOTICE("response status is 0x%x\n", response->header.status);
+ /* propagate error status to return status */
+ if (response->header.status & 0x80000000) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ }
+out:
+ return status;
+}
+
+struct dcerpc_binding_handle* get_wsp_pipe(struct wsp_client_ctx *ctx)
+{
+ return ctx->rpccli->binding_handle;
+}
diff --git a/source3/rpc_client/wsp_cli.h b/source3/rpc_client/wsp_cli.h
new file mode 100644
index 0000000..3159bd2
--- /dev/null
+++ b/source3/rpc_client/wsp_cli.h
@@ -0,0 +1,114 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Window Search Service
+ *
+ * Copyright (c) Noel Power
+ *
+ * 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 __LIBCLI_WSP_WSP_CLI
+#define __LIBCLI_WSP_WSP_CLI
+
+#include "libcli/wsp/wsp_aqs.h"
+
+enum search_kind {
+ CALENDAR,
+ COMMUNICATION,
+ CONTACT,
+ DOCUMENT,
+ EMAIL,
+ FEED,
+ FOLDER,
+ GAME,
+ INSTANTMESSAGE,
+ JOURNAL,
+ LINK,
+ MOVIE,
+ MUSIC,
+ NOTE,
+ PICTURE,
+ PROGRAM,
+ RECORDEDTV,
+ SEARCHFOLDER,
+ TASK,
+ VIDEO,
+ WEBHISTORY,
+ NONE,
+ UNKNOWN,
+};
+
+enum search_kind get_kind(const char* kind_str);
+
+struct wsp_cpmcreatequeryin;
+struct wsp_cpmsetbindingsin;
+struct wsp_cpmgetrowsin;
+struct dcerpc_binding_handle;
+
+bool init_connectin_request(TALLOC_CTX *ctx,
+ struct wsp_request* request,
+ const char* clientmachine,
+ const char* clientuser,
+ const char* server);
+
+bool create_querysearch_request(TALLOC_CTX * ctx,
+ struct wsp_request* request,
+ t_select_stmt *sql);
+
+bool create_setbindings_request(TALLOC_CTX * ctx,
+ struct wsp_request* request,
+ t_select_stmt *sql,
+ uint32_t cursor,
+ bool is_64bit);
+
+void create_seekat_getrows_request(TALLOC_CTX * ctx,
+ struct wsp_request* request,
+ uint32_t cursor,
+ uint32_t bookmark,
+ uint32_t skip,
+ uint32_t rows,
+ uint32_t cbreserved,
+ uint32_t ulclientbase,
+ uint32_t cbrowwidth,
+ uint32_t fbwdfetch);
+
+enum ndr_err_code extract_rowsarray(TALLOC_CTX * ctx,
+ DATA_BLOB *rows_buf,
+ bool is_64bit,
+ struct wsp_cpmsetbindingsin *bindingsin,
+ uint32_t cbreserved,
+ uint64_t baseaddress,
+ uint32_t rows,
+ struct wsp_cbasestoragevariant **rowsarray);
+
+struct wsp_client_ctx;
+struct cli_credentials;
+
+NTSTATUS wsp_server_connect(TALLOC_CTX *mem_ctx,
+ const char *servername,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct cli_credentials *credential,
+ struct cli_state *cli,
+ struct wsp_client_ctx **ctx);
+
+/* simple sync api */
+NTSTATUS wsp_request_response(TALLOC_CTX* ctx,
+ struct wsp_client_ctx *wsp_ctx,
+ struct wsp_request* request,
+ struct wsp_response *response,
+ DATA_BLOB *unread);
+
+struct dcerpc_binding_handle* get_wsp_pipe(struct wsp_client_ctx *ctx);
+#endif