From 8daa83a594a2e98f39d764422bfbdbc62c9efd44 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 19:20:00 +0200 Subject: Adding upstream version 2:4.20.0+dfsg. Signed-off-by: Daniel Baumann --- source3/rpc_client/cli_lsarpc.c | 850 +++++ source3/rpc_client/cli_lsarpc.h | 278 ++ source3/rpc_client/cli_mdssvc.c | 1214 +++++++ source3/rpc_client/cli_mdssvc.h | 97 + source3/rpc_client/cli_mdssvc_private.h | 74 + source3/rpc_client/cli_mdssvc_util.c | 551 ++++ source3/rpc_client/cli_mdssvc_util.h | 45 + source3/rpc_client/cli_netlogon.c | 860 +++++ source3/rpc_client/cli_netlogon.h | 111 + source3/rpc_client/cli_pipe.c | 3677 +++++++++++++++++++++ source3/rpc_client/cli_pipe.h | 143 + source3/rpc_client/cli_pipe_schannel.c | 120 + source3/rpc_client/cli_samr.c | 654 ++++ source3/rpc_client/cli_samr.h | 229 ++ source3/rpc_client/cli_spoolss.c | 1006 ++++++ source3/rpc_client/cli_spoolss.h | 153 + source3/rpc_client/cli_winreg.c | 975 ++++++ source3/rpc_client/cli_winreg.h | 449 +++ source3/rpc_client/cli_winreg_int.c | 323 ++ source3/rpc_client/cli_winreg_int.h | 103 + source3/rpc_client/cli_winreg_spoolss.c | 4729 ++++++++++++++++++++++++++++ source3/rpc_client/cli_winreg_spoolss.h | 720 +++++ source3/rpc_client/init_lsa.c | 60 + source3/rpc_client/init_lsa.h | 35 + source3/rpc_client/init_samr.c | 126 + source3/rpc_client/init_samr.h | 54 + source3/rpc_client/init_spoolss.c | 479 +++ source3/rpc_client/init_spoolss.h | 55 + source3/rpc_client/local_np.c | 852 +++++ source3/rpc_client/local_np.h | 56 + source3/rpc_client/py_mdscli.c | 572 ++++ source3/rpc_client/rpc_client.h | 52 + source3/rpc_client/rpc_transport.h | 104 + source3/rpc_client/rpc_transport_np.c | 179 ++ source3/rpc_client/rpc_transport_sock.c | 53 + source3/rpc_client/rpc_transport_tstream.c | 564 ++++ source3/rpc_client/util_netlogon.c | 449 +++ source3/rpc_client/util_netlogon.h | 54 + source3/rpc_client/wsp_cli.c | 2221 +++++++++++++ source3/rpc_client/wsp_cli.h | 114 + 40 files changed, 23440 insertions(+) create mode 100644 source3/rpc_client/cli_lsarpc.c create mode 100644 source3/rpc_client/cli_lsarpc.h create mode 100644 source3/rpc_client/cli_mdssvc.c create mode 100644 source3/rpc_client/cli_mdssvc.h create mode 100644 source3/rpc_client/cli_mdssvc_private.h create mode 100644 source3/rpc_client/cli_mdssvc_util.c create mode 100644 source3/rpc_client/cli_mdssvc_util.h create mode 100644 source3/rpc_client/cli_netlogon.c create mode 100644 source3/rpc_client/cli_netlogon.h create mode 100644 source3/rpc_client/cli_pipe.c create mode 100644 source3/rpc_client/cli_pipe.h create mode 100644 source3/rpc_client/cli_pipe_schannel.c create mode 100644 source3/rpc_client/cli_samr.c create mode 100644 source3/rpc_client/cli_samr.h create mode 100644 source3/rpc_client/cli_spoolss.c create mode 100644 source3/rpc_client/cli_spoolss.h create mode 100644 source3/rpc_client/cli_winreg.c create mode 100644 source3/rpc_client/cli_winreg.h create mode 100644 source3/rpc_client/cli_winreg_int.c create mode 100644 source3/rpc_client/cli_winreg_int.h create mode 100644 source3/rpc_client/cli_winreg_spoolss.c create mode 100644 source3/rpc_client/cli_winreg_spoolss.h create mode 100644 source3/rpc_client/init_lsa.c create mode 100644 source3/rpc_client/init_lsa.h create mode 100644 source3/rpc_client/init_samr.c create mode 100644 source3/rpc_client/init_samr.h create mode 100644 source3/rpc_client/init_spoolss.c create mode 100644 source3/rpc_client/init_spoolss.h create mode 100644 source3/rpc_client/local_np.c create mode 100644 source3/rpc_client/local_np.h create mode 100644 source3/rpc_client/py_mdscli.c create mode 100644 source3/rpc_client/rpc_client.h create mode 100644 source3/rpc_client/rpc_transport.h create mode 100644 source3/rpc_client/rpc_transport_np.c create mode 100644 source3/rpc_client/rpc_transport_sock.c create mode 100644 source3/rpc_client/rpc_transport_tstream.c create mode 100644 source3/rpc_client/util_netlogon.c create mode 100644 source3/rpc_client/util_netlogon.h create mode 100644 source3/rpc_client/wsp_cli.c create mode 100644 source3/rpc_client/wsp_cli.h (limited to 'source3/rpc_client') 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 . +*/ + +#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= 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; ibinding_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 . + */ + +#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 . +*/ + +#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 . +*/ + +#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 . +*/ + +#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 . +*/ + +#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 . +*/ + +#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 . +*/ + +#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 = ""; + } + + 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 . +*/ + +#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 . + */ + +#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 . + */ + +#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 . + */ + +#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 . +*/ + +#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 +#include + +/* 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 . + */ + +#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 . +*/ + +#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 . +*/ + +#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 + * + * 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 . + */ + +#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 + * + * 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 . + */ + +#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 + * + * 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 . + */ + +#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 + * + * 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 . + */ + +#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 + * + * 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 . + */ + +#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 + * + * 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 . + */ + +#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 . + */ + +#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 . + */ + +#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 . + */ + +#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 +#include + +/************************************************************************* + 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 . + */ + +#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 . + */ + +#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 . + */ + +#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 . + */ + +#include "source3/include/includes.h" +#include +#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 . + */ + +#ifndef _LOCAL_NP_H_ +#define _LOCAL_NP_H_ + +#include +#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 . +*/ + +#include "lib/replace/system/python.h" +#include +#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 . + */ + + +#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 . + */ + +#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 . + */ + +#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 . + */ + +#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 . + */ + +#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 . +*/ + +#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 . +*/ + +#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 . + */ + +#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 +#include +#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 . + */ +#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 -- cgit v1.2.3